commit 40504ab11fcaaf050f0a53e7826a146593488bc3 Author: NH Date: Thu Apr 17 20:38:35 2025 +0800 BG diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f10f1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +################################################################################ +# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。 +################################################################################ + +/.vs +/out/build/x64-Debug diff --git a/ArqHelper.cpp b/ArqHelper.cpp new file mode 100644 index 0000000..29cc297 --- /dev/null +++ b/ArqHelper.cpp @@ -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 diff --git a/ArqHelper.h b/ArqHelper.h new file mode 100644 index 0000000..ec8ad26 --- /dev/null +++ b/ArqHelper.h @@ -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 + +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 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 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 CArqSessionPoolT; + +template class CArqSessionExT : public CArqSessionT, public CSafeCounter +{ + using __super = CArqSessionT; + + using __super::Reset; + + friend class CArqSessionPoolT; + +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 CArqSessionPoolT : private CIOHandler +{ + using CArqSessionEx = CArqSessionExT; + using TArqSessionList = CRingPool; + using TArqSessionQueue = CCASQueue; + +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 const DWORD CArqSessionPoolT::DEFAULT_SESSION_LOCK_TIME = DEFAULT_OBJECT_CACHE_LOCK_TIME; +template const DWORD CArqSessionPoolT::DEFAULT_SESSION_POOL_SIZE = DEFAULT_OBJECT_CACHE_POOL_SIZE; +template const DWORD CArqSessionPoolT::DEFAULT_SESSION_POOL_HOLD = DEFAULT_OBJECT_CACHE_POOL_HOLD; + +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5dc4bf0 --- /dev/null +++ b/CMakeLists.txt @@ -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}) diff --git a/HPSocket-SSL.cpp b/HPSocket-SSL.cpp new file mode 100644 index 0000000..e4b0001 --- /dev/null +++ b/HPSocket-SSL.cpp @@ -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 \ No newline at end of file diff --git a/HPSocket.cpp b/HPSocket.cpp new file mode 100644 index 0000000..9aa9893 --- /dev/null +++ b/HPSocket.cpp @@ -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 diff --git a/HPSocket4C-SSL.cpp b/HPSocket4C-SSL.cpp new file mode 100644 index 0000000..568138d --- /dev/null +++ b/HPSocket4C-SSL.cpp @@ -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 C_HP_SSLServer; +typedef C_HP_ObjectT C_HP_SSLPullServer; +typedef C_HP_ObjectT C_HP_SSLPackServer; + +typedef C_HP_ObjectT C_HP_SSLAgent; +typedef C_HP_ObjectT C_HP_SSLPullAgent; +typedef C_HP_ObjectT C_HP_SSLPackAgent; + +typedef C_HP_ObjectT C_HP_SSLClient; +typedef C_HP_ObjectT C_HP_SSLPullClient; +typedef C_HP_ObjectT 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(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(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(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(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(pServer)->BindSSLServerName(lpszServerName, iContextIndex); +} + +HPSOCKET_API void __HP_CALL HP_SSLServer_CleanupSSLContext(HP_SSLServer pServer) +{ + C_HP_Object::ToSecond(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(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(pAgent)->SetupSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert); +} + +HPSOCKET_API void __HP_CALL HP_SSLAgent_CleanupSSLContext(HP_SSLAgent pAgent) +{ + C_HP_Object::ToSecond(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(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(pClient)->SetupSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert); +} + +HPSOCKET_API void __HP_CALL HP_SSLClient_CleanupSSLContext(HP_SSLClient pClient) +{ + C_HP_Object::ToSecond(pClient)->CleanupSSLContext(); +} + +/***************************************************************************************/ +/************************************* SSL 操作方法 ************************************/ + +HPSOCKET_API BOOL __HP_CALL HP_SSLServer_StartSSLHandShake(HP_SSLServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToSecond(pServer)->StartSSLHandShake(dwConnID); +} + +HPSOCKET_API void __HP_CALL HP_SSLServer_SetSSLAutoHandShake(HP_SSLServer pServer, BOOL bAutoHandShake) +{ + C_HP_Object::ToSecond(pServer)->SetSSLAutoHandShake(bAutoHandShake); +} + +HPSOCKET_API BOOL __HP_CALL HP_SSLServer_IsSSLAutoHandShake(HP_SSLServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->IsSSLAutoHandShake(); +} + +HPSOCKET_API void __HP_CALL HP_SSLServer_SetSSLCipherList(HP_SSLServer pServer, LPCTSTR lpszCipherList) +{ + C_HP_Object::ToSecond(pServer)->SetSSLCipherList(lpszCipherList); +} + +HPSOCKET_API LPCTSTR __HP_CALL HP_SSLServer_GetSSLCipherList(HP_SSLServer pServer) +{ + return C_HP_Object::ToSecond(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(pServer)->GetSSLSessionInfo(dwConnID, enInfo, lppInfo); +} + +HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_StartSSLHandShake(HP_SSLAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToSecond(pAgent)->StartSSLHandShake(dwConnID); +} + +HPSOCKET_API void __HP_CALL HP_SSLAgent_SetSSLAutoHandShake(HP_SSLAgent pAgent, BOOL bAutoHandShake) +{ + C_HP_Object::ToSecond(pAgent)->SetSSLAutoHandShake(bAutoHandShake); +} + +HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_IsSSLAutoHandShake(HP_SSLAgent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->IsSSLAutoHandShake(); +} + +HPSOCKET_API void __HP_CALL HP_SSLAgent_SetSSLCipherList(HP_SSLAgent pAgent, LPCTSTR lpszCipherList) +{ + C_HP_Object::ToSecond(pAgent)->SetSSLCipherList(lpszCipherList); +} + +HPSOCKET_API LPCTSTR __HP_CALL HP_SSLAgent_GetSSLCipherList(HP_SSLAgent pAgent) +{ + return C_HP_Object::ToSecond(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(pAgent)->GetSSLSessionInfo(dwConnID, enInfo, lppInfo); +} + +HPSOCKET_API BOOL __HP_CALL HP_SSLClient_StartSSLHandShake(HP_SSLClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->StartSSLHandShake(); +} + +HPSOCKET_API void __HP_CALL HP_SSLClient_SetSSLAutoHandShake(HP_SSLClient pClient, BOOL bAutoHandShake) +{ + C_HP_Object::ToSecond(pClient)->SetSSLAutoHandShake(bAutoHandShake); +} + +HPSOCKET_API BOOL __HP_CALL HP_SSLClient_IsSSLAutoHandShake(HP_SSLClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->IsSSLAutoHandShake(); +} + +HPSOCKET_API void __HP_CALL HP_SSLClient_SetSSLCipherList(HP_SSLClient pClient, LPCTSTR lpszCipherList) +{ + C_HP_Object::ToSecond(pClient)->SetSSLCipherList(lpszCipherList); +} + +HPSOCKET_API LPCTSTR __HP_CALL HP_SSLClient_GetSSLCipherList(HP_SSLClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetSSLCipherList(); +} + +HPSOCKET_API BOOL __HP_CALL HP_SSLClient_GetSSLSessionInfo(HP_SSLClient pClient, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo) +{ + return C_HP_Object::ToSecond(pClient)->GetSSLSessionInfo(enInfo, lppInfo); +} + +/*****************************************************************************************************************************************************/ +/******************************************************************** HTTPS Exports ******************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _HTTP_SUPPORT + +typedef C_HP_ObjectT C_HP_HttpsServer; +typedef C_HP_ObjectT C_HP_HttpsAgent; +typedef C_HP_ObjectT C_HP_HttpsClient; +typedef C_HP_ObjectT 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 \ No newline at end of file diff --git a/HPSocket4C.cpp b/HPSocket4C.cpp new file mode 100644 index 0000000..fb6b8af --- /dev/null +++ b/HPSocket4C.cpp @@ -0,0 +1,3747 @@ +/* + * 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 "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" +#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 ******************************************************************/ +/*****************************************************************************************************************************************************/ + +typedef C_HP_ObjectT C_HP_TcpServer; +typedef C_HP_ObjectT C_HP_TcpPullServer; +typedef C_HP_ObjectT C_HP_TcpPackServer; + +typedef C_HP_ObjectT C_HP_TcpAgent; +typedef C_HP_ObjectT C_HP_TcpPullAgent; +typedef C_HP_ObjectT C_HP_TcpPackAgent; + +typedef C_HP_ObjectT C_HP_TcpClient; +typedef C_HP_ObjectT C_HP_TcpPullClient; +typedef C_HP_ObjectT C_HP_TcpPackClient; + +#ifdef _UDP_SUPPORT + +typedef C_HP_ObjectT C_HP_UdpServer; +typedef C_HP_ObjectT C_HP_UdpClient; +typedef C_HP_ObjectT C_HP_UdpCast; +typedef C_HP_ObjectT C_HP_UdpNode; + +typedef C_HP_ObjectT C_HP_UdpArqServer; +typedef C_HP_ObjectT C_HP_UdpArqClient; + +#endif + +/****************************************************/ +/**************** TCP/UDP 对象创建函数 ***************/ + +HPSOCKET_API HP_TcpServer __HP_CALL Create_HP_TcpServer(HP_TcpServerListener pListener) +{ + return (HP_TcpServer)(new C_HP_TcpServer((ITcpServerListener*)pListener)); +} + +HPSOCKET_API HP_TcpAgent __HP_CALL Create_HP_TcpAgent(HP_TcpAgentListener pListener) +{ + return (HP_TcpAgent)(new C_HP_TcpAgent((ITcpAgentListener*)pListener)); +} + +HPSOCKET_API HP_TcpClient __HP_CALL Create_HP_TcpClient(HP_TcpClientListener pListener) +{ + return (HP_TcpClient)(new C_HP_TcpClient((ITcpClientListener*)pListener)); +} + +HPSOCKET_API HP_TcpPullServer __HP_CALL Create_HP_TcpPullServer(HP_TcpPullServerListener pListener) +{ + return (HP_TcpPullServer)(new C_HP_TcpPullServer((ITcpServerListener*)pListener)); +} + +HPSOCKET_API HP_TcpPullAgent __HP_CALL Create_HP_TcpPullAgent(HP_TcpPullAgentListener pListener) +{ + return (HP_TcpPullAgent)(new C_HP_TcpPullAgent((ITcpAgentListener*)pListener)); +} + +HPSOCKET_API HP_TcpPullClient __HP_CALL Create_HP_TcpPullClient(HP_TcpPullClientListener pListener) +{ + return (HP_TcpPullClient)(new C_HP_TcpPullClient((ITcpClientListener*)pListener)); +} + +HPSOCKET_API HP_TcpPackServer __HP_CALL Create_HP_TcpPackServer(HP_TcpServerListener pListener) +{ + return (HP_TcpPackServer)(new C_HP_TcpPackServer((ITcpServerListener*)pListener)); +} + +HPSOCKET_API HP_TcpPackAgent __HP_CALL Create_HP_TcpPackAgent(HP_TcpAgentListener pListener) +{ + return (HP_TcpPackAgent)(new C_HP_TcpPackAgent((ITcpAgentListener*)pListener)); +} + +HPSOCKET_API HP_TcpPackClient __HP_CALL Create_HP_TcpPackClient(HP_TcpClientListener pListener) +{ + return (HP_TcpPackClient)(new C_HP_TcpPackClient((ITcpClientListener*)pListener)); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpServer(HP_TcpServer pServer) +{ + delete (C_HP_TcpServer*)pServer; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpAgent(HP_TcpAgent pAgent) +{ + delete (C_HP_TcpAgent*)pAgent; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpClient(HP_TcpClient pClient) +{ + delete (C_HP_TcpClient*)pClient; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullServer(HP_TcpPullServer pServer) +{ + delete (C_HP_TcpPullServer*)pServer; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullAgent(HP_TcpPullAgent pAgent) +{ + delete (C_HP_TcpPullAgent*)pAgent; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullClient(HP_TcpPullClient pClient) +{ + delete (C_HP_TcpPullClient*)pClient; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackServer(HP_TcpPackServer pServer) +{ + delete (C_HP_TcpPackServer*)pServer; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackAgent(HP_TcpPackAgent pAgent) +{ + delete (C_HP_TcpPackAgent*)pAgent; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackClient(HP_TcpPackClient pClient) +{ + delete (C_HP_TcpPackClient*)pClient; +} + +HPSOCKET_API HP_TcpServerListener __HP_CALL Create_HP_TcpServerListener() +{ + return (HP_TcpServerListener)(new C_HP_TcpServerListener); +} + +HPSOCKET_API HP_TcpAgentListener __HP_CALL Create_HP_TcpAgentListener() +{ + return (HP_TcpAgentListener)(new C_HP_TcpAgentListener); +} + +HPSOCKET_API HP_TcpClientListener __HP_CALL Create_HP_TcpClientListener() +{ + return (HP_TcpClientListener)(new C_HP_TcpClientListener); +} + +HPSOCKET_API HP_TcpPullServerListener __HP_CALL Create_HP_TcpPullServerListener() +{ + return (HP_TcpPullServerListener)(new C_HP_TcpPullServerListener); +} + +HPSOCKET_API HP_TcpPullAgentListener __HP_CALL Create_HP_TcpPullAgentListener() +{ + return (HP_TcpPullAgentListener)(new C_HP_TcpPullAgentListener); +} + +HPSOCKET_API HP_TcpPullClientListener __HP_CALL Create_HP_TcpPullClientListener() +{ + return (HP_TcpPullClientListener)(new C_HP_TcpPullClientListener); +} + +HPSOCKET_API HP_TcpPackServerListener __HP_CALL Create_HP_TcpPackServerListener() +{ + return (HP_TcpPackServerListener)(new C_HP_TcpPackServerListener); +} + +HPSOCKET_API HP_TcpPackAgentListener __HP_CALL Create_HP_TcpPackAgentListener() +{ + return (HP_TcpPackAgentListener)(new C_HP_TcpPackAgentListener); +} + +HPSOCKET_API HP_TcpPackClientListener __HP_CALL Create_HP_TcpPackClientListener() +{ + return (HP_TcpPackClientListener)(new C_HP_TcpPackClientListener); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpServerListener(HP_TcpServerListener pListener) +{ + delete (C_HP_TcpServerListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpAgentListener(HP_TcpAgentListener pListener) +{ + delete (C_HP_TcpAgentListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpClientListener(HP_TcpClientListener pListener) +{ + delete (C_HP_TcpClientListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullServerListener(HP_TcpPullServerListener pListener) +{ + delete (C_HP_TcpPullServerListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullAgentListener(HP_TcpPullAgentListener pListener) +{ + delete (C_HP_TcpPullAgentListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullClientListener(HP_TcpPullClientListener pListener) +{ + delete (C_HP_TcpPullClientListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackServerListener(HP_TcpPackServerListener pListener) +{ + delete (C_HP_TcpPackServerListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackAgentListener(HP_TcpPackAgentListener pListener) +{ + delete (C_HP_TcpPackAgentListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackClientListener(HP_TcpPackClientListener pListener) +{ + delete (C_HP_TcpPackClientListener*)pListener; +} + +#ifdef _UDP_SUPPORT + +HPSOCKET_API HP_UdpServer __HP_CALL Create_HP_UdpServer(HP_UdpServerListener pListener) +{ + return (HP_UdpServer)(new C_HP_UdpServer((IUdpServerListener*)pListener)); +} + +HPSOCKET_API HP_UdpClient __HP_CALL Create_HP_UdpClient(HP_UdpClientListener pListener) +{ + return (HP_UdpClient)(new C_HP_UdpClient((IUdpClientListener*)pListener)); +} + +HPSOCKET_API HP_UdpCast __HP_CALL Create_HP_UdpCast(HP_UdpCastListener pListener) +{ + return (HP_UdpCast)(new C_HP_UdpCast((IUdpCastListener*)pListener)); +} + +HPSOCKET_API HP_UdpNode __HP_CALL Create_HP_UdpNode(HP_UdpNodeListener pListener) +{ + return (HP_UdpNode)(new C_HP_UdpNode((IUdpNodeListener*)pListener)); +} + +HPSOCKET_API HP_UdpArqServer __HP_CALL Create_HP_UdpArqServer(HP_UdpServerListener pListener) +{ + return (HP_UdpArqServer)(new C_HP_UdpArqServer((IUdpServerListener*)pListener)); +} + +HPSOCKET_API HP_UdpArqClient __HP_CALL Create_HP_UdpArqClient(HP_UdpClientListener pListener) +{ + return (HP_UdpArqClient)(new C_HP_UdpArqClient((IUdpClientListener*)pListener)); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpServer(HP_UdpServer pServer) +{ + delete (C_HP_UdpServer*)pServer; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpClient(HP_UdpClient pClient) +{ + delete (C_HP_UdpClient*)pClient; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpCast(HP_UdpCast pCast) +{ + delete (C_HP_UdpCast*)pCast; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpNode(HP_UdpNode pNode) +{ + delete (C_HP_UdpNode*)pNode; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqServer(HP_UdpArqServer pServer) +{ + delete (C_HP_UdpArqServer*)pServer; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqClient(HP_UdpArqClient pClient) +{ + delete (C_HP_UdpArqClient*)pClient; +} + +HPSOCKET_API HP_UdpServerListener __HP_CALL Create_HP_UdpServerListener() +{ + return (HP_UdpServerListener)(new C_HP_UdpServerListener); +} + +HPSOCKET_API HP_UdpClientListener __HP_CALL Create_HP_UdpClientListener() +{ + return (HP_UdpClientListener)(new C_HP_UdpClientListener); +} + +HPSOCKET_API HP_UdpCastListener __HP_CALL Create_HP_UdpCastListener() +{ + return (HP_UdpCastListener)(new C_HP_UdpCastListener); +} + +HPSOCKET_API HP_UdpNodeListener __HP_CALL Create_HP_UdpNodeListener() +{ + return (HP_UdpNodeListener)(new C_HP_UdpNodeListener); +} + +HPSOCKET_API HP_UdpArqServerListener __HP_CALL Create_HP_UdpArqServerListener() +{ + return (HP_UdpArqServerListener)(new C_HP_UdpArqServerListener); +} + +HPSOCKET_API HP_UdpArqClientListener __HP_CALL Create_HP_UdpArqClientListener() +{ + return (HP_UdpArqClientListener)(new C_HP_UdpArqClientListener); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpServerListener(HP_UdpServerListener pListener) +{ + delete (C_HP_UdpServerListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpClientListener(HP_UdpClientListener pListener) +{ + delete (C_HP_UdpClientListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpCastListener(HP_UdpCastListener pListener) +{ + delete (C_HP_UdpClientListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpNodeListener(HP_UdpNodeListener pListener) +{ + delete (C_HP_UdpNodeListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqServerListener(HP_UdpArqServerListener pListener) +{ + delete (C_HP_UdpArqServerListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqClientListener(HP_UdpArqClientListener pListener) +{ + delete (C_HP_UdpArqClientListener*)pListener; +} + +#endif + +/**********************************************************************************/ +/***************************** Server 回调函数设置方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnPrepareListen(HP_ServerListener pListener, HP_FN_Server_OnPrepareListen fn) +{ + ((C_HP_TcpServerListener*)pListener)->m_fnOnPrepareListen = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnAccept(HP_ServerListener pListener, HP_FN_Server_OnAccept fn) +{ + ((C_HP_TcpServerListener*)pListener)->m_fnOnAccept = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnHandShake(HP_ServerListener pListener, HP_FN_Server_OnHandShake fn) +{ + ((C_HP_TcpServerListener*)pListener)->m_fnOnHandShake = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnSend(HP_ServerListener pListener, HP_FN_Server_OnSend fn) +{ + ((C_HP_TcpServerListener*)pListener)->m_fnOnSend = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnReceive(HP_ServerListener pListener, HP_FN_Server_OnReceive fn) +{ + ((C_HP_TcpServerListener*)pListener)->m_fnOnReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnPullReceive(HP_ServerListener pListener, HP_FN_Server_OnPullReceive fn) +{ + ((C_HP_TcpServerListener*)pListener)->m_fnOnPullReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnClose(HP_ServerListener pListener, HP_FN_Server_OnClose fn) +{ + ((C_HP_TcpServerListener*)pListener)->m_fnOnClose = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnShutdown(HP_ServerListener pListener, HP_FN_Server_OnShutdown fn) +{ + ((C_HP_TcpServerListener*)pListener)->m_fnOnShutdown = fn; +} + +/**********************************************************************************/ +/***************************** Agent 回调函数设置方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnPrepareConnect(HP_AgentListener pListener, HP_FN_Agent_OnPrepareConnect fn) +{ + ((C_HP_TcpAgentListener*)pListener)->m_fnOnPrepareConnect = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnConnect(HP_AgentListener pListener, HP_FN_Agent_OnConnect fn) +{ + ((C_HP_TcpAgentListener*)pListener)->m_fnOnConnect = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnHandShake(HP_AgentListener pListener, HP_FN_Agent_OnHandShake fn) +{ + ((C_HP_TcpAgentListener*)pListener)->m_fnOnHandShake = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnSend(HP_AgentListener pListener, HP_FN_Agent_OnSend fn) +{ + ((C_HP_TcpAgentListener*)pListener)->m_fnOnSend = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnReceive(HP_AgentListener pListener, HP_FN_Agent_OnReceive fn) +{ + ((C_HP_TcpAgentListener*)pListener)->m_fnOnReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnPullReceive(HP_AgentListener pListener, HP_FN_Agent_OnPullReceive fn) +{ + ((C_HP_TcpAgentListener*)pListener)->m_fnOnPullReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnClose(HP_AgentListener pListener, HP_FN_Agent_OnClose fn) +{ + ((C_HP_TcpAgentListener*)pListener)->m_fnOnClose = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnShutdown(HP_AgentListener pListener, HP_FN_Agent_OnShutdown fn) +{ + ((C_HP_TcpAgentListener*)pListener)->m_fnOnShutdown = fn; +} + +/**********************************************************************************/ +/***************************** Client 回调函数设置方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnPrepareConnect(HP_ClientListener pListener, HP_FN_Client_OnPrepareConnect fn) +{ + ((C_HP_TcpClientListener*)pListener)->m_fnOnPrepareConnect = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnConnect(HP_ClientListener pListener, HP_FN_Client_OnConnect fn) +{ + ((C_HP_TcpClientListener*)pListener)->m_fnOnConnect = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnHandShake(HP_ClientListener pListener, HP_FN_Client_OnHandShake fn) +{ + ((C_HP_TcpClientListener*)pListener)->m_fnOnHandShake = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnSend(HP_ClientListener pListener, HP_FN_Client_OnSend fn) +{ + ((C_HP_TcpClientListener*)pListener)->m_fnOnSend = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnReceive(HP_ClientListener pListener, HP_FN_Client_OnReceive fn) +{ + ((C_HP_TcpClientListener*)pListener)->m_fnOnReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnPullReceive(HP_ClientListener pListener, HP_FN_Client_OnPullReceive fn) +{ + ((C_HP_TcpClientListener*)pListener)->m_fnOnPullReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnClose(HP_ClientListener pListener, HP_FN_Client_OnClose fn) +{ + ((C_HP_TcpClientListener*)pListener)->m_fnOnClose = fn; +} + +#ifdef _UDP_SUPPORT + +/**********************************************************************************/ +/***************************** UdpNode 回调函数设置方法 *****************************/ + + +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnPrepareListen(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnPrepareListen fn) +{ + ((C_HP_UdpNodeListener*)pListener)->m_fnOnPrepareListen = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnSend(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnSend fn) +{ + ((C_HP_UdpNodeListener*)pListener)->m_fnOnSend = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnReceive(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnReceive fn) +{ + ((C_HP_UdpNodeListener*)pListener)->m_fnOnReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnError(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnError fn) +{ + ((C_HP_UdpNodeListener*)pListener)->m_fnOnError = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnShutdown(HP_UdpNodeListener pListener, HP_FN_UdpNode_OnShutdown fn) +{ + ((C_HP_UdpNodeListener*)pListener)->m_fnOnShutdown = fn; +} + +#endif + +/**************************************************************************/ +/***************************** Server 操作方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_Server_Start(HP_Server pServer, LPCTSTR lpszBindAddress, USHORT usPort) +{ + return C_HP_Object::ToSecond(pServer)->Start(lpszBindAddress, usPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_Stop(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->Stop(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_Wait(HP_Server pServer, DWORD dwMilliseconds) +{ + return C_HP_Object::ToSecond(pServer)->Wait(dwMilliseconds); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_Send(HP_Server pServer, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength) +{ + return C_HP_Object::ToSecond(pServer)->Send(dwConnID, pBuffer, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_SendPart(HP_Server pServer, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset) +{ + return C_HP_Object::ToSecond(pServer)->Send(dwConnID, pBuffer, iLength, iOffset); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_SendPackets(HP_Server pServer, HP_CONNID dwConnID, const WSABUF pBuffers[], int iCount) +{ + return C_HP_Object::ToSecond(pServer)->SendPackets(dwConnID, pBuffers, iCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_PauseReceive(HP_Server pServer, HP_CONNID dwConnID, BOOL bPause) +{ + return C_HP_Object::ToSecond(pServer)->PauseReceive(dwConnID, bPause); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_Disconnect(HP_Server pServer, HP_CONNID dwConnID, BOOL bForce) +{ + return C_HP_Object::ToSecond(pServer)->Disconnect(dwConnID, bForce); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_DisconnectLongConnections(HP_Server pServer, DWORD dwPeriod, BOOL bForce) +{ + return C_HP_Object::ToSecond(pServer)->DisconnectLongConnections(dwPeriod, bForce); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_DisconnectSilenceConnections(HP_Server pServer, DWORD dwPeriod, BOOL bForce) +{ + return C_HP_Object::ToSecond(pServer)->DisconnectSilenceConnections(dwPeriod, bForce); +} + +/******************************************************************************/ +/***************************** Server 属性访问方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_Server_SetConnectionExtra(HP_Server pServer, HP_CONNID dwConnID, PVOID pExtra) +{ + return C_HP_Object::ToSecond(pServer)->SetConnectionExtra(dwConnID, pExtra); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_GetConnectionExtra(HP_Server pServer, HP_CONNID dwConnID, PVOID* ppExtra) +{ + return C_HP_Object::ToSecond(pServer)->GetConnectionExtra(dwConnID, ppExtra); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_IsSecure(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->IsSecure(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_HasStarted(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->HasStarted(); +} + +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_Server_GetState(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetState(); +} + +HPSOCKET_API En_HP_SocketError __HP_CALL HP_Server_GetLastError(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetLastError(); +} + +HPSOCKET_API LPCTSTR __HP_CALL HP_Server_GetLastErrorDesc(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetLastErrorDesc(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_GetPendingDataLength(HP_Server pServer, HP_CONNID dwConnID, int* piPending) +{ + return C_HP_Object::ToSecond(pServer)->GetPendingDataLength(dwConnID, *piPending); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_IsPauseReceive(HP_Server pServer, HP_CONNID dwConnID, BOOL* pbPaused) +{ + return C_HP_Object::ToSecond(pServer)->IsPauseReceive(dwConnID, *pbPaused); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_IsConnected(HP_Server pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToSecond(pServer)->IsConnected(dwConnID); +} + +HPSOCKET_API DWORD __HP_CALL HP_Server_GetConnectionCount(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetConnectionCount(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_GetAllConnectionIDs(HP_Server pServer, HP_CONNID pIDs[], DWORD* pdwCount) +{ + return C_HP_Object::ToSecond(pServer)->GetAllConnectionIDs(pIDs, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_GetConnectPeriod(HP_Server pServer, HP_CONNID dwConnID, DWORD* pdwPeriod) +{ + return C_HP_Object::ToSecond(pServer)->GetConnectPeriod(dwConnID, *pdwPeriod); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_GetSilencePeriod(HP_Server pServer, HP_CONNID dwConnID, DWORD* pdwPeriod) +{ + return C_HP_Object::ToSecond(pServer)->GetSilencePeriod(dwConnID, *pdwPeriod); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_GetListenAddress(HP_Server pServer, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pServer)->GetListenAddress(lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_GetLocalAddress(HP_Server pServer, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pServer)->GetLocalAddress(dwConnID, lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_GetRemoteAddress(HP_Server pServer, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pServer)->GetRemoteAddress(dwConnID, lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetReuseAddressPolicy(HP_Server pServer, En_HP_ReuseAddressPolicy enReusePolicy) +{ + C_HP_Object::ToSecond(pServer)->SetReuseAddressPolicy(enReusePolicy); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetSendPolicy(HP_Server pServer, En_HP_SendPolicy enSendPolicy) +{ + C_HP_Object::ToSecond(pServer)->SetSendPolicy(enSendPolicy); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetOnSendSyncPolicy(HP_Server pServer, En_HP_OnSendSyncPolicy enSyncPolicy) +{ + C_HP_Object::ToSecond(pServer)->SetOnSendSyncPolicy(enSyncPolicy); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjLockTime(HP_Server pServer, DWORD dwFreeSocketObjLockTime) +{ + C_HP_Object::ToSecond(pServer)->SetFreeSocketObjLockTime(dwFreeSocketObjLockTime); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjPool(HP_Server pServer, DWORD dwFreeSocketObjPool) +{ + C_HP_Object::ToSecond(pServer)->SetFreeSocketObjPool(dwFreeSocketObjPool); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetFreeBufferObjPool(HP_Server pServer, DWORD dwFreeBufferObjPool) +{ + C_HP_Object::ToSecond(pServer)->SetFreeBufferObjPool(dwFreeBufferObjPool); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjHold(HP_Server pServer, DWORD dwFreeSocketObjHold) +{ + C_HP_Object::ToSecond(pServer)->SetFreeSocketObjHold(dwFreeSocketObjHold); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetFreeBufferObjHold(HP_Server pServer, DWORD dwFreeBufferObjHold) +{ + C_HP_Object::ToSecond(pServer)->SetFreeBufferObjHold(dwFreeBufferObjHold); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetMaxConnectionCount(HP_Server pServer, DWORD dwMaxConnectionCount) +{ + C_HP_Object::ToSecond(pServer)->SetMaxConnectionCount(dwMaxConnectionCount); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetWorkerThreadCount(HP_Server pServer, DWORD dwWorkerThreadCount) +{ + C_HP_Object::ToSecond(pServer)->SetWorkerThreadCount(dwWorkerThreadCount); +} + +HPSOCKET_API void __HP_CALL HP_Server_SetMarkSilence(HP_Server pServer, BOOL bMarkSilence) +{ + C_HP_Object::ToSecond(pServer)->SetMarkSilence(bMarkSilence); +} + +HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Server_GetReuseAddressPolicy(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetReuseAddressPolicy(); +} + +HPSOCKET_API En_HP_SendPolicy __HP_CALL HP_Server_GetSendPolicy(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetSendPolicy(); +} + +HPSOCKET_API En_HP_OnSendSyncPolicy __HP_CALL HP_Server_GetOnSendSyncPolicy(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetOnSendSyncPolicy(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjLockTime(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetFreeSocketObjLockTime(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjPool(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetFreeSocketObjPool(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeBufferObjPool(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetFreeBufferObjPool(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjHold(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetFreeSocketObjHold(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeBufferObjHold(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetFreeBufferObjHold(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Server_GetMaxConnectionCount(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetMaxConnectionCount(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Server_GetWorkerThreadCount(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetWorkerThreadCount(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Server_IsMarkSilence(HP_Server pServer) +{ + return C_HP_Object::ToSecond(pServer)->IsMarkSilence(); +} + +/**********************************************************************************/ +/******************************* TCP Server 操作方法 *******************************/ + +HPSOCKET_API BOOL __HP_CALL HP_TcpServer_SendSmallFile(HP_Server pServer, HP_CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail) +{ + return C_HP_Object::ToSecond(pServer)->SendSmallFile(dwConnID, lpszFileName, pHead, pTail); +} + +/**********************************************************************************/ +/***************************** TCP Server 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_TcpServer_SetAcceptSocketCount(HP_TcpServer pServer, DWORD dwAcceptSocketCount) +{ + C_HP_Object::ToSecond(pServer)->SetAcceptSocketCount(dwAcceptSocketCount); +} + +HPSOCKET_API void __HP_CALL HP_TcpServer_SetSocketBufferSize(HP_TcpServer pServer, DWORD dwSocketBufferSize) +{ + C_HP_Object::ToSecond(pServer)->SetSocketBufferSize(dwSocketBufferSize); +} + +HPSOCKET_API void __HP_CALL HP_TcpServer_SetSocketListenQueue(HP_TcpServer pServer, DWORD dwSocketListenQueue) +{ + C_HP_Object::ToSecond(pServer)->SetSocketListenQueue(dwSocketListenQueue); +} + +HPSOCKET_API void __HP_CALL HP_TcpServer_SetKeepAliveTime(HP_TcpServer pServer, DWORD dwKeepAliveTime) +{ + C_HP_Object::ToSecond(pServer)->SetKeepAliveTime(dwKeepAliveTime); +} + +HPSOCKET_API void __HP_CALL HP_TcpServer_SetKeepAliveInterval(HP_TcpServer pServer, DWORD dwKeepAliveInterval) +{ + C_HP_Object::ToSecond(pServer)->SetKeepAliveInterval(dwKeepAliveInterval); +} + +HPSOCKET_API void __HP_CALL HP_TcpServer_SetNoDelay(HP_TcpServer pServer, BOOL bNoDelay) +{ + C_HP_Object::ToSecond(pServer)->SetNoDelay(bNoDelay); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetAcceptSocketCount(HP_TcpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetAcceptSocketCount(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetSocketBufferSize(HP_TcpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetSocketBufferSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetSocketListenQueue(HP_TcpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetSocketListenQueue(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetKeepAliveTime(HP_TcpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetKeepAliveTime(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetKeepAliveInterval(HP_TcpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetKeepAliveInterval(); +} + +HPSOCKET_API BOOL __HP_CALL HP_TcpServer_IsNoDelay(HP_TcpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->IsNoDelay(); +} + +#ifdef _UDP_SUPPORT + +/**********************************************************************************/ +/***************************** UDP Server 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_UdpServer_SetMaxDatagramSize(HP_UdpServer pServer, DWORD dwMaxDatagramSize) +{ + C_HP_Object::ToSecond(pServer)->SetMaxDatagramSize(dwMaxDatagramSize); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetMaxDatagramSize(HP_UdpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetMaxDatagramSize(); +} + +HPSOCKET_API void __HP_CALL HP_UdpServer_SetPostReceiveCount(HP_UdpServer pServer, DWORD dwPostReceiveCount) +{ + C_HP_Object::ToSecond(pServer)->SetPostReceiveCount(dwPostReceiveCount); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetPostReceiveCount(HP_UdpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetPostReceiveCount(); +} + +HPSOCKET_API void __HP_CALL HP_UdpServer_SetDetectAttempts(HP_UdpServer pServer, DWORD dwDetectAttempts) +{ + C_HP_Object::ToSecond(pServer)->SetDetectAttempts(dwDetectAttempts); +} + +HPSOCKET_API void __HP_CALL HP_UdpServer_SetDetectInterval(HP_UdpServer pServer, DWORD dwDetectInterval) +{ + C_HP_Object::ToSecond(pServer)->SetDetectInterval(dwDetectInterval); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetDetectAttempts(HP_UdpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetDetectAttempts(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetDetectInterval(HP_UdpServer pServer) +{ + return C_HP_Object::ToSecond(pServer)->GetDetectInterval(); +} + +/**********************************************************************************/ +/*************************** UDP ARQ Server 属性访问方法 ***************************/ + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetNoDelay(HP_UdpArqServer pServer, BOOL bNoDelay) +{ + C_HP_Object::ToFirst(pServer)->SetNoDelay(bNoDelay); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetTurnoffCongestCtrl(HP_UdpArqServer pServer, BOOL bTurnOff) +{ + C_HP_Object::ToFirst(pServer)->SetTurnoffCongestCtrl(bTurnOff); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetFlushInterval(HP_UdpArqServer pServer, DWORD dwFlushInterval) +{ + C_HP_Object::ToFirst(pServer)->SetFlushInterval(dwFlushInterval); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetResendByAcks(HP_UdpArqServer pServer, DWORD dwResendByAcks) +{ + C_HP_Object::ToFirst(pServer)->SetResendByAcks(dwResendByAcks); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetSendWndSize(HP_UdpArqServer pServer, DWORD dwSendWndSize) +{ + C_HP_Object::ToFirst(pServer)->SetSendWndSize(dwSendWndSize); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetRecvWndSize(HP_UdpArqServer pServer, DWORD dwRecvWndSize) +{ + C_HP_Object::ToFirst(pServer)->SetRecvWndSize(dwRecvWndSize); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMinRto(HP_UdpArqServer pServer, DWORD dwMinRto) +{ + C_HP_Object::ToFirst(pServer)->SetMinRto(dwMinRto); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetFastLimit(HP_UdpArqServer pServer, DWORD dwFastLimit) +{ + C_HP_Object::ToFirst(pServer)->SetFastLimit(dwFastLimit); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMaxTransUnit(HP_UdpArqServer pServer, DWORD dwMaxTransUnit) +{ + C_HP_Object::ToFirst(pServer)->SetMaxTransUnit(dwMaxTransUnit); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMaxMessageSize(HP_UdpArqServer pServer, DWORD dwMaxMessageSize) +{ + C_HP_Object::ToFirst(pServer)->SetMaxMessageSize(dwMaxMessageSize); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetHandShakeTimeout(HP_UdpArqServer pServer, DWORD dwHandShakeTimeout) +{ + C_HP_Object::ToFirst(pServer)->SetHandShakeTimeout(dwHandShakeTimeout); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_IsNoDelay(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->IsNoDelay(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_IsTurnoffCongestCtrl(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->IsTurnoffCongestCtrl(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetFlushInterval(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetFlushInterval(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetResendByAcks(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetResendByAcks(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetSendWndSize(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetSendWndSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetRecvWndSize(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetRecvWndSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMinRto(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetMinRto(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetFastLimit(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetFastLimit(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMaxTransUnit(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetMaxTransUnit(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMaxMessageSize(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetMaxMessageSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetHandShakeTimeout(HP_UdpArqServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetHandShakeTimeout(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_GetWaitingSendMessageCount(HP_UdpArqServer pServer, HP_CONNID dwConnID, int* piCount) +{ + return C_HP_Object::ToFirst(pServer)->GetWaitingSendMessageCount(dwConnID, *piCount); +} + +#endif + +/**************************************************************************/ +/***************************** Agent 操作方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_Agent_Start(HP_Agent pAgent, LPCTSTR lpszBindAddress, BOOL bAsyncConnect) +{ + return C_HP_Object::ToSecond(pAgent)->Start(lpszBindAddress, bAsyncConnect); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_Stop(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->Stop(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_Wait(HP_Agent pAgent, DWORD dwMilliseconds) +{ + return C_HP_Object::ToSecond(pAgent)->Wait(dwMilliseconds); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_Connect(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID) +{ + return C_HP_Object::ToSecond(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtra(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra) +{ + return C_HP_Object::ToSecond(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, pExtra); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithLocalPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, USHORT usLocalPort) +{ + return C_HP_Object::ToSecond(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, nullptr, usLocalPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithLocalAddress(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, LPCTSTR lpszLocalAddress) +{ + return C_HP_Object::ToSecond(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, nullptr, 0, lpszLocalAddress); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtraAndLocalPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra, USHORT usLocalPort) +{ + return C_HP_Object::ToSecond(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, pExtra, usLocalPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtraAndLocalAddressPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra, USHORT usLocalPort, LPCTSTR lpszLocalAddress) +{ + return C_HP_Object::ToSecond(pAgent)->Connect(lpszRemoteAddress, usPort, pdwConnID, pExtra, usLocalPort, lpszLocalAddress); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_Send(HP_Agent pAgent, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength) +{ + return C_HP_Object::ToSecond(pAgent)->Send(dwConnID, pBuffer, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_SendPart(HP_Agent pAgent, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset) +{ + return C_HP_Object::ToSecond(pAgent)->Send(dwConnID, pBuffer, iLength, iOffset); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_SendPackets(HP_Agent pAgent, HP_CONNID dwConnID, const WSABUF pBuffers[], int iCount) +{ + return C_HP_Object::ToSecond(pAgent)->SendPackets(dwConnID, pBuffers, iCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_PauseReceive(HP_Agent pAgent, HP_CONNID dwConnID, BOOL bPause) +{ + return C_HP_Object::ToSecond(pAgent)->PauseReceive(dwConnID, bPause); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_Disconnect(HP_Agent pAgent, HP_CONNID dwConnID, BOOL bForce) +{ + return C_HP_Object::ToSecond(pAgent)->Disconnect(dwConnID, bForce); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_DisconnectLongConnections(HP_Agent pAgent, DWORD dwPeriod, BOOL bForce) +{ + return C_HP_Object::ToSecond(pAgent)->DisconnectLongConnections(dwPeriod, bForce); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_DisconnectSilenceConnections(HP_Agent pAgent, DWORD dwPeriod, BOOL bForce) +{ + return C_HP_Object::ToSecond(pAgent)->DisconnectSilenceConnections(dwPeriod, bForce); +} + +/******************************************************************************/ +/***************************** Agent 属性访问方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_Agent_SetConnectionExtra(HP_Agent pAgent, HP_CONNID dwConnID, PVOID pExtra) +{ + return C_HP_Object::ToSecond(pAgent)->SetConnectionExtra(dwConnID, pExtra); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetConnectionExtra(HP_Agent pAgent, HP_CONNID dwConnID, PVOID* ppExtra) +{ + return C_HP_Object::ToSecond(pAgent)->GetConnectionExtra(dwConnID, ppExtra); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_IsSecure(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->IsSecure(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_HasStarted(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->HasStarted(); +} + +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_Agent_GetState(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetState(); +} + +HPSOCKET_API En_HP_SocketError __HP_CALL HP_Agent_GetLastError(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetLastError(); +} + +HPSOCKET_API LPCTSTR __HP_CALL HP_Agent_GetLastErrorDesc(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetLastErrorDesc(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetPendingDataLength(HP_Agent pAgent, HP_CONNID dwConnID, int* piPending) +{ + return C_HP_Object::ToSecond(pAgent)->GetPendingDataLength(dwConnID, *piPending); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_IsPauseReceive(HP_Agent pAgent, HP_CONNID dwConnID, BOOL* pbPaused) +{ + return C_HP_Object::ToSecond(pAgent)->IsPauseReceive(dwConnID, *pbPaused); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_IsConnected(HP_Agent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToSecond(pAgent)->IsConnected(dwConnID); +} + +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetConnectionCount(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetConnectionCount(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetAllConnectionIDs(HP_Agent pAgent, HP_CONNID pIDs[], DWORD* pdwCount) +{ + return C_HP_Object::ToSecond(pAgent)->GetAllConnectionIDs(pIDs, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetConnectPeriod(HP_Agent pAgent, HP_CONNID dwConnID, DWORD* pdwPeriod) +{ + return C_HP_Object::ToSecond(pAgent)->GetConnectPeriod(dwConnID, *pdwPeriod); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetSilencePeriod(HP_Agent pAgent, HP_CONNID dwConnID, DWORD* pdwPeriod) +{ + return C_HP_Object::ToSecond(pAgent)->GetSilencePeriod(dwConnID, *pdwPeriod); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetLocalAddress(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pAgent)->GetLocalAddress(dwConnID, lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetRemoteAddress(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pAgent)->GetRemoteAddress(dwConnID, lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetRemoteHost(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszHost[], int* piHostLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pAgent)->GetRemoteHost(dwConnID, lpszHost, *piHostLen, *pusPort); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetReuseAddressPolicy(HP_Agent pAgent, En_HP_ReuseAddressPolicy enReusePolicy) +{ + C_HP_Object::ToSecond(pAgent)->SetReuseAddressPolicy(enReusePolicy); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetSendPolicy(HP_Agent pAgent, En_HP_SendPolicy enSendPolicy) +{ + C_HP_Object::ToSecond(pAgent)->SetSendPolicy(enSendPolicy); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetOnSendSyncPolicy(HP_Agent pAgent, En_HP_OnSendSyncPolicy enSyncPolicy) +{ + C_HP_Object::ToSecond(pAgent)->SetOnSendSyncPolicy(enSyncPolicy); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjLockTime(HP_Agent pAgent, DWORD dwFreeSocketObjLockTime) +{ + C_HP_Object::ToSecond(pAgent)->SetFreeSocketObjLockTime(dwFreeSocketObjLockTime); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjPool(HP_Agent pAgent, DWORD dwFreeSocketObjPool) +{ + C_HP_Object::ToSecond(pAgent)->SetFreeSocketObjPool(dwFreeSocketObjPool); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeBufferObjPool(HP_Agent pAgent, DWORD dwFreeBufferObjPool) +{ + C_HP_Object::ToSecond(pAgent)->SetFreeBufferObjPool(dwFreeBufferObjPool); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjHold(HP_Agent pAgent, DWORD dwFreeSocketObjHold) +{ + C_HP_Object::ToSecond(pAgent)->SetFreeSocketObjHold(dwFreeSocketObjHold); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeBufferObjHold(HP_Agent pAgent, DWORD dwFreeBufferObjHold) +{ + C_HP_Object::ToSecond(pAgent)->SetFreeBufferObjHold(dwFreeBufferObjHold); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetMaxConnectionCount(HP_Agent pAgent, DWORD dwMaxConnectionCount) +{ + C_HP_Object::ToSecond(pAgent)->SetMaxConnectionCount(dwMaxConnectionCount); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetWorkerThreadCount(HP_Agent pAgent, DWORD dwWorkerThreadCount) +{ + C_HP_Object::ToSecond(pAgent)->SetWorkerThreadCount(dwWorkerThreadCount); +} + +HPSOCKET_API void __HP_CALL HP_Agent_SetMarkSilence(HP_Agent pAgent, BOOL bMarkSilence) +{ + C_HP_Object::ToSecond(pAgent)->SetMarkSilence(bMarkSilence); +} + +HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Agent_GetReuseAddressPolicy(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetReuseAddressPolicy(); +} + +HPSOCKET_API En_HP_SendPolicy __HP_CALL HP_Agent_GetSendPolicy(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetSendPolicy(); +} + +HPSOCKET_API En_HP_OnSendSyncPolicy __HP_CALL HP_Agent_GetOnSendSyncPolicy(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetOnSendSyncPolicy(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjLockTime(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetFreeSocketObjLockTime(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjPool(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetFreeSocketObjPool(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeBufferObjPool(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetFreeBufferObjPool(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjHold(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetFreeSocketObjHold(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeBufferObjHold(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetFreeBufferObjHold(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetMaxConnectionCount(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetMaxConnectionCount(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetWorkerThreadCount(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetWorkerThreadCount(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Agent_IsMarkSilence(HP_Agent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->IsMarkSilence(); +} + +/**********************************************************************************/ +/******************************* TCP Agent 操作方法 *******************************/ + +HPSOCKET_API BOOL __HP_CALL HP_TcpAgent_SendSmallFile(HP_Agent pAgent, HP_CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail) +{ + return C_HP_Object::ToSecond(pAgent)->SendSmallFile(dwConnID, lpszFileName, pHead, pTail); +} + +/**********************************************************************************/ +/***************************** TCP Agent 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetSyncConnectTimeout(HP_TcpAgent pAgent, DWORD dwSyncConnectTimeout) +{ + C_HP_Object::ToSecond(pAgent)->SetSyncConnectTimeout(dwSyncConnectTimeout); +} + +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetSocketBufferSize(HP_TcpAgent pAgent, DWORD dwSocketBufferSize) +{ + C_HP_Object::ToSecond(pAgent)->SetSocketBufferSize(dwSocketBufferSize); +} + +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetKeepAliveTime(HP_TcpAgent pAgent, DWORD dwKeepAliveTime) +{ + C_HP_Object::ToSecond(pAgent)->SetKeepAliveTime(dwKeepAliveTime); +} + +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetKeepAliveInterval(HP_TcpAgent pAgent, DWORD dwKeepAliveInterval) +{ + C_HP_Object::ToSecond(pAgent)->SetKeepAliveInterval(dwKeepAliveInterval); +} + +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetNoDelay(HP_TcpAgent pAgent, BOOL bNoDelay) +{ + C_HP_Object::ToSecond(pAgent)->SetNoDelay(bNoDelay); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetSyncConnectTimeout(HP_TcpAgent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetSyncConnectTimeout(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetSocketBufferSize(HP_TcpAgent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetSocketBufferSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetKeepAliveTime(HP_TcpAgent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetKeepAliveTime(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetKeepAliveInterval(HP_TcpAgent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->GetKeepAliveInterval(); +} + +HPSOCKET_API BOOL __HP_CALL HP_TcpAgent_IsNoDelay(HP_TcpAgent pAgent) +{ + return C_HP_Object::ToSecond(pAgent)->IsNoDelay(); +} + +/******************************************************************************/ +/***************************** Client 组件操作方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_Client_Start(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect) +{ + return C_HP_Object::ToSecond(pClient)->Start(lpszRemoteAddress, usPort, bAsyncConnect); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_StartWithBindAddress(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress) +{ + return C_HP_Object::ToSecond(pClient)->Start(lpszRemoteAddress, usPort, bAsyncConnect, lpszBindAddress); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_StartWithBindAddressAndLocalPort(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort) +{ + return C_HP_Object::ToSecond(pClient)->Start(lpszRemoteAddress, usPort, bAsyncConnect, lpszBindAddress, usLocalPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_Stop(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->Stop(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_Send(HP_Client pClient, const BYTE* pBuffer, int iLength) +{ + return C_HP_Object::ToSecond(pClient)->Send(pBuffer, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_SendPart(HP_Client pClient, const BYTE* pBuffer, int iLength, int iOffset) +{ + return C_HP_Object::ToSecond(pClient)->Send(pBuffer, iLength, iOffset); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_SendPackets(HP_Client pClient, const WSABUF pBuffers[], int iCount) +{ + return C_HP_Object::ToSecond(pClient)->SendPackets(pBuffers, iCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_PauseReceive(HP_Client pClient, BOOL bPause) +{ + return C_HP_Object::ToSecond(pClient)->PauseReceive(bPause); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_Wait(HP_Client pClient, DWORD dwMilliseconds) +{ + return C_HP_Object::ToSecond(pClient)->Wait(dwMilliseconds); +} + +/******************************************************************************/ +/***************************** Client 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_Client_SetExtra(HP_Client pClient, PVOID pExtra) +{ + C_HP_Object::ToSecond(pClient)->SetExtra(pExtra); +} + +HPSOCKET_API PVOID __HP_CALL HP_Client_GetExtra(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetExtra(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_IsSecure(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->IsSecure(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_HasStarted(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->HasStarted(); +} + +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_Client_GetState(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetState(); +} + +HPSOCKET_API En_HP_SocketError __HP_CALL HP_Client_GetLastError(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetLastError(); +} + +HPSOCKET_API LPCTSTR __HP_CALL HP_Client_GetLastErrorDesc(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetLastErrorDesc(); +} + +HPSOCKET_API HP_CONNID __HP_CALL HP_Client_GetConnectionID(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetConnectionID(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_GetLocalAddress(HP_Client pClient, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pClient)->GetLocalAddress(lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_GetRemoteHost(HP_Client pClient, TCHAR lpszHost[], int* piHostLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pClient)->GetRemoteHost(lpszHost, *piHostLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_GetPendingDataLength(HP_Client pClient, int* piPending) +{ + return C_HP_Object::ToSecond(pClient)->GetPendingDataLength(*piPending); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_IsPauseReceive(HP_Client pClient, BOOL* pbPaused) +{ + return C_HP_Object::ToSecond(pClient)->IsPauseReceive(*pbPaused); +} + +HPSOCKET_API BOOL __HP_CALL HP_Client_IsConnected(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->IsConnected(); +} + +HPSOCKET_API void __HP_CALL HP_Client_SetReuseAddressPolicy(HP_Client pClient, En_HP_ReuseAddressPolicy enReusePolicy) +{ + C_HP_Object::ToSecond(pClient)->SetReuseAddressPolicy(enReusePolicy); +} + +HPSOCKET_API void __HP_CALL HP_Client_SetFreeBufferPoolSize(HP_Client pClient, DWORD dwFreeBufferPoolSize) +{ + C_HP_Object::ToSecond(pClient)->SetFreeBufferPoolSize(dwFreeBufferPoolSize); +} + +HPSOCKET_API void __HP_CALL HP_Client_SetFreeBufferPoolHold(HP_Client pClient, DWORD dwFreeBufferPoolHold) +{ + C_HP_Object::ToSecond(pClient)->SetFreeBufferPoolHold(dwFreeBufferPoolHold); +} + +HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Client_GetReuseAddressPolicy(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetReuseAddressPolicy(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Client_GetFreeBufferPoolSize(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetFreeBufferPoolSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_Client_GetFreeBufferPoolHold(HP_Client pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetFreeBufferPoolHold(); +} + +/**********************************************************************************/ +/******************************* TCP Client 操作方法 *******************************/ + +HPSOCKET_API BOOL __HP_CALL HP_TcpClient_SendSmallFile(HP_Client pClient, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail) +{ + return C_HP_Object::ToSecond(pClient)->SendSmallFile(lpszFileName, pHead, pTail); +} + +/**********************************************************************************/ +/***************************** TCP Client 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_TcpClient_SetSyncConnectTimeout(HP_TcpClient pClient, DWORD dwSyncConnectTimeout) +{ + C_HP_Object::ToSecond(pClient)->SetSyncConnectTimeout(dwSyncConnectTimeout); +} + +HPSOCKET_API void __HP_CALL HP_TcpClient_SetSocketBufferSize(HP_TcpClient pClient, DWORD dwSocketBufferSize) +{ + C_HP_Object::ToSecond(pClient)->SetSocketBufferSize(dwSocketBufferSize); +} + +HPSOCKET_API void __HP_CALL HP_TcpClient_SetKeepAliveTime(HP_TcpClient pClient, DWORD dwKeepAliveTime) +{ + C_HP_Object::ToSecond(pClient)->SetKeepAliveTime(dwKeepAliveTime); +} + +HPSOCKET_API void __HP_CALL HP_TcpClient_SetKeepAliveInterval(HP_TcpClient pClient, DWORD dwKeepAliveInterval) +{ + C_HP_Object::ToSecond(pClient)->SetKeepAliveInterval(dwKeepAliveInterval); +} + +HPSOCKET_API void __HP_CALL HP_TcpClient_SetNoDelay(HP_TcpClient pClient, BOOL bNoDelay) +{ + C_HP_Object::ToSecond(pClient)->SetNoDelay(bNoDelay); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetSyncConnectTimeout(HP_TcpClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetSyncConnectTimeout(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetSocketBufferSize(HP_TcpClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetSocketBufferSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetKeepAliveTime(HP_TcpClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetKeepAliveTime(); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetKeepAliveInterval(HP_TcpClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetKeepAliveInterval(); +} + +HPSOCKET_API BOOL __HP_CALL HP_TcpClient_IsNoDelay(HP_TcpClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->IsNoDelay(); +} + +#ifdef _UDP_SUPPORT + +/**********************************************************************************/ +/***************************** UDP Client 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_UdpClient_SetMaxDatagramSize(HP_UdpClient pClient, DWORD dwMaxDatagramSize) +{ + C_HP_Object::ToSecond(pClient)->SetMaxDatagramSize(dwMaxDatagramSize); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetMaxDatagramSize(HP_UdpClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetMaxDatagramSize(); +} + +HPSOCKET_API void __HP_CALL HP_UdpClient_SetDetectAttempts(HP_UdpClient pClient, DWORD dwDetectAttempts) +{ + C_HP_Object::ToSecond(pClient)->SetDetectAttempts(dwDetectAttempts); +} + +HPSOCKET_API void __HP_CALL HP_UdpClient_SetDetectInterval(HP_UdpClient pClient, DWORD dwDetectInterval) +{ + C_HP_Object::ToSecond(pClient)->SetDetectInterval(dwDetectInterval); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetDetectAttempts(HP_UdpClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetDetectAttempts(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetDetectInterval(HP_UdpClient pClient) +{ + return C_HP_Object::ToSecond(pClient)->GetDetectInterval(); +} + +/**********************************************************************************/ +/*************************** UDP ARQ Client 属性访问方法 ***************************/ + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetNoDelay(HP_UdpArqClient pClient, BOOL bNoDelay) +{ + C_HP_Object::ToFirst(pClient)->SetNoDelay(bNoDelay); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetTurnoffCongestCtrl(HP_UdpArqClient pClient, BOOL bTurnOff) +{ + C_HP_Object::ToFirst(pClient)->SetTurnoffCongestCtrl(bTurnOff); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetFlushInterval(HP_UdpArqClient pClient, DWORD dwFlushInterval) +{ + C_HP_Object::ToFirst(pClient)->SetFlushInterval(dwFlushInterval); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetResendByAcks(HP_UdpArqClient pClient, DWORD dwResendByAcks) +{ + C_HP_Object::ToFirst(pClient)->SetResendByAcks(dwResendByAcks); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetSendWndSize(HP_UdpArqClient pClient, DWORD dwSendWndSize) +{ + C_HP_Object::ToFirst(pClient)->SetSendWndSize(dwSendWndSize); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetRecvWndSize(HP_UdpArqClient pClient, DWORD dwRecvWndSize) +{ + C_HP_Object::ToFirst(pClient)->SetRecvWndSize(dwRecvWndSize); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMinRto(HP_UdpArqClient pClient, DWORD dwMinRto) +{ + C_HP_Object::ToFirst(pClient)->SetMinRto(dwMinRto); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetFastLimit(HP_UdpArqClient pClient, DWORD dwFastLimit) +{ + C_HP_Object::ToFirst(pClient)->SetFastLimit(dwFastLimit); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMaxTransUnit(HP_UdpArqClient pClient, DWORD dwMaxTransUnit) +{ + C_HP_Object::ToFirst(pClient)->SetMaxTransUnit(dwMaxTransUnit); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMaxMessageSize(HP_UdpArqClient pClient, DWORD dwMaxMessageSize) +{ + C_HP_Object::ToFirst(pClient)->SetMaxMessageSize(dwMaxMessageSize); +} + +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetHandShakeTimeout(HP_UdpArqClient pClient, DWORD dwHandShakeTimeout) +{ + C_HP_Object::ToFirst(pClient)->SetHandShakeTimeout(dwHandShakeTimeout); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_IsNoDelay(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->IsNoDelay(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_IsTurnoffCongestCtrl(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->IsTurnoffCongestCtrl(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetFlushInterval(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetFlushInterval(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetResendByAcks(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetResendByAcks(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetSendWndSize(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetSendWndSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetRecvWndSize(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetRecvWndSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMinRto(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetMinRto(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetFastLimit(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetFastLimit(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMaxTransUnit(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetMaxTransUnit(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMaxMessageSize(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetMaxMessageSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetHandShakeTimeout(HP_UdpArqClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetHandShakeTimeout(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_GetWaitingSendMessageCount(HP_UdpArqClient pClient, int* piCount) +{ + return C_HP_Object::ToFirst(pClient)->GetWaitingSendMessageCount(*piCount); +} + +/**********************************************************************************/ +/****************************** UDP Cast 属性访问方法 ******************************/ + +HPSOCKET_API void __HP_CALL HP_UdpCast_SetMaxDatagramSize(HP_UdpCast pCast, DWORD dwMaxDatagramSize) +{ + C_HP_Object::ToSecond(pCast)->SetMaxDatagramSize(dwMaxDatagramSize); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpCast_GetMaxDatagramSize(HP_UdpCast pCast) +{ + return C_HP_Object::ToSecond(pCast)->GetMaxDatagramSize(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpCast_GetRemoteAddress(HP_UdpCast pCast, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pCast)->GetRemoteAddress(lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API void __HP_CALL HP_UdpCast_SetCastMode(HP_UdpCast pCast, En_HP_CastMode enCastMode) +{ + C_HP_Object::ToSecond(pCast)->SetCastMode(enCastMode); +} + +HPSOCKET_API En_HP_CastMode __HP_CALL HP_UdpCast_GetCastMode(HP_UdpCast pCast) +{ + return C_HP_Object::ToSecond(pCast)->GetCastMode(); +} + +HPSOCKET_API void __HP_CALL HP_UdpCast_SetMultiCastTtl(HP_UdpCast pCast, int iMCTtl) +{ + C_HP_Object::ToSecond(pCast)->SetMultiCastTtl(iMCTtl); +} + +HPSOCKET_API int __HP_CALL HP_UdpCast_GetMultiCastTtl(HP_UdpCast pCast) +{ + return C_HP_Object::ToSecond(pCast)->GetMultiCastTtl(); +} + +HPSOCKET_API void __HP_CALL HP_UdpCast_SetMultiCastLoop(HP_UdpCast pCast, BOOL bMCLoop) +{ + C_HP_Object::ToSecond(pCast)->SetMultiCastLoop(bMCLoop); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpCast_IsMultiCastLoop(HP_UdpCast pCast) +{ + return C_HP_Object::ToSecond(pCast)->IsMultiCastLoop(); +} + +/**********************************************************************************/ +/****************************** UDP Node 组件操作方法 ******************************/ + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Start(HP_UdpNode pNode, LPCTSTR lpszBindAddress, USHORT usPort) +{ + return C_HP_Object::ToSecond(pNode)->Start(lpszBindAddress, usPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_StartWithCast(HP_UdpNode pNode, LPCTSTR lpszBindAddress, USHORT usPort, En_HP_CastMode enCastMode, LPCTSTR lpszCastAddress) +{ + return C_HP_Object::ToSecond(pNode)->Start(lpszBindAddress, usPort, enCastMode, lpszCastAddress); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Stop(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->Stop(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Wait(HP_UdpNode pNode, DWORD dwMilliseconds) +{ + return C_HP_Object::ToSecond(pNode)->Wait(dwMilliseconds); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Send(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength) +{ + return C_HP_Object::ToSecond(pNode)->Send(lpszRemoteAddress, usRemotePort, pBuffer, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendPart(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength, int iOffset) +{ + return C_HP_Object::ToSecond(pNode)->Send(lpszRemoteAddress, usRemotePort, pBuffer, iLength, iOffset); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendPackets(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const WSABUF pBuffers[], int iCount) +{ + return C_HP_Object::ToSecond(pNode)->SendPackets(lpszRemoteAddress, usRemotePort, pBuffers, iCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCast(HP_UdpNode pNode, const BYTE* pBuffer, int iLength) +{ + return C_HP_Object::ToSecond(pNode)->SendCast(pBuffer, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCastPart(HP_UdpNode pNode, const BYTE* pBuffer, int iLength, int iOffset) +{ + return C_HP_Object::ToSecond(pNode)->SendCast(pBuffer, iLength, iOffset); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCastPackets(HP_UdpNode pNode, const WSABUF pBuffers[], int iCount) +{ + return C_HP_Object::ToSecond(pNode)->SendCastPackets(pBuffers, iCount); +} + +/**********************************************************************************/ +/****************************** UDP Node 属性访问方法 ******************************/ + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetExtra(HP_UdpNode pNode, PVOID pExtra) +{ + C_HP_Object::ToSecond(pNode)->SetExtra(pExtra); +} + +HPSOCKET_API PVOID __HP_CALL HP_UdpNode_GetExtra(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetExtra(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_HasStarted(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->HasStarted(); +} + +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_UdpNode_GetState(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetState(); +} + +HPSOCKET_API En_HP_SocketError __HP_CALL HP_UdpNode_GetLastError(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetLastError(); +} + +HPSOCKET_API LPCTSTR __HP_CALL HP_UdpNode_GetLastErrorDesc(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetLastErrorDesc(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetLocalAddress(HP_UdpNode pNode, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pNode)->GetLocalAddress(lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetCastAddress(HP_UdpNode pNode, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return C_HP_Object::ToSecond(pNode)->GetCastAddress(lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API En_HP_CastMode __HP_CALL HP_UdpNode_GetCastMode(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetCastMode(); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetPendingDataLength(HP_UdpNode pNode, int* piPending) +{ + return C_HP_Object::ToSecond(pNode)->GetPendingDataLength(*piPending); +} + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetMaxDatagramSize(HP_UdpNode pNode, DWORD dwMaxDatagramSize) +{ + C_HP_Object::ToSecond(pNode)->SetMaxDatagramSize(dwMaxDatagramSize); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetMaxDatagramSize(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetMaxDatagramSize(); +} + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetMultiCastTtl(HP_UdpNode pNode, int iMCTtl) +{ + C_HP_Object::ToSecond(pNode)->SetMultiCastTtl(iMCTtl); +} + +HPSOCKET_API int __HP_CALL HP_UdpNode_GetMultiCastTtl(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetMultiCastTtl(); +} + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetMultiCastLoop(HP_UdpNode pNode, BOOL bMCLoop) +{ + C_HP_Object::ToSecond(pNode)->SetMultiCastLoop(bMCLoop); +} + +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_IsMultiCastLoop(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->IsMultiCastLoop(); +} + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetReuseAddressPolicy(HP_UdpNode pNode, En_HP_ReuseAddressPolicy enReusePolicy) +{ + C_HP_Object::ToSecond(pNode)->SetReuseAddressPolicy(enReusePolicy); +} + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetWorkerThreadCount(HP_UdpNode pNode, DWORD dwWorkerThreadCount) +{ + C_HP_Object::ToSecond(pNode)->SetWorkerThreadCount(dwWorkerThreadCount); +} + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetPostReceiveCount(HP_UdpNode pNode, DWORD dwPostReceiveCount) +{ + C_HP_Object::ToSecond(pNode)->SetPostReceiveCount(dwPostReceiveCount); +} + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetFreeBufferPoolSize(HP_UdpNode pNode, DWORD dwFreeBufferPoolSize) +{ + C_HP_Object::ToSecond(pNode)->SetFreeBufferPoolSize(dwFreeBufferPoolSize); +} + +HPSOCKET_API void __HP_CALL HP_UdpNode_SetFreeBufferPoolHold(HP_UdpNode pNode, DWORD dwFreeBufferPoolHold) +{ + C_HP_Object::ToSecond(pNode)->SetFreeBufferPoolHold(dwFreeBufferPoolHold); +} + +HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_UdpNode_GetReuseAddressPolicy(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetReuseAddressPolicy(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetWorkerThreadCount(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetWorkerThreadCount(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetPostReceiveCount(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetPostReceiveCount(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetFreeBufferPoolSize(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetFreeBufferPoolSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetFreeBufferPoolHold(HP_UdpNode pNode) +{ + return C_HP_Object::ToSecond(pNode)->GetFreeBufferPoolHold(); +} + +#endif + +/***************************************************************************************/ +/***************************** TCP Pull Server 组件操作方法 *****************************/ + +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullServer_Fetch(HP_TcpPullServer pServer, HP_CONNID dwConnID, BYTE* pData, int iLength) +{ + return C_HP_Object::ToFirst(pServer)->Fetch(dwConnID, pData, iLength); +} + +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullServer_Peek(HP_TcpPullServer pServer, HP_CONNID dwConnID, BYTE* pData, int iLength) +{ + return C_HP_Object::ToFirst(pServer)->Peek(dwConnID, pData, iLength); +} + +/***************************************************************************************/ +/***************************** TCP Pull Server 属性访问方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pull Agent 组件操作方法 *****************************/ + +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullAgent_Fetch(HP_TcpPullAgent pAgent, HP_CONNID dwConnID, BYTE* pData, int iLength) +{ + return C_HP_Object::ToFirst(pAgent)->Fetch(dwConnID, pData, iLength); +} + +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullAgent_Peek(HP_TcpPullAgent pAgent, HP_CONNID dwConnID, BYTE* pData, int iLength) +{ + return C_HP_Object::ToFirst(pAgent)->Peek(dwConnID, pData, iLength); +} + +/***************************************************************************************/ +/***************************** TCP Pull Agent 属性访问方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pull Client 组件操作方法 *****************************/ + +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullClient_Fetch(HP_TcpPullClient pClient, BYTE* pData, int iLength) +{ + return C_HP_Object::ToFirst(pClient)->Fetch(pData, iLength); +} + +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullClient_Peek(HP_TcpPullClient pClient, BYTE* pData, int iLength) +{ + return C_HP_Object::ToFirst(pClient)->Peek(pData, iLength); +} + +/***************************************************************************************/ +/***************************** TCP Pull Client 属性访问方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pack Server 组件操作方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pack Server 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_TcpPackServer_SetMaxPackSize(HP_TcpPackServer pServer, DWORD dwMaxPackSize) +{ + C_HP_Object::ToFirst(pServer)->SetMaxPackSize(dwMaxPackSize); +} + +HPSOCKET_API void __HP_CALL HP_TcpPackServer_SetPackHeaderFlag(HP_TcpPackServer pServer, USHORT usPackHeaderFlag) +{ + C_HP_Object::ToFirst(pServer)->SetPackHeaderFlag(usPackHeaderFlag); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpPackServer_GetMaxPackSize(HP_TcpPackServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetMaxPackSize(); +} + +HPSOCKET_API USHORT __HP_CALL HP_TcpPackServer_GetPackHeaderFlag(HP_TcpPackServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetPackHeaderFlag(); +} + +/***************************************************************************************/ +/***************************** TCP Pack Agent 组件操作方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pack Agent 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_TcpPackAgent_SetMaxPackSize(HP_TcpPackAgent pAgent, DWORD dwMaxPackSize) +{ + C_HP_Object::ToFirst(pAgent)->SetMaxPackSize(dwMaxPackSize); +} + +HPSOCKET_API void __HP_CALL HP_TcpPackAgent_SetPackHeaderFlag(HP_TcpPackAgent pAgent, USHORT usPackHeaderFlag) +{ + C_HP_Object::ToFirst(pAgent)->SetPackHeaderFlag(usPackHeaderFlag); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpPackAgent_GetMaxPackSize(HP_TcpPackAgent pAgent) +{ + return C_HP_Object::ToFirst(pAgent)->GetMaxPackSize(); +} + +HPSOCKET_API USHORT __HP_CALL HP_TcpPackAgent_GetPackHeaderFlag(HP_TcpPackAgent pAgent) +{ + return C_HP_Object::ToFirst(pAgent)->GetPackHeaderFlag(); +} + +/***************************************************************************************/ +/***************************** TCP Pack Client 组件操作方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pack Client 属性访问方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_TcpPackClient_SetMaxPackSize(HP_TcpPackClient pClient, DWORD dwMaxPackSize) +{ + C_HP_Object::ToFirst(pClient)->SetMaxPackSize(dwMaxPackSize); +} + +HPSOCKET_API void __HP_CALL HP_TcpPackClient_SetPackHeaderFlag(HP_TcpPackClient pClient, USHORT usPackHeaderFlag) +{ + C_HP_Object::ToFirst(pClient)->SetPackHeaderFlag(usPackHeaderFlag); +} + +HPSOCKET_API DWORD __HP_CALL HP_TcpPackClient_GetMaxPackSize(HP_TcpPackClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetMaxPackSize(); +} + +HPSOCKET_API USHORT __HP_CALL HP_TcpPackClient_GetPackHeaderFlag(HP_TcpPackClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetPackHeaderFlag(); +} + +/*****************************************************************************************************************************************************/ +/*************************************************************** Global Function Exports *************************************************************/ +/*****************************************************************************************************************************************************/ + +HPSOCKET_API DWORD __HP_CALL HP_GetHPSocketVersion() +{ + return ::GetHPSocketVersion(); +} + +HPSOCKET_API LPCTSTR __HP_CALL HP_GetSocketErrorDesc(En_HP_SocketError enCode) +{ + return ::GetSocketErrorDesc(enCode); +} + +HPSOCKET_API DWORD __HP_CALL SYS_GetLastError() +{ + return ::GetLastError(); +} + +HPSOCKET_API LPCSTR __HP_CALL SYS_GetLastErrorStr() +{ + return ::GetLastErrorStr(); +} + +HPSOCKET_API int __HP_CALL SYS_SetSocketOption(SOCKET sock, int level, int name, LPVOID val, int len) +{ + return ::SSO_SetSocketOption(sock, level, name, val, len); +} + +HPSOCKET_API int __HP_CALL SYS_GetSocketOption(SOCKET sock, int level, int name, LPVOID val, int* len) +{ + return ::SSO_GetSocketOption(sock, level, name, val, len); +} + +HPSOCKET_API int __HP_CALL SYS_IoctlSocket(SOCKET sock, long cmd, ULONG* arg) +{ + return ::SSO_IoctlSocket(sock, cmd, arg); +} + +HPSOCKET_API BOOL __HP_CALL SYS_fcntl_SETFL(FD fd, INT fl, BOOL bSet) +{ + return ::fcntl_SETFL(fd, fl, bSet); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_NoBlock(SOCKET sock, BOOL bNoBlock) +{ + return ::SSO_NoBlock(sock, bNoBlock); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_NoDelay(SOCKET sock, BOOL bNoDelay) +{ + return ::SSO_NoDelay(sock, bNoDelay); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_DontLinger(SOCKET sock, BOOL bDont) +{ + return ::SSO_DontLinger(sock, bDont); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_Linger(SOCKET sock, USHORT l_onoff, USHORT l_linger) +{ + return ::SSO_Linger(sock, l_onoff, l_linger); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_RecvBuffSize(SOCKET sock, int size) +{ + return ::SSO_RecvBuffSize(sock, size); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_SendBuffSize(SOCKET sock, int size) +{ + return ::SSO_SendBuffSize(sock, size); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_RecvTimeOut(SOCKET sock, int ms) +{ + return ::SSO_RecvTimeOut(sock, ms); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_SendTimeOut(SOCKET sock, int ms) +{ + return ::SSO_SendTimeOut(sock, ms); +} + +HPSOCKET_API int __HP_CALL SYS_SSO_ReuseAddress(SOCKET sock, En_HP_ReuseAddressPolicy opt) +{ + return ::SSO_ReuseAddress(sock, opt); +} + +HPSOCKET_API BOOL __HP_CALL SYS_GetSocketLocalAddress(SOCKET socket, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return ::GetSocketLocalAddress(socket, lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL SYS_GetSocketRemoteAddress(SOCKET socket, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort) +{ + return ::GetSocketRemoteAddress(socket, lpszAddress, *piAddressLen, *pusPort); +} + +HPSOCKET_API BOOL __HP_CALL SYS_EnumHostIPAddresses(LPCTSTR lpszHost, En_HP_IPAddrType enType, HP_LPTIPAddr** lpppIPAddr, int* piIPAddrCount) +{ + return ::EnumHostIPAddresses(lpszHost, enType, lpppIPAddr, *piIPAddrCount); +} + +HPSOCKET_API BOOL __HP_CALL SYS_FreeHostIPAddresses(HP_LPTIPAddr* lppIPAddr) +{ + return ::FreeHostIPAddresses(lppIPAddr); +} + +HPSOCKET_API BOOL __HP_CALL SYS_IsIPAddress(LPCTSTR lpszAddress, En_HP_IPAddrType* penType) +{ + return ::IsIPAddress(lpszAddress, penType); +} + +HPSOCKET_API BOOL __HP_CALL SYS_GetIPAddress(LPCTSTR lpszHost, TCHAR lpszIP[], int* piIPLenth, En_HP_IPAddrType* penType) +{ + return ::GetIPAddress(lpszHost, lpszIP, *piIPLenth, *penType); +} + +HPSOCKET_API ULONGLONG __HP_CALL SYS_NToH64(ULONGLONG value) +{ + return ::NToH64(value); +} + +HPSOCKET_API ULONGLONG __HP_CALL SYS_HToN64(ULONGLONG value) +{ + return ::HToN64(value); +} + +HPSOCKET_API USHORT __HP_CALL SYS_SwapEndian16(USHORT value) +{ + return ENDIAN_SWAP_16(value); +} + +HPSOCKET_API DWORD __HP_CALL SYS_SwapEndian32(DWORD value) +{ + return ENDIAN_SWAP_32(value); +} + +HPSOCKET_API BOOL __HP_CALL SYS_IsLittleEndian() +{ + return ::IsLittleEndian(); +} + +HPSOCKET_API LPBYTE __HP_CALL SYS_Malloc(int size) +{ + return MALLOC(BYTE, size); +} + +HPSOCKET_API LPBYTE __HP_CALL SYS_Realloc(LPBYTE p, int size) +{ + return REALLOC(BYTE, p, size); +} + +HPSOCKET_API VOID __HP_CALL SYS_Free(LPBYTE p) +{ + FREE(p); +} + +HPSOCKET_API LPVOID __HP_CALL SYS_Calloc(int number, int size) +{ + return CALLOC(number, size); +} + +HPSOCKET_API DWORD __HP_CALL SYS_GuessBase64EncodeBound(DWORD dwSrcLen) +{ + return ::GuessBase64EncodeBound(dwSrcLen); +} + +HPSOCKET_API DWORD __HP_CALL SYS_GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) +{ + return ::GuessBase64DecodeBound(lpszSrc, dwSrcLen); +} + +HPSOCKET_API int __HP_CALL SYS_Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::Base64Encode(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API int __HP_CALL SYS_Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::Base64Decode(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API DWORD __HP_CALL SYS_GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) +{ + return ::GuessUrlEncodeBound(lpszSrc, dwSrcLen); +} + +HPSOCKET_API DWORD __HP_CALL SYS_GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) +{ + return ::GuessUrlDecodeBound(lpszSrc, dwSrcLen); +} + +HPSOCKET_API int __HP_CALL SYS_UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::UrlEncode(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API int __HP_CALL SYS_UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::UrlDecode(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +#ifdef _ZLIB_SUPPORT + +HPSOCKET_API int __HP_CALL SYS_Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::Compress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API int __HP_CALL SYS_CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iLevel, int iMethod, int iWindowBits, int iMemLevel, int iStrategy) +{ + return ::CompressEx(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy); +} + +HPSOCKET_API int __HP_CALL SYS_Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::Uncompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API int __HP_CALL SYS_UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iWindowBits) +{ + return ::UncompressEx(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen, iWindowBits); +} + +HPSOCKET_API DWORD __HP_CALL SYS_GuessCompressBound(DWORD dwSrcLen, BOOL bGZip) +{ + return ::GuessCompressBound(dwSrcLen, bGZip); +} + +HPSOCKET_API int __HP_CALL SYS_GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::GZipCompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API int __HP_CALL SYS_GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::GZipUncompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API DWORD __HP_CALL SYS_GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen) +{ + return ::GZipGuessUncompressBound(lpszSrc, dwSrcLen); +} + +#endif + +#ifdef _BROTLI_SUPPORT + +HPSOCKET_API int __HP_CALL SYS_BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::BrotliCompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API int __HP_CALL SYS_BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iQuality, int iWindow, int iMode) +{ + return ::BrotliCompressEx(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen, iQuality, iWindow , iMode); +} + +HPSOCKET_API int __HP_CALL SYS_BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen) +{ + return ::BrotliUncompress(lpszSrc, dwSrcLen, lpszDest, *pdwDestLen); +} + +HPSOCKET_API DWORD __HP_CALL SYS_BrotliGuessCompressBound(DWORD dwSrcLen) +{ + return ::BrotliGuessCompressBound(dwSrcLen); +} + +#endif + +#ifdef _ICONV_SUPPORT + +HPSOCKET_API BOOL __HP_CALL SYS_CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int* piOutBufLen) +{ + return ::CharsetConvert(lpszFromCharset, lpszToCharset, lpszInBuf, iInBufLen, lpszOutBuf, *piOutBufLen); +} + +HPSOCKET_API BOOL __HP_CALL SYS_GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int* piDestLength) +{ + return ::GbkToUnicodeEx(szSrc, iSrcLength, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int* piDestLength) +{ + return ::UnicodeToGbkEx(szSrc, iSrcLength, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int* piDestLength) +{ + return ::Utf8ToUnicodeEx(szSrc, iSrcLength, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int* piDestLength) +{ + return ::UnicodeToUtf8Ex(szSrc, iSrcLength, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int* piDestLength) +{ + return ::GbkToUtf8Ex(szSrc, iSrcLength, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int* piDestLength) +{ + return ::Utf8ToGbkEx(szSrc, iSrcLength, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_GbkToUnicode(const char szSrc[], WCHAR szDest[], int* piDestLength) +{ + return ::GbkToUnicode(szSrc, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToGbk(const WCHAR szSrc[], char szDest[], int* piDestLength) +{ + return ::UnicodeToGbk(szSrc, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int* piDestLength) +{ + return ::Utf8ToUnicode(szSrc, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int* piDestLength) +{ + return ::UnicodeToUtf8(szSrc, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_GbkToUtf8(const char szSrc[], char szDest[], int* piDestLength) +{ + return ::GbkToUtf8(szSrc, szDest, *piDestLength); +} + +HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToGbk(const char szSrc[], char szDest[], int* piDestLength) +{ + return ::Utf8ToGbk(szSrc, szDest, *piDestLength); +} + +#endif + +/*****************************************************************************************************************************************************/ +/******************************************************************** HTTP Exports *******************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _HTTP_SUPPORT + +typedef C_HP_ObjectT C_HP_HttpServer; +typedef C_HP_ObjectT C_HP_HttpAgent; +typedef C_HP_ObjectT C_HP_HttpClient; +typedef C_HP_ObjectT C_HP_HttpSyncClient; + +/****************************************************/ +/***************** HTTP 对象创建函数 *****************/ + +HPSOCKET_API HP_HttpServer __HP_CALL Create_HP_HttpServer(HP_HttpServerListener pListener) +{ + return (HP_HttpServer)(new C_HP_HttpServer((IHttpServerListener*)pListener)); +} + +HPSOCKET_API HP_HttpAgent __HP_CALL Create_HP_HttpAgent(HP_HttpAgentListener pListener) +{ + return (HP_HttpAgent)(new C_HP_HttpAgent((IHttpAgentListener*)pListener)); +} + +HPSOCKET_API HP_HttpClient __HP_CALL Create_HP_HttpClient(HP_HttpClientListener pListener) +{ + return (HP_HttpClient)(new C_HP_HttpClient((IHttpClientListener*)pListener)); +} + +HPSOCKET_API HP_HttpSyncClient __HP_CALL Create_HP_HttpSyncClient(HP_HttpClientListener pListener) +{ + return (HP_HttpSyncClient)(new C_HP_HttpSyncClient((IHttpClientListener*)pListener)); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_HttpServer(HP_HttpServer pServer) +{ + delete (C_HP_HttpServer*)pServer; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_HttpAgent(HP_HttpAgent pAgent) +{ + delete (C_HP_HttpAgent*)pAgent; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_HttpClient(HP_HttpClient pClient) +{ + delete (C_HP_HttpClient*)pClient; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_HttpSyncClient(HP_HttpSyncClient pClient) +{ + delete (C_HP_HttpSyncClient*)pClient; +} + +HPSOCKET_API HP_HttpServerListener __HP_CALL Create_HP_HttpServerListener() +{ + return (HP_HttpServerListener)(new C_HP_HttpServerListener); +} + +HPSOCKET_API HP_HttpAgentListener __HP_CALL Create_HP_HttpAgentListener() +{ + return (HP_HttpAgentListener)(new C_HP_HttpAgentListener); +} + +HPSOCKET_API HP_HttpClientListener __HP_CALL Create_HP_HttpClientListener() +{ + return (HP_HttpClientListener)(new C_HP_HttpClientListener); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_HttpServerListener(HP_HttpServerListener pListener) +{ + delete (C_HP_HttpServerListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_HttpAgentListener(HP_HttpAgentListener pListener) +{ + delete (C_HP_HttpAgentListener*)pListener; +} + +HPSOCKET_API void __HP_CALL Destroy_HP_HttpClientListener(HP_HttpClientListener pListener) +{ + delete (C_HP_HttpClientListener*)pListener; +} + +/**********************************************************************************/ +/*************************** HTTP Server 回调函数设置方法 **************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnMessageBegin(HP_HttpServerListener pListener, HP_FN_HttpServer_OnMessageBegin fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnMessageBegin = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnRequestLine(HP_HttpServerListener pListener, HP_FN_HttpServer_OnRequestLine fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnRequestLine = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHeader(HP_HttpServerListener pListener, HP_FN_HttpServer_OnHeader fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHeadersComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnHeadersComplete fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnHeadersComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnBody(HP_HttpServerListener pListener, HP_FN_HttpServer_OnBody fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnBody = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnChunkHeader(HP_HttpServerListener pListener, HP_FN_HttpServer_OnChunkHeader fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnChunkHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnChunkComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnChunkComplete fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnChunkComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnMessageComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnMessageComplete fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnMessageComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnUpgrade(HP_HttpServerListener pListener, HP_FN_HttpServer_OnUpgrade fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnUpgrade = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnParseError(HP_HttpServerListener pListener, HP_FN_HttpServer_OnParseError fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnParseError = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageHeader(HP_HttpServerListener pListener, HP_FN_HttpServer_OnWSMessageHeader fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnWSMessageHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageBody(HP_HttpServerListener pListener, HP_FN_HttpServer_OnWSMessageBody fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnWSMessageBody = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnWSMessageComplete fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnHttp.m_fnOnWSMessageComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnPrepareListen(HP_HttpServerListener pListener, HP_FN_HttpServer_OnPrepareListen fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnPrepareListen = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnAccept(HP_HttpServerListener pListener, HP_FN_HttpServer_OnAccept fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnAccept = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHandShake(HP_HttpServerListener pListener, HP_FN_HttpServer_OnHandShake fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnHandShake = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnSend(HP_HttpServerListener pListener, HP_FN_HttpServer_OnSend fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnSend = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnReceive(HP_HttpServerListener pListener, HP_FN_HttpServer_OnReceive fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnClose(HP_HttpServerListener pListener, HP_FN_HttpServer_OnClose fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnClose = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnShutdown(HP_HttpServerListener pListener, HP_FN_HttpServer_OnShutdown fn) +{ + ((C_HP_HttpServerListener*)pListener)->m_lsnServer.m_fnOnShutdown = fn; +} + +/**********************************************************************************/ +/**************************** HTTP Agent 回调函数设置方法 **************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnMessageBegin(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnMessageBegin fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnMessageBegin = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnStatusLine(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnStatusLine fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnStatusLine = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHeader(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnHeader fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHeadersComplete(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnHeadersComplete fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnHeadersComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnBody(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnBody fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnBody = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnChunkHeader(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnChunkHeader fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnChunkHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnChunkComplete(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnChunkComplete fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnChunkComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnMessageComplete(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnMessageComplete fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnMessageComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnUpgrade(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnUpgrade fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnUpgrade = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnParseError(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnParseError fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnParseError = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageHeader(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnWSMessageHeader fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnWSMessageHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageBody(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnWSMessageBody fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnWSMessageBody = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageComplete(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnWSMessageComplete fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnHttp.m_fnOnWSMessageComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnPrepareConnect(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnPrepareConnect fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnPrepareConnect = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnConnect(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnConnect fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnConnect = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHandShake(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnHandShake fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnHandShake = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnReceive(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnReceive fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnSend(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnSend fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnSend = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnClose(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnClose fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnClose = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnShutdown(HP_HttpAgentListener pListener, HP_FN_HttpAgent_OnShutdown fn) +{ + ((C_HP_HttpAgentListener*)pListener)->m_lsnAgent.m_fnOnShutdown = fn; +} + +/**********************************************************************************/ +/*************************** HTTP Client 回调函数设置方法 **************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnMessageBegin(HP_HttpClientListener pListener, HP_FN_HttpClient_OnMessageBegin fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnMessageBegin = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnStatusLine(HP_HttpClientListener pListener, HP_FN_HttpClient_OnStatusLine fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnStatusLine = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHeader(HP_HttpClientListener pListener, HP_FN_HttpClient_OnHeader fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHeadersComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnHeadersComplete fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnHeadersComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnBody(HP_HttpClientListener pListener, HP_FN_HttpClient_OnBody fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnBody = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnChunkHeader(HP_HttpClientListener pListener, HP_FN_HttpClient_OnChunkHeader fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnChunkHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnChunkComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnChunkComplete fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnChunkComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnMessageComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnMessageComplete fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnMessageComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnUpgrade(HP_HttpClientListener pListener, HP_FN_HttpClient_OnUpgrade fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnUpgrade = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnParseError(HP_HttpClientListener pListener, HP_FN_HttpClient_OnParseError fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnParseError = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageHeader(HP_HttpClientListener pListener, HP_FN_HttpClient_OnWSMessageHeader fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnWSMessageHeader = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageBody(HP_HttpClientListener pListener, HP_FN_HttpClient_OnWSMessageBody fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnWSMessageBody = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnWSMessageComplete fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnHttp.m_fnOnWSMessageComplete = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnPrepareConnect(HP_HttpClientListener pListener, HP_FN_HttpClient_OnPrepareConnect fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnPrepareConnect = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnConnect(HP_HttpClientListener pListener, HP_FN_HttpClient_OnConnect fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnConnect = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHandShake(HP_HttpClientListener pListener, HP_FN_HttpClient_OnHandShake fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnHandShake = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnReceive(HP_HttpClientListener pListener, HP_FN_HttpClient_OnReceive fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnReceive = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnSend(HP_HttpClientListener pListener, HP_FN_HttpClient_OnSend fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnSend = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnClose(HP_HttpClientListener pListener, HP_FN_HttpClient_OnClose fn) +{ + ((C_HP_HttpClientListener*)pListener)->m_lsnClient.m_fnOnClose = fn; +} + +/**************************************************************************/ +/*************************** HTTP Server 操作方法 **************************/ + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendResponse(HP_HttpServer pServer, HP_CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pData, int iLength) +{ + return C_HP_Object::ToFirst(pServer)->SendResponse(dwConnID, usStatusCode, lpszDesc, lpHeaders, iHeaderCount, pData, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendLocalFile(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszFileName, USHORT usStatusCode, LPCSTR lpszDesc, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pServer)->SendLocalFile(dwConnID, lpszFileName, usStatusCode, lpszDesc, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendChunkData(HP_HttpServer pServer, HP_CONNID dwConnID, const BYTE* pData, int iLength, LPCSTR lpszExtensions) +{ + return C_HP_Object::ToFirst(pServer)->SendChunkData(dwConnID, pData, iLength, lpszExtensions); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendWSMessage(HP_HttpServer pServer, HP_CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE* pData, int iLength, ULONGLONG ullBodyLen) +{ + return C_HP_Object::ToFirst(pServer)->SendWSMessage(dwConnID, bFinal, iReserved, iOperationCode, pData, iLength, ullBodyLen); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_Release(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->Release(dwConnID); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_StartHttp(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->StartHttp(dwConnID); +} + +/******************************************************************************/ +/*************************** HTTP Server 属性访问方法 **************************/ + +HPSOCKET_API void __HP_CALL HP_HttpServer_SetReleaseDelay(HP_HttpServer pServer, DWORD dwReleaseDelay) +{ + C_HP_Object::ToFirst(pServer)->SetReleaseDelay(dwReleaseDelay); +} + +HPSOCKET_API DWORD __HP_CALL HP_HttpServer_GetReleaseDelay(HP_HttpServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetReleaseDelay(); +} + +HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetUrlFieldSet(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetUrlFieldSet(dwConnID); +} + + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetUrlField(HP_HttpServer pServer, HP_CONNID dwConnID, En_HP_HttpUrlField enField) +{ + return C_HP_Object::ToFirst(pServer)->GetUrlField(dwConnID, enField); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetMethod(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetMethod(dwConnID); +} + +HPSOCKET_API void __HP_CALL HP_HttpServer_SetLocalVersion(HP_HttpServer pServer, En_HP_HttpVersion usVersion) +{ + C_HP_Object::ToFirst(pServer)->SetLocalVersion(usVersion); +} + +HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpServer_GetLocalVersion(HP_HttpServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->GetLocalVersion(); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsUpgrade(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->IsUpgrade(dwConnID); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsKeepAlive(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->IsKeepAlive(dwConnID); +} + +HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetVersion(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetVersion(dwConnID); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetHost(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetHost(dwConnID); +} + +HPSOCKET_API ULONGLONG __HP_CALL HP_HttpServer_GetContentLength(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetContentLength(dwConnID); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetContentType(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetContentType(dwConnID); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetContentEncoding(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetContentEncoding(dwConnID); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetTransferEncoding(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetTransferEncoding(dwConnID); +} + +HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpServer_GetUpgradeType(HP_HttpServer pServer, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pServer)->GetUpgradeType(dwConnID); +} + +HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetParseErrorCode(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR* lpszErrorDesc) +{ + return C_HP_Object::ToFirst(pServer)->GetParseErrorCode(dwConnID, lpszErrorDesc); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetHeader(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) +{ + return C_HP_Object::ToFirst(pServer)->GetHeader(dwConnID, lpszName, lpszValue); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetHeaders(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pServer)->GetHeaders(dwConnID, lpszName, lpszValue, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllHeaders(HP_HttpServer pServer, HP_CONNID dwConnID, HP_THeader lpHeaders[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pServer)->GetAllHeaders(dwConnID, lpHeaders, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllHeaderNames(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pServer)->GetAllHeaderNames(dwConnID, lpszName, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetCookie(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) +{ + return C_HP_Object::ToFirst(pServer)->GetCookie(dwConnID, lpszName, lpszValue); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllCookies(HP_HttpServer pServer, HP_CONNID dwConnID, HP_TCookie lpCookies[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pServer)->GetAllCookies(dwConnID, lpCookies, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetWSMessageState(HP_HttpServer pServer, HP_CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain) +{ + return C_HP_Object::ToFirst(pServer)->GetWSMessageState(dwConnID, lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain); +} + +HPSOCKET_API void __HP_CALL HP_HttpServer_SetHttpAutoStart(HP_HttpServer pServer, BOOL bAutoStart) +{ + C_HP_Object::ToFirst(pServer)->SetHttpAutoStart(bAutoStart); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsHttpAutoStart(HP_HttpServer pServer) +{ + return C_HP_Object::ToFirst(pServer)->IsHttpAutoStart(); +} + +/**************************************************************************/ +/*************************** HTTP Agent 操作方法 ***************************/ + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendRequest(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) +{ + return C_HP_Object::ToFirst(pAgent)->SendRequest(dwConnID, lpszMethod, lpszPath, lpHeaders, iHeaderCount, pBody, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendLocalFile(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pAgent)->SendLocalFile(dwConnID, lpszFileName, lpszMethod, lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendChunkData(HP_HttpAgent pAgent, HP_CONNID dwConnID, const BYTE* pData, int iLength, LPCSTR lpszExtensions) +{ + return C_HP_Object::ToFirst(pAgent)->SendChunkData(dwConnID, pData, iLength, lpszExtensions); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPost(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) +{ + return C_HP_Object::ToFirst(pAgent)->SendPost(dwConnID, lpszPath, lpHeaders, iHeaderCount, pBody, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPut(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) +{ + return C_HP_Object::ToFirst(pAgent)->SendPut(dwConnID, lpszPath, lpHeaders, iHeaderCount, pBody, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPatch(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) +{ + return C_HP_Object::ToFirst(pAgent)->SendPatch(dwConnID, lpszPath, lpHeaders, iHeaderCount, pBody, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendGet(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pAgent)->SendGet(dwConnID, lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendDelete(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pAgent)->SendDelete(dwConnID, lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendHead(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pAgent)->SendHead(dwConnID, lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendTrace(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pAgent)->SendTrace(dwConnID, lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendOptions(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pAgent)->SendOptions(dwConnID, lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendConnect(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszHost, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pAgent)->SendConnect(dwConnID, lpszHost, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendWSMessage(HP_HttpAgent pAgent, HP_CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen) +{ + return C_HP_Object::ToFirst(pAgent)->SendWSMessage(dwConnID, bFinal, iReserved, iOperationCode, lpszMask, pData, iLength, ullBodyLen); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_StartHttp(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->StartHttp(dwConnID); +} + +/******************************************************************************/ +/*************************** HTTP Agent 属性访问方法 ***************************/ + +HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetStatusCode(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->GetStatusCode(dwConnID); +} + +HPSOCKET_API void __HP_CALL HP_HttpAgent_SetLocalVersion(HP_HttpAgent pAgent, En_HP_HttpVersion usVersion) +{ + C_HP_Object::ToFirst(pAgent)->SetLocalVersion(usVersion); +} + +HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpAgent_GetLocalVersion(HP_HttpAgent pAgent) +{ + return C_HP_Object::ToFirst(pAgent)->GetLocalVersion(); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsUpgrade(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->IsUpgrade(dwConnID); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsKeepAlive(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->IsKeepAlive(dwConnID); +} + +HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetVersion(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->GetVersion(dwConnID); +} + +HPSOCKET_API ULONGLONG __HP_CALL HP_HttpAgent_GetContentLength(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->GetContentLength(dwConnID); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetContentType(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->GetContentType(dwConnID); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetContentEncoding(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->GetContentEncoding(dwConnID); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetTransferEncoding(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->GetTransferEncoding(dwConnID); +} + +HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpAgent_GetUpgradeType(HP_HttpAgent pAgent, HP_CONNID dwConnID) +{ + return C_HP_Object::ToFirst(pAgent)->GetUpgradeType(dwConnID); +} + +HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetParseErrorCode(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR* lpszErrorDesc) +{ + return C_HP_Object::ToFirst(pAgent)->GetParseErrorCode(dwConnID, lpszErrorDesc); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetHeader(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) +{ + return C_HP_Object::ToFirst(pAgent)->GetHeader(dwConnID, lpszName, lpszValue); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetHeaders(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pAgent)->GetHeaders(dwConnID, lpszName, lpszValue, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllHeaders(HP_HttpAgent pAgent, HP_CONNID dwConnID, HP_THeader lpHeaders[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pAgent)->GetAllHeaders(dwConnID, lpHeaders, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllHeaderNames(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pAgent)->GetAllHeaderNames(dwConnID, lpszName, *pdwCount); +} + +HPSOCKET_API void __HP_CALL HP_HttpAgent_SetUseCookie(HP_HttpAgent pAgent, BOOL bUseCookie) +{ + C_HP_Object::ToFirst(pAgent)->SetUseCookie(bUseCookie); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsUseCookie(HP_HttpAgent pAgent) +{ + return C_HP_Object::ToFirst(pAgent)->IsUseCookie(); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetCookie(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) +{ + return C_HP_Object::ToFirst(pAgent)->GetCookie(dwConnID, lpszName, lpszValue); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllCookies(HP_HttpAgent pAgent, HP_CONNID dwConnID, HP_TCookie lpCookies[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pAgent)->GetAllCookies(dwConnID, lpCookies, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetWSMessageState(HP_HttpAgent pAgent, HP_CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain) +{ + return C_HP_Object::ToFirst(pAgent)->GetWSMessageState(dwConnID, lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain); +} + +HPSOCKET_API void __HP_CALL HP_HttpAgent_SetHttpAutoStart(HP_HttpAgent pAgent, BOOL bAutoStart) +{ + C_HP_Object::ToFirst(pAgent)->SetHttpAutoStart(bAutoStart); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsHttpAutoStart(HP_HttpAgent pAgent) +{ + return C_HP_Object::ToFirst(pAgent)->IsHttpAutoStart(); +} + +/**************************************************************************/ +/*************************** HTTP Client 操作方法 **************************/ + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendRequest(HP_HttpClient pClient, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) +{ + return C_HP_Object::ToFirst(pClient)->SendRequest(lpszMethod, lpszPath, lpHeaders, iHeaderCount, pBody, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendLocalFile(HP_HttpClient pClient, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pClient)->SendLocalFile(lpszFileName, lpszMethod, lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendChunkData(HP_HttpClient pClient, const BYTE* pData, int iLength, LPCSTR lpszExtensions) +{ + return C_HP_Object::ToFirst(pClient)->SendChunkData(pData, iLength, lpszExtensions); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPost(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) +{ + return C_HP_Object::ToFirst(pClient)->SendPost(lpszPath, lpHeaders, iHeaderCount, pBody, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPut(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) +{ + return C_HP_Object::ToFirst(pClient)->SendPut(lpszPath, lpHeaders, iHeaderCount, pBody, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPatch(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) +{ + return C_HP_Object::ToFirst(pClient)->SendPatch(lpszPath, lpHeaders, iHeaderCount, pBody, iLength); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendGet(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pClient)->SendGet(lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendDelete(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pClient)->SendDelete(lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendHead(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pClient)->SendHead(lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendTrace(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pClient)->SendTrace(lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendOptions(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pClient)->SendOptions(lpszPath, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendConnect(HP_HttpClient pClient, LPCSTR lpszHost, const HP_THeader lpHeaders[], int iHeaderCount) +{ + return C_HP_Object::ToFirst(pClient)->SendConnect(lpszHost, lpHeaders, iHeaderCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendWSMessage(HP_HttpClient pClient, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen) +{ + return C_HP_Object::ToFirst(pClient)->SendWSMessage(bFinal, iReserved, iOperationCode, lpszMask, pData, iLength, ullBodyLen); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_StartHttp(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->StartHttp(); +} + +/******************************************************************************/ +/*************************** HTTP Client 属性访问方法 **************************/ + +HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetStatusCode(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetStatusCode(); +} + +HPSOCKET_API void __HP_CALL HP_HttpClient_SetLocalVersion(HP_HttpClient pClient, En_HP_HttpVersion usVersion) +{ + C_HP_Object::ToFirst(pClient)->SetLocalVersion(usVersion); +} + +HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpClient_GetLocalVersion(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetLocalVersion(); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsUpgrade(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->IsUpgrade(); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsKeepAlive(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->IsKeepAlive(); +} + +HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetVersion(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetVersion(); +} + +HPSOCKET_API ULONGLONG __HP_CALL HP_HttpClient_GetContentLength(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetContentLength(); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetContentType(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetContentType(); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetContentEncoding(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetContentEncoding(); +} + +HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetTransferEncoding(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetTransferEncoding(); +} + +HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpClient_GetUpgradeType(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetUpgradeType(); +} + +HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetParseErrorCode(HP_HttpClient pClient, LPCSTR* lpszErrorDesc) +{ + return C_HP_Object::ToFirst(pClient)->GetParseErrorCode(lpszErrorDesc); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetHeader(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR* lpszValue) +{ + return C_HP_Object::ToFirst(pClient)->GetHeader(lpszName, lpszValue); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetHeaders(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pClient)->GetHeaders(lpszName, lpszValue, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllHeaders(HP_HttpClient pClient, HP_THeader lpHeaders[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pClient)->GetAllHeaders(lpHeaders, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllHeaderNames(HP_HttpClient pClient, LPCSTR lpszName[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pClient)->GetAllHeaderNames(lpszName, *pdwCount); +} + +HPSOCKET_API void __HP_CALL HP_HttpClient_SetUseCookie(HP_HttpClient pClient, BOOL bUseCookie) +{ + C_HP_Object::ToFirst(pClient)->SetUseCookie(bUseCookie); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsUseCookie(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->IsUseCookie(); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetCookie(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR* lpszValue) +{ + return C_HP_Object::ToFirst(pClient)->GetCookie(lpszName, lpszValue); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllCookies(HP_HttpClient pClient, HP_TCookie lpCookies[], DWORD* pdwCount) +{ + return C_HP_Object::ToFirst(pClient)->GetAllCookies(lpCookies, *pdwCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetWSMessageState(HP_HttpClient pClient, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain) +{ + return C_HP_Object::ToFirst(pClient)->GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain); +} + +HPSOCKET_API void __HP_CALL HP_HttpClient_SetHttpAutoStart(HP_HttpClient pClient, BOOL bAutoStart) +{ + C_HP_Object::ToFirst(pClient)->SetHttpAutoStart(bAutoStart); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsHttpAutoStart(HP_HttpClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->IsHttpAutoStart(); +} + +/**************************************************************************/ +/************************ HTTP Sync Client 操作方法 ************************/ + +HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_OpenUrl(HP_HttpSyncClient pClient, LPCSTR lpszMethod, LPCSTR lpszUrl, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength, BOOL bForceReconnect) +{ + return C_HP_Object::ToFirst(pClient)->OpenUrl(lpszMethod, lpszUrl, lpHeaders, iHeaderCount, pBody, iLength, bForceReconnect); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_CleanupRequestResult(HP_HttpSyncClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->CleanupRequestResult(); +} + +/**************************************************************************/ +/********************** HTTP Sync Client 属性访问方法 **********************/ + +HPSOCKET_API void __HP_CALL HP_HttpSyncClient_SetConnectTimeout(HP_HttpSyncClient pClient, DWORD dwConnectTimeout) +{ + C_HP_Object::ToFirst(pClient)->SetConnectTimeout(dwConnectTimeout); +} + +HPSOCKET_API void __HP_CALL HP_HttpSyncClient_SetRequestTimeout(HP_HttpSyncClient pClient, DWORD dwRequestTimeout) +{ + C_HP_Object::ToFirst(pClient)->SetRequestTimeout(dwRequestTimeout); +} + +HPSOCKET_API DWORD __HP_CALL HP_HttpSyncClient_GetConnectTimeout(HP_HttpSyncClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetConnectTimeout(); +} + +HPSOCKET_API DWORD __HP_CALL HP_HttpSyncClient_GetRequestTimeout(HP_HttpSyncClient pClient) +{ + return C_HP_Object::ToFirst(pClient)->GetRequestTimeout(); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_GetResponseBody(HP_HttpSyncClient pClient, LPCBYTE* lpszBody, int* piLength) +{ + return C_HP_Object::ToFirst(pClient)->GetResponseBody(lpszBody, piLength); +} + +/**************************************************************************/ +/*************************** HTTP Cookie 管理方法 **************************/ + +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists) +{ + return g_CookieMgr.LoadFromFile(lpszFile, bKeepExists); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_SaveToFile(LPCSTR lpszFile, BOOL bKeepExists) +{ + return g_CookieMgr.SaveToFile(lpszFile, bKeepExists); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_ClearCookies(LPCSTR lpszDomain, LPCSTR lpszPath) +{ + return g_CookieMgr.ClearCookies(lpszDomain, lpszPath); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_RemoveExpiredCookies(LPCSTR lpszDomain, LPCSTR lpszPath) +{ + return g_CookieMgr.RemoveExpiredCookies(lpszDomain, lpszPath); +} + +HPSOCKET_API BOOL __HP_CALL 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_CALL HP_HttpCookie_MGR_DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName) +{ + return g_CookieMgr.DeleteCookie(lpszDomain, lpszPath, lpszName); +} + +HPSOCKET_API void __HP_CALL HP_HttpCookie_MGR_SetEnableThirdPartyCookie(BOOL bEnableThirdPartyCookie) +{ + g_CookieMgr.SetEnableThirdPartyCookie(bEnableThirdPartyCookie); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_IsEnableThirdPartyCookie() +{ + return g_CookieMgr.IsEnableThirdPartyCookie(); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_ParseExpires(LPCSTR lpszExpires, __time64_t* ptmExpires) +{ + return CCookie::ParseExpires(lpszExpires, *ptmExpires); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_MakeExpiresStr(char lpszBuff[], int* piBuffLen, __time64_t tmExpires) +{ + return CCookie::MakeExpiresStr(lpszBuff, *piBuffLen, tmExpires); +} + +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_ToString(char lpszBuff[], int* piBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, int enSameSite) +{ + return CCookie::ToString(lpszBuff, *piBuffLen, lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, (CCookie::EnSameSite)enSameSite); +} + +HPSOCKET_API __time64_t __HP_CALL HP_HttpCookie_HLP_CurrentUTCTime() +{ + return CCookie::CurrentUTCTime(); +} + +HPSOCKET_API __time64_t __HP_CALL HP_HttpCookie_HLP_MaxAgeToExpires(int iMaxAge) +{ + return CCookie::MaxAgeToExpires(iMaxAge); +} + +HPSOCKET_API int __HP_CALL HP_HttpCookie_HLP_ExpiresToMaxAge(__time64_t tmExpires) +{ + return CCookie::ExpiresToMaxAge(tmExpires); +} + +/*****************************************************************************************************************************************************/ +/************************************************************* HTTP Global Function Exports **********************************************************/ +/*****************************************************************************************************************************************************/ + +#endif + +/*****************************************************************************************************************************************************/ +/**************************************************************** Thread Pool Exports ****************************************************************/ +/*****************************************************************************************************************************************************/ + +/**********************************************************************************/ +/************************** Thread Pool 回调函数设置方法 ***************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnStartup(HP_ThreadPoolListener pListener, HP_FN_ThreadPool_OnStartup fn) +{ + ((C_HP_ThreadPoolListener*)pListener)->m_fnOnStartup = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnShutdown(HP_ThreadPoolListener pListener, HP_FN_ThreadPool_OnShutdown fn) +{ + ((C_HP_ThreadPoolListener*)pListener)->m_fnOnShutdown = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnWorkerThreadStart(HP_ThreadPoolListener pListener, HP_FN_ThreadPool_OnWorkerThreadStart fn) +{ + ((C_HP_ThreadPoolListener*)pListener)->m_fnOnWorkerThreadStart = fn; +} + +HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnWorkerThreadEnd(HP_ThreadPoolListener pListener, HP_FN_ThreadPool_OnWorkerThreadEnd fn) +{ + ((C_HP_ThreadPoolListener*)pListener)->m_fnOnWorkerThreadEnd = fn; +} + +/****************************************************/ +/******************* 对象创建函数 ********************/ + +HPSOCKET_API HP_ThreadPoolListener __HP_CALL Create_HP_ThreadPoolListener() +{ + return (HP_ThreadPoolListener)(new C_HP_ThreadPoolListener); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_ThreadPoolListener(HP_ThreadPoolListener pListener) +{ + delete (IHPThreadPoolListener*)pListener; +} + +HPSOCKET_API HP_ThreadPool __HP_CALL Create_HP_ThreadPool(HP_ThreadPoolListener pListener) +{ + return (HP_ThreadPool)(new CHPThreadPool((IHPThreadPoolListener*)pListener)); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_ThreadPool(HP_ThreadPool pThreadPool) +{ + delete (IHPThreadPool*)pThreadPool; +} + +HPSOCKET_API LPTSocketTask __HP_CALL Create_HP_SocketTaskObj(Fn_SocketTaskProc fnTaskProc, PVOID pSender, HP_CONNID dwConnID, LPCBYTE pBuffer, INT iBuffLen, En_HP_TaskBufferType enBuffType, WPARAM wParam, LPARAM lParam) +{ + return ::CreateSocketTaskObj(fnTaskProc, pSender, dwConnID, pBuffer, iBuffLen, enBuffType, wParam, lParam); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_SocketTaskObj(LPTSocketTask pTask) +{ + ::DestroySocketTaskObj(pTask); +} + +/***********************************************************************/ +/***************************** 组件操作方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Start(HP_ThreadPool pThreadPool, DWORD dwThreadCount, DWORD dwMaxQueueSize, En_HP_RejectedPolicy enRejectedPolicy, DWORD dwStackSize) +{ + return ((IHPThreadPool*)pThreadPool)->Start(dwThreadCount, dwMaxQueueSize, enRejectedPolicy, dwStackSize); +} + +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Stop(HP_ThreadPool pThreadPool, DWORD dwMaxWait) +{ + return ((IHPThreadPool*)pThreadPool)->Stop(dwMaxWait); +} + +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Submit(HP_ThreadPool pThreadPool, HP_Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait) +{ + return ((IHPThreadPool*)pThreadPool)->Submit(fnTaskProc, pvArg, dwMaxWait); +} + +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Submit_Task(HP_ThreadPool pThreadPool, HP_LPTSocketTask pTask, DWORD dwMaxWait) +{ + return ((IHPThreadPool*)pThreadPool)->Submit(pTask, dwMaxWait); +} + +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_AdjustThreadCount(HP_ThreadPool pThreadPool, DWORD dwNewThreadCount) +{ + return ((IHPThreadPool*)pThreadPool)->AdjustThreadCount(dwNewThreadCount); +} + +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Wait(HP_ThreadPool pThreadPool, DWORD dwMilliseconds) +{ + return ((IHPThreadPool*)pThreadPool)->Wait(dwMilliseconds); +} + +/***********************************************************************/ +/***************************** 属性访问方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_HasStarted(HP_ThreadPool pThreadPool) +{ + return ((IHPThreadPool*)pThreadPool)->HasStarted(); +} + +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_ThreadPool_GetState(HP_ThreadPool pThreadPool) +{ + return ((IHPThreadPool*)pThreadPool)->GetState(); +} + +HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetQueueSize(HP_ThreadPool pThreadPool) +{ + return ((IHPThreadPool*)pThreadPool)->GetQueueSize(); +} + +HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetTaskCount(HP_ThreadPool pThreadPool) +{ + return ((IHPThreadPool*)pThreadPool)->GetTaskCount(); +} + +HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetThreadCount(HP_ThreadPool pThreadPool) +{ + return ((IHPThreadPool*)pThreadPool)->GetThreadCount(); +} + +HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetMaxQueueSize(HP_ThreadPool pThreadPool) +{ + return ((IHPThreadPool*)pThreadPool)->GetMaxQueueSize(); +} + +HPSOCKET_API En_HP_RejectedPolicy __HP_CALL HP_ThreadPool_GetRejectedPolicy(HP_ThreadPool pThreadPool) +{ + return ((IHPThreadPool*)pThreadPool)->GetRejectedPolicy(); +} + +/*****************************************************************************************************************************************************/ +/********************************************************* Compressor / Decompressor Exports *********************************************************/ +/*****************************************************************************************************************************************************/ + +/****************************************************/ +/******************* 对象创建函数 ********************/ + +HPSOCKET_API void __HP_CALL Destroy_HP_Compressor(HP_Compressor pCompressor) +{ + ::DestroyCompressor((IHPCompressor*)pCompressor); +} + +HPSOCKET_API void __HP_CALL Destroy_HP_Decompressor(HP_Decompressor pDecompressor) +{ + ::DestroyDecompressor((IHPDecompressor*)pDecompressor); +} + +#ifdef _ZLIB_SUPPORT + +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_ZLibCompressor(HP_Fn_CompressDataCallback fnCallback) +{ + return ::CreateZLibCompressor(fnCallback); +} + +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_ZLibCompressorEx(HP_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 HP_Compressor __HP_CALL Create_HP_GZipCompressor(HP_Fn_CompressDataCallback fnCallback) +{ + return ::CreateGZipCompressor(fnCallback); +} + +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_GZipCompressorEx(HP_Fn_CompressDataCallback fnCallback, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize) +{ + return ::CreateGZipCompressor(fnCallback, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize); +} + +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_ZLibDecompressor(HP_Fn_DecompressDataCallback fnCallback) +{ + return ::CreateZLibDecompressor(fnCallback); +} + +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_ZLibDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, int iWindowBits, DWORD dwBuffSize) +{ + return ::CreateZLibDecompressor(fnCallback, iWindowBits, dwBuffSize); +} + +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_GZipDecompressor(HP_Fn_DecompressDataCallback fnCallback) +{ + return ::CreateGZipDecompressor(fnCallback); +} + +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_GZipDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize) +{ + return ::CreateGZipDecompressor(fnCallback, dwBuffSize); +} + +#endif + +#ifdef _BROTLI_SUPPORT + +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_BrotliCompressor(HP_Fn_CompressDataCallback fnCallback) +{ + return ::CreateBrotliCompressor(fnCallback); +} + +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_BrotliCompressorEx(HP_Fn_CompressDataCallback fnCallback, int iQuality, int iWindow, int iMode, DWORD dwBuffSize) +{ + return ::CreateBrotliCompressor(fnCallback, iQuality, iWindow, iMode, dwBuffSize); +} + +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_BrotliDecompressor(HP_Fn_DecompressDataCallback fnCallback) +{ + return ::CreateBrotliDecompressor(fnCallback); +} + +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_BrotliDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize) +{ + return ::CreateBrotliDecompressor(fnCallback, dwBuffSize); +} + +#endif + +/***********************************************************************/ +/***************************** 组件操作方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_Compressor_Process(HP_Compressor pCompressor, const BYTE* pData, int iLength, BOOL bLast, PVOID pContext) +{ + return ((IHPCompressor*)pCompressor)->Process(pData, iLength, bLast, pContext); +} + +HPSOCKET_API BOOL __HP_CALL HP_Compressor_ProcessEx(HP_Compressor pCompressor, const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush, PVOID pContext) +{ + return ((IHPCompressor*)pCompressor)->ProcessEx(pData, iLength, bLast, bFlush, pContext); +} + +HPSOCKET_API BOOL __HP_CALL HP_Compressor_Reset(HP_Compressor pCompressor) +{ + return ((IHPCompressor*)pCompressor)->Reset(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Decompressor_Process(HP_Decompressor pDecompressor, const BYTE* pData, int iLength, PVOID pContext) +{ + return ((IHPDecompressor*)pDecompressor)->Process(pData, iLength, pContext); +} + +HPSOCKET_API BOOL __HP_CALL HP_Decompressor_Reset(HP_Decompressor pDecompressor) +{ + return ((IHPDecompressor*)pDecompressor)->Reset(); +} + +/***********************************************************************/ +/***************************** 属性访问方法 *****************************/ + +HPSOCKET_API BOOL __HP_CALL HP_Compressor_IsValid(HP_Compressor pCompressor) +{ + return ((IHPCompressor*)pCompressor)->IsValid(); +} + +HPSOCKET_API BOOL __HP_CALL HP_Decompressor_IsValid(HP_Decompressor pDecompressor) +{ + return ((IHPDecompressor*)pDecompressor)->IsValid(); +} diff --git a/HPThreadPool.cpp b/HPThreadPool.cpp new file mode 100644 index 0000000..0cbed1a --- /dev/null +++ b/HPThreadPool.cpp @@ -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 + +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 pThreadAttr; + + if(m_dwStackSize != 0) + { + pThreadAttr = make_unique(); + 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); +} diff --git a/HPThreadPool.h b/HPThreadPool.h new file mode 100644 index 0000000..60b6165 --- /dev/null +++ b/HPThreadPool.h @@ -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; + +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 m_stThreads; + + DECLARE_NO_COPY_CLASS(CHPThreadPool) +}; diff --git a/HttpAgent.cpp b/HttpAgent.cpp new file mode 100644 index 0000000..a3daf33 --- /dev/null +++ b/HttpAgent.cpp @@ -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 BOOL CHttpAgentT::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 void CHttpAgentT::PrepareStart() +{ + __super::PrepareStart(); + + m_objPool.SetHttpObjLockTime(GetFreeSocketObjLockTime()); + m_objPool.SetHttpObjPoolSize(GetFreeSocketObjPool()); + m_objPool.SetHttpObjPoolHold(GetFreeSocketObjHold()); + + m_objPool.Prepare(); +} + +template BOOL CHttpAgentT::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 BOOL CHttpAgentT::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 BOOL CHttpAgentT::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 BOOL CHttpAgentT::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 szData = make_unique(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 EnHandleResult CHttpAgentT::FireConnect(TAgentSocketObj* pSocketObj) +{ + return m_bHttpAutoStart ? __super::FireConnect(pSocketObj) : __super::DoFireConnect(pSocketObj); +} + +template EnHandleResult CHttpAgentT::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 EnHandleResult CHttpAgentT::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 EnHandleResult CHttpAgentT::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 EnHandleResult CHttpAgentT::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 EnHandleResult CHttpAgentT::DoFireShutdown() +{ + EnHandleResult result = __super::DoFireShutdown(); + + m_objPool.Clear(); + + return result; +} + +template void CHttpAgentT::ReleaseGCSocketObj(BOOL bForce) +{ + __super::ReleaseGCSocketObj(bForce); + +#ifdef USE_EXTERNAL_GC + m_objPool.ReleaseGCHttpObj(bForce); +#endif +} + +template BOOL CHttpAgentT::IsUpgrade(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->IsUpgrade(); +} + +template BOOL CHttpAgentT::IsKeepAlive(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->IsKeepAlive(); +} + +template USHORT CHttpAgentT::GetVersion(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return 0; + + return pHttpObj->GetVersion(); +} + +template ULONGLONG CHttpAgentT::GetContentLength(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return 0; + + return pHttpObj->GetContentLength(); +} + +template LPCSTR CHttpAgentT::GetContentType(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetContentType(); +} + +template LPCSTR CHttpAgentT::GetContentEncoding(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetContentEncoding(); +} + +template LPCSTR CHttpAgentT::GetTransferEncoding(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetTransferEncoding(); +} + +template EnHttpUpgradeType CHttpAgentT::GetUpgradeType(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return HUT_NONE; + + return pHttpObj->GetUpgradeType(); +} + +template USHORT CHttpAgentT::GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return 0; + + return pHttpObj->GetParseErrorCode(lpszErrorDesc); +} + +template BOOL CHttpAgentT::GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetHeader(lpszName, lpszValue); +} + +template BOOL CHttpAgentT::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 BOOL CHttpAgentT::GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetAllHeaders(lpHeaders, dwCount); +} + +template BOOL CHttpAgentT::GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetAllHeaderNames(lpszName, dwCount); +} + +template BOOL CHttpAgentT::GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetCookie(lpszName, lpszValue); +} + +template BOOL CHttpAgentT::GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetAllCookies(lpCookies, dwCount); +} + +template USHORT CHttpAgentT::GetStatusCode(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return 0; + + return pHttpObj->GetStatusCode(); +} + +template BOOL CHttpAgentT::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 inline typename CHttpAgentT::THttpObj* CHttpAgentT::FindHttpObj(CONNID dwConnID) +{ + THttpObj* pHttpObj = nullptr; + GetConnectionReserved(dwConnID, (PVOID*)&pHttpObj); + + return pHttpObj; +} + +template inline typename CHttpAgentT::THttpObj* CHttpAgentT::FindHttpObj(TAgentSocketObj* pSocketObj) +{ + THttpObj* pHttpObj = nullptr; + GetConnectionReserved(pSocketObj, (PVOID*)&pHttpObj); + + return pHttpObj; +} + +template BOOL CHttpAgentT::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 BOOL CHttpAgentT::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 typename CHttpAgentT::THttpObj* CHttpAgentT::DoStartHttp(TAgentSocketObj* pSocketObj) +{ + THttpObj* pHttpObj = m_objPool.PickFreeHttpObj(this, pSocketObj); + VERIFY(SetConnectionReserved(pSocketObj, pHttpObj)); + + return pHttpObj; +} + +// ------------------------------------------------------------------------------------------------------------- // + +template class CHttpAgentT; + +#ifdef _SSL_SUPPORT + +#include "SSLAgent.h" + +template class CHttpAgentT; + +#endif + +#endif \ No newline at end of file diff --git a/HttpAgent.h b/HttpAgent.h new file mode 100644 index 0000000..ab79bca --- /dev/null +++ b/HttpAgent.h @@ -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 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; + using THttpObj = THttpObjT; + + 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 CHttpAgent; + +#ifdef _SSL_SUPPORT + +#include "SSLAgent.h" + +typedef CHttpAgentT CHttpsAgent; + +#endif + +#endif \ No newline at end of file diff --git a/HttpClient.cpp b/HttpClient.cpp new file mode 100644 index 0000000..0da8ce6 --- /dev/null +++ b/HttpClient.cpp @@ -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 BOOL CHttpClientT::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 BOOL CHttpClientT::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 BOOL CHttpClientT::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 BOOL CHttpClientT::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 BOOL CHttpClientT::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 szData = make_unique(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 BOOL CHttpClientT::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 BOOL CHttpSyncClientT::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 BOOL CHttpSyncClientT::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 BOOL CHttpSyncClientT::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 BOOL CHttpSyncClientT::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 BOOL CHttpSyncClientT::CleanupRequestResult() +{ + m_pHttpObj = &m_objHttp; + m_enProgress = HSRP_WAITING; + + m_szBuffer.Free(); + m_objHttp2.Reset(); + m_evWait.Reset(); + + return TRUE; +} + +template void CHttpSyncClientT::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 BOOL CHttpSyncClientT::WaitForEvent(DWORD dwWait) +{ + LONG lTimeout = INFINITE; + + if(dwWait != 0) + lTimeout = (LONG)dwWait; + + return (m_evWait.Wait(lTimeout) > TIMEOUT); +} + +template BOOL CHttpSyncClientT::GetResponseBody(LPCBYTE* lpszBody, int* iLength) +{ + ASSERT(lpszBody && iLength); + + *lpszBody = m_szBuffer.Ptr(); + *iLength = (int)m_szBuffer.Size(); + + return TRUE; +} + +template EnHandleResult CHttpSyncClientT::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 EnHandleResult CHttpSyncClientT::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 EnHttpParseResult CHttpSyncClientT::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 EnHttpParseResult CHttpSyncClientT::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 EnHttpParseResult CHttpSyncClientT::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 EnHttpParseResult CHttpSyncClientT::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 EnHandleResult CHttpSyncClientT::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 EnHandleResult CHttpSyncClientT::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 EnHandleResult CHttpSyncClientT::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 EnHandleResult CHttpSyncClientT::OnConnect(ITcpClient* pSender, CONNID dwConnID) +{ + EnHandleResult rs = HR_OK; + + if(m_pListener2 != nullptr) + return m_pListener2->OnConnect(pSender, dwConnID); + + return rs; +} + +template EnHandleResult CHttpSyncClientT::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 EnHandleResult CHttpSyncClientT::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 EnHttpParseResult CHttpSyncClientT::OnMessageBegin(IHttpClient* pSender, CONNID dwConnID) +{ + EnHttpParseResult rs = HPR_OK; + + if(m_pListener2 != nullptr) + rs = m_pListener2->OnMessageBegin(pSender, dwConnID); + + return rs; +} + +template EnHttpParseResult CHttpSyncClientT::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 EnHttpParseResult CHttpSyncClientT::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 EnHttpParseResult CHttpSyncClientT::OnHeadersComplete(IHttpClient* pSender, CONNID dwConnID) +{ + EnHttpParseResult rs = HPR_OK; + + if(m_pListener2 != nullptr) + rs = m_pListener2->OnHeadersComplete(pSender, dwConnID); + + return rs; +} + +template EnHttpParseResult CHttpSyncClientT::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 EnHttpParseResult CHttpSyncClientT::OnChunkComplete(IHttpClient* pSender, CONNID dwConnID) +{ + EnHttpParseResult rs = HPR_OK; + + if(m_pListener2 != nullptr) + rs = m_pListener2->OnChunkComplete(pSender, dwConnID); + + return rs; +} + +template EnHandleResult CHttpSyncClientT::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; +template class CHttpSyncClientT; + +#ifdef _SSL_SUPPORT + +#include "SSLClient.h" + +template class CHttpClientT; +template class CHttpSyncClientT; + +#endif + +#endif \ No newline at end of file diff --git a/HttpClient.h b/HttpClient.h new file mode 100644 index 0000000..9cb12a7 --- /dev/null +++ b/HttpClient.h @@ -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 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; + 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 CHttpSyncClientT : public CHttpClientT, private CHttpClientListener +{ + using __super = CHttpClientT; + 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 m_szBuffer; +}; + +// ------------------------------------------------------------------------------------------------------------- // + +typedef CHttpClientT CHttpClient; +typedef CHttpSyncClientT CHttpSyncClient; + +#ifdef _SSL_SUPPORT + +#include "SSLClient.h" + +typedef CHttpClientT CHttpsClient; +typedef CHttpSyncClientT CHttpsSyncClient; + +#endif + +#endif \ No newline at end of file diff --git a/HttpCookie.cpp b/HttpCookie.cpp new file mode 100644 index 0000000..4b6cf7b --- /dev/null +++ b/HttpCookie.cpp @@ -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 lsDomains(1, lpszDomain); + list 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::const_iterator it = lsDomains.begin(), end = lsDomains.end(); it != end; ++it) + { + for(list::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 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 \ No newline at end of file diff --git a/HttpCookie.h b/HttpCookie.h new file mode 100644 index 0000000..681be36 --- /dev/null +++ b/HttpCookie.h @@ -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 CCookiePtrSet; +typedef CCookiePtrSet::const_iterator CCookiePtrSetCI; +typedef CCookiePtrSet::iterator CCookiePtrSetI; + +typedef unordered_set CCookieSet; +typedef CCookieSet::const_iterator CCookieSetCI; +typedef CCookieSet::iterator CCookieSetI; + +typedef unordered_map CCookiePathMap; +typedef CCookiePathMap::const_iterator CCookiePathMapCI; +typedef CCookiePathMap::iterator CCookiePathMapI; + +typedef unordered_map 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 \ No newline at end of file diff --git a/HttpHelper.cpp b/HttpHelper.cpp new file mode 100644 index 0000000..9787494 --- /dev/null +++ b/HttpHelper.cpp @@ -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 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 \ No newline at end of file diff --git a/HttpHelper.h b/HttpHelper.h new file mode 100644 index 0000000..c4d375f --- /dev/null +++ b/HttpHelper.h @@ -0,0 +1,1385 @@ +/* + * 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 "HttpCookie.h" + +#ifdef _HTTP_SUPPORT + +#include "./common/http/llhttp.h" +#include "./common/http/llhttp_url.h" + +typedef llhttp_t http_parser; +typedef llhttp_settings_t http_parser_settings; + +#define LLHTTP_MAX_ERROR_NUM 23 +#define LLHTTP_MAX_METHOD_NUM 45 + + +/************************************************************************ +名称:HTTP 全局常量 +描述:声明 HTTP 组件的公共全局常量 +************************************************************************/ + +#define HTTP_DEFAULT_PORT 80 +#define HTTPS_DEFAULT_PORT 443 + +#define HTTP_SCHEMA "http://" +#define HTTPS_SCHEMA "https://" + +#define HTTP_CRLF "\r\n" +#define HTTP_PATH_SEPARATOR_CHAR '/' +#define HTTP_PATH_SEPARATOR "/" +#define HTTP_HEADER_SEPARATOR ": " +#define HTTP_COOKIE_SEPARATOR "; " +#define HTTP_1_0_STR "HTTP/1.0" +#define HTTP_1_1_STR "HTTP/1.1" + +#define HTTP_HEADER_HOST "Host" +#define HTTP_HEADER_COOKIE "Cookie" +#define HTTP_HEADER_SET_COOKIE "Set-Cookie" +#define HTTP_HEADER_CONTENT_TYPE "Content-Type" +#define HTTP_HEADER_CONTENT_LENGTH "Content-Length" +#define HTTP_HEADER_CONTENT_ENCODING "Content-Encoding" +#define HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding" +#define HTTP_HEADER_CONNECTION "Connection" +#define HTTP_HEADER_UPGRADE "Upgrade" +#define HTTP_HEADER_VALUE_WEB_SOCKET "WebSocket" + +#define HTTP_CONNECTION_CLOSE_VALUE "close" +#define HTTP_CONNECTION_KEEPALIVE_VALUE "keep-alive" + +#define HTTP_METHOD_POST "POST" +#define HTTP_METHOD_PUT "PUT" +#define HTTP_METHOD_PATCH "PATCH" +#define HTTP_METHOD_GET "GET" +#define HTTP_METHOD_DELETE "DELETE" +#define HTTP_METHOD_HEAD "HEAD" +#define HTTP_METHOD_TRACE "TRACE" +#define HTTP_METHOD_OPTIONS "OPTIONS" +#define HTTP_METHOD_CONNECT "CONNECT" + +#define HTTP_MIN_WS_HEADER_LEN 2 +#define HTTP_MAX_WS_HEADER_LEN 14 + +#define MIN_HTTP_RELEASE_CHECK_INTERVAL ((DWORD)1000) +#define MIN_HTTP_RELEASE_DELAY 100 +#define MAX_HTTP_RELEASE_DELAY (60 * 1000) +#define DEFAULT_HTTP_RELEASE_DELAY (5 * 1000) +#define DEFAULT_HTTP_VERSION HV_1_1 + +#define DEFAULT_HTTP_SYNC_CONNECT_TIMEOUT 5000 +#define DEFAULT_HTTP_SYNC_REQUEST_TIMEOUT 10000 + +// ------------------------------------------------------------------------------------------------------------- // + +enum EnHttpSyncRequestProgress +{ + HSRP_DONE, + HSRP_WAITING, + HSRP_ERROR, + HSRP_CLOSE +}; + +struct TDyingConnection +{ + CONNID connID; + DWORD killTime; + + TDyingConnection(CONNID id, DWORD kt = 0) + : connID (id) + , killTime (kt == 0 ? ::TimeGetTime() : kt) + { + + } + + static TDyingConnection* Construct(CONNID id, DWORD kt = 0) {return new TDyingConnection(id, kt);} + static void Destruct(TDyingConnection* pObj) {if(pObj) delete pObj;} + +}; + +typedef unordered_multimap THeaderMap; +typedef THeaderMap::const_iterator THeaderMapCI; +typedef THeaderMap::iterator THeaderMapI; + +typedef unordered_map TCookieMap; +typedef TCookieMap::const_iterator TCookieMapCI; +typedef TCookieMap::iterator TCookieMapI; + +// ------------------------------------------------------------------------------------------------------------- // + +struct TBaseWSHeader +{ +public: + BOOL fin() + { + return (data >> 7) & 0x1; + } + + void set_fin(BOOL v) + { + data |= ((v ? 1 : 0) << 7); + } + + BYTE rsv() + { + return (data >> 4) & 0x7; + } + + void set_rsv(BYTE v) + { + data |= ((v & 0x7) << 4); + } + + BYTE code() + { + return data & 0xF; + } + + void set_code(BYTE v) + { + data |= (v & 0xF); + } + + BOOL mask() + { + return (data >> 15) & 0x1; + } + + void set_mask(BOOL v) + { + data |= ((v ? 1 : 0) << 15); + } + + BYTE len() + { + return (data >> 8) & 0x7F; + } + + void set_len(BYTE v) + { + data |= ((v & 0x7F) << 8); + } + + USHORT extlen() + { + return ntohs((USHORT)(data >> 16)); + } + + void set_extlen(USHORT v) + { + data |= (htons(v) << 16); + } + + TBaseWSHeader(const BYTE* p, BOOL bZero = FALSE) + : data(*(UINT*)p) + { + if(bZero) data = 0; + } + +private: + UINT& data; +}; + +template struct TWSContext +{ +public: + EnHandleResult Parse(const BYTE* pData, int iLength) + { + ASSERT(pData != nullptr && iLength > 0); + + EnHandleResult hr = HR_OK; + BYTE* pTemp = (BYTE*)pData; + int iRemain = iLength; + int iMin = 0; + + while(iRemain > 0) + { + if(m_bHeader) + { + iMin = MIN(m_iHeaderRemain, iRemain); + memcpy(m_szHeader + m_iHeaderLen - m_iHeaderRemain, pTemp, iMin); + + m_iHeaderRemain -= iMin; + + if(m_iHeaderRemain == 0) + { + TBaseWSHeader bh(m_szHeader); + + int iLen = bh.len(); + int iExtLen = iLen < 126 ? 0 : (iLen == 126 ? 2 : 8); + int iMaskLen = bh.mask() ? 4 : 0; + int iRealHeaderLen = HTTP_MIN_WS_HEADER_LEN + iExtLen + iMaskLen; + + if(m_iHeaderLen < iRealHeaderLen) + { + m_iHeaderRemain = iRealHeaderLen - m_iHeaderLen; + m_iHeaderLen = iRealHeaderLen; + } + else + { + if((m_pHttpObj->IsRequest() && iMaskLen == 0) || (!m_pHttpObj->IsRequest() && iMaskLen > 0)) + { + ::SetLastError(ERROR_INVALID_DATA); + hr = HR_ERROR; + + break; + } + + m_ullBodyLen = iExtLen == 0 ? iLen : (iExtLen == 2 ? bh.extlen() : NToH64(*(ULONGLONG*)(m_szHeader + HTTP_MIN_WS_HEADER_LEN))); + m_ullBodyRemain = m_ullBodyLen; + m_lpszMask = iMaskLen > 0 ? m_szHeader + HTTP_MIN_WS_HEADER_LEN + iExtLen : nullptr; + + hr = m_pHttpObj->on_ws_message_header(bh.fin(), bh.rsv(), bh.code(), m_lpszMask, m_ullBodyLen); + + if(hr == HR_ERROR) + break; + + if(m_ullBodyLen > 0) + m_bHeader = FALSE; + else + { + hr = CompleteMessage(); + + if(hr == HR_ERROR) + break; + } + } + } + } + else + { + iMin = (int)MIN(m_ullBodyRemain, (ULONGLONG)iRemain); + + if(m_lpszMask) + { + int iFactor = (m_ullBodyLen - m_ullBodyRemain) & 0x03; + + for(int i = 0; i < iMin; i++) + pTemp[i] = pTemp[i] ^ m_lpszMask[(i + iFactor) & 0x03]; + } + + m_ullBodyRemain -= iMin; + + hr = m_pHttpObj->on_ws_message_body(pTemp, iMin); + + if(hr == HR_ERROR) + break; + + if(m_ullBodyRemain == 0) + { + hr = CompleteMessage(); + + if(hr == HR_ERROR) + break; + } + } + + pTemp += iMin; + iRemain -= iMin; + } + + return hr; + } + + BOOL GetMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain) + { + TBaseWSHeader bh(m_szHeader); + + if(lpbFinal) *lpbFinal = bh.fin(); + if(lpiReserved) *lpiReserved = bh.rsv(); + if(lpiOperationCode) *lpiOperationCode = bh.code(); + if(lpszMask) *lpszMask = m_lpszMask; + if(lpullBodyLen) *lpullBodyLen = m_ullBodyLen; + if(lpullBodyRemain) *lpullBodyRemain = m_ullBodyRemain; + + return TRUE; + } + + BOOL CopyData(const TWSContext& src) + { + if(&src == this) + return FALSE; + + memcpy(m_szHeader, src.m_szHeader, HTTP_MAX_WS_HEADER_LEN); + + if(src.m_lpszMask) + m_lpszMask = m_szHeader + (src.m_lpszMask - src.m_szHeader); + else + m_lpszMask = nullptr; + + m_ullBodyLen = src.m_ullBodyLen; + m_ullBodyRemain = src.m_ullBodyRemain; + + return TRUE; + } + +public: + TWSContext(T* pHttpObj) : m_pHttpObj(pHttpObj) + { + Reset(); + } + +private: + EnHandleResult CompleteMessage() + { + EnHandleResult hr = m_pHttpObj->on_ws_message_complete(); + + Reset(); + + return hr; + } + + void Reset() + { + m_bHeader = TRUE; + m_lpszMask = nullptr; + m_iHeaderLen = HTTP_MIN_WS_HEADER_LEN; + m_iHeaderRemain = HTTP_MIN_WS_HEADER_LEN; + m_ullBodyLen = 0; + m_ullBodyRemain = 0; + } + +private: + T* m_pHttpObj; + + BYTE m_szHeader[HTTP_MAX_WS_HEADER_LEN]; + const BYTE* m_lpszMask; + + BOOL m_bHeader; + int m_iHeaderLen; + int m_iHeaderRemain; + ULONGLONG m_ullBodyLen; + ULONGLONG m_ullBodyRemain; +}; + +// ------------------------------------------------------------------------------------------------------------- // + +/* Http 上下文结构 */ +template struct THttpObjT +{ +public: + EnHandleResult Execute(const BYTE* pData, int iLength) + { + ASSERT(pData != nullptr && iLength > 0); + + if(m_parser.upgrade) + { + if(m_enUpgrade == HUT_WEB_SOCKET) + { + if(m_pwsContext != nullptr) + return m_pwsContext->Parse(pData, iLength); + } + else + { + return m_pContext->DoFireSuperReceive(m_pSocket, pData, iLength); + } + } + + EnHandleResult hr = HR_OK; + llhttp_errno rs = ::llhttp_execute(&m_parser, (LPCSTR)pData, iLength); + + if(rs == HPE_OK) + ASSERT(m_parser.error_pos == nullptr); + else if(rs == HPE_PAUSED_UPGRADE) + { + int iPased = (m_parser.error_pos == nullptr) ? iLength : (int)((const BYTE*)(m_parser.error_pos) - pData); + hr = Upgrade(pData, iLength, iPased); + } + else + { + m_pContext->FireParseError(m_pSocket, m_parser.error, ::llhttp_get_error_reason(&m_parser)); + hr = HR_ERROR; + } + + return hr; + } + + void CheckBodyIdentityEof() + { + if(!m_parser.upgrade) + ::llhttp_finish(&m_parser); + } + + static int on_message_begin(http_parser* p) + { + THttpObjT* pSelf = Self(p); + + pSelf->ResetHeaderState(FALSE, FALSE); + + return pSelf->m_pContext->FireMessageBegin(pSelf->m_pSocket); + } + + static int on_url(http_parser* p, const char* at, size_t length) + { + Self(p)->AppendBuffer(at, length); + + return HPR_OK; + } + + static int on_url_complete(llhttp_t* p) + { + THttpObjT* pSelf = Self(p); + EnHttpParseResult hpr = pSelf->ParseUrl(); + + if(hpr == HPR_OK) + hpr = pSelf->m_pContext->FireRequestLine(pSelf->m_pSocket, ::llhttp_method_name((llhttp_method_t)p->method), pSelf->GetBuffer()); + + pSelf->ResetBuffer(); + + return hpr; + } + + static int on_status(http_parser* p, const char* at, size_t length) + { + Self(p)->AppendBuffer(at, length); + + return HPR_OK; + } + + static int on_status_complete(llhttp_t* p) + { + THttpObjT* pSelf = Self(p); + EnHttpParseResult hpr = pSelf->m_pContext->FireStatusLine(pSelf->m_pSocket, p->status_code, pSelf->GetBuffer()); + + pSelf->ResetBuffer(); + + return hpr; + } + + static int on_header_field(http_parser* p, const char* at, size_t length) + { + Self(p)->AppendBuffer(at, length); + + return HPR_OK; + } + + static int on_header_field_complete(llhttp_t* p) + { + THttpObjT* pSelf = Self(p); + pSelf->m_strCurHeader = pSelf->GetBuffer(); + pSelf->ResetBuffer(); + + return HPR_OK; + } + + static int on_header_value(http_parser* p, const char* at, size_t length) + { + Self(p)->AppendBuffer(at, length); + + return HPR_OK; + } + + static int on_header_value_complete(llhttp_t* p) + { + THttpObjT* pSelf = Self(p); + + pSelf->m_headers.emplace(move(THeaderMap::value_type(pSelf->m_strCurHeader, pSelf->GetBuffer()))); + + EnHttpParseResult hpr = pSelf->m_pContext->FireHeader(pSelf->m_pSocket, pSelf->m_strCurHeader, pSelf->GetBuffer()); + + if(hpr != HPR_ERROR && !pSelf->GetBufferRef().IsEmpty()) + { + if(pSelf->m_bRequest && pSelf->m_strCurHeader.CompareNoCase(HTTP_HEADER_COOKIE) == 0) + hpr = pSelf->ParseCookie(); + else if(!pSelf->m_bRequest && pSelf->m_strCurHeader.CompareNoCase(HTTP_HEADER_SET_COOKIE) == 0) + hpr = pSelf->ParseSetCookie(); + } + + pSelf->ResetBuffer(); + + return hpr; + } + + static int on_headers_complete(http_parser* p) + { + THttpObjT* pSelf = Self(p); + + pSelf->CheckUpgrade(); + pSelf->ResetHeaderBuffer(); + + EnHttpParseResult rs = pSelf->m_pContext->FireHeadersComplete(pSelf->m_pSocket); + + if(!pSelf->m_bRequest && pSelf->GetMethodInt() == HTTP_HEAD && rs == HPR_OK) + rs = HPR_SKIP_BODY; + + return rs; + } + + static int on_body(http_parser* p, const char* at, size_t length) + { + THttpObjT* pSelf = Self(p); + + return pSelf->m_pContext->FireBody(pSelf->m_pSocket, (const BYTE*)at, (int)length); + } + + static int on_chunk_header(http_parser* p) + { + THttpObjT* pSelf = Self(p); + + return pSelf->m_pContext->FireChunkHeader(pSelf->m_pSocket, (int)p->content_length); + } + + static int on_chunk_complete(http_parser* p) + { + THttpObjT* pSelf = Self(p); + + return pSelf->m_pContext->FireChunkComplete(pSelf->m_pSocket); + } + + static int on_message_complete(http_parser* p) + { + THttpObjT* pSelf = Self(p); + + return pSelf->m_pContext->FireMessageComplete(pSelf->m_pSocket); + } + + EnHandleResult on_ws_message_header(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen) + { + return m_pContext->FireWSMessageHeader(m_pSocket, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen); + } + + EnHandleResult on_ws_message_body(const BYTE* pData, int iLength) + { + return m_pContext->FireWSMessageBody(m_pSocket, pData, iLength); + } + + EnHandleResult on_ws_message_complete() + { + return m_pContext->FireWSMessageComplete(m_pSocket); + } + +private: + + EnHandleResult Upgrade(const BYTE* pData, int iLength, int iPased) + { + ASSERT(m_parser.upgrade); + + if(m_pContext->FireUpgrade(m_pSocket, m_enUpgrade) != HPR_OK) + return HR_ERROR; + + ResetHeaderState(); + + if(m_enUpgrade == HUT_WEB_SOCKET) + m_pwsContext = new TWSContext>(this); + + if(iPased < iLength) + return Execute(pData + iPased, iLength - iPased); + + return HR_OK; + } + + void CheckUpgrade() + { + if(!m_parser.upgrade) + return; + + if(m_bRequest && m_parser.method == HTTP_CONNECT) + m_enUpgrade = HUT_HTTP_TUNNEL; + else + { + LPCSTR lpszValue; + if(GetHeader(HTTP_HEADER_UPGRADE, &lpszValue) && stricmp(HTTP_HEADER_VALUE_WEB_SOCKET, lpszValue) == 0) + m_enUpgrade = HUT_WEB_SOCKET; + else + m_enUpgrade = HUT_UNKNOWN; + } + } + + EnHttpParseResult ParseUrl() + { + http_parser_url url = {0}; + + BOOL isConnect = m_parser.method == HTTP_CONNECT; + int rs = ::http_parser_parse_url(m_strBuffer, m_strBuffer.GetLength(), isConnect, &url); + + if(rs != HPE_OK) + { + /* + m_parser.error = HPE_INVALID_URL; + m_parser.reason = "Parse url fail"; + */ + + return HPR_ERROR; + } + + m_usUrlFieldSet = url.field_set; + LPCSTR lpszBuffer = m_strBuffer; + + for(int i = 0; i < UF_MAX; i++) + { + if((url.field_set & (1 << i)) != 0) + m_pstrUrlFileds[i].SetString((lpszBuffer + url.field_data[i].off), url.field_data[i].len); + } + + return HPR_OK; + } + + EnHttpParseResult ParseCookie() + { + int i = 0; + + do + { + CStringA tk = m_strBuffer.Tokenize(COOKIE_FIELD_SEP, i); + + if(i == -1) + break; + + int j = tk.Trim().Find(COOKIE_KV_SEP_CHAR); + + if(j <= 0) + continue; + /* + { + m_parser.http_errno = HPE_INVALID_HEADER_TOKEN; + return HPR_ERROR; + } + */ + + AddCookie(tk.Left(j), tk.Mid(j + 1)); + + } while(TRUE); + + return HPR_OK; + } + + EnHttpParseResult ParseSetCookie() + { + CCookieMgr* pCookieMgr = m_pContext->GetCookieMgr(); + + if(pCookieMgr == nullptr) + return HPR_OK; + + LPCSTR lpszDomain = GetDomain(); + LPCSTR lpszPath = GetPath(); + + unique_ptr pCookie(CCookie::FromString(m_strBuffer, lpszDomain, lpszPath)); + + if(pCookie == nullptr) + return HPR_ERROR; + + if(pCookie->Match(lpszDomain, lpszPath, TRUE, m_pContext->IsSecure())) + { + if(pCookie->IsExpired()) + DeleteCookie(pCookie->name); + else + AddCookie(pCookie->name, pCookie->value); + } + + if(pCookieMgr->IsEnableThirdPartyCookie() || pCookie->IsSameDomain(lpszDomain)) + pCookieMgr->SetCookie(*pCookie); + + return HPR_OK; + } + +public: + int GetCount() const {return 0;} + DWORD GetFreeTime() const {return m_dwFreeTime;} + void SetFree() {m_dwFreeTime = ::TimeGetTime();} + + BOOL IsRequest() {return m_bRequest;} + BOOL IsUpgrade() {return m_parser.upgrade;} + BOOL IsKeepAlive() {return ::llhttp_should_keep_alive(&m_parser);} + USHORT GetVersion() {return MAKEWORD(m_parser.http_major, m_parser.http_minor);} + ULONGLONG GetContentLength() {return m_parser.content_length;} + + int GetMethodInt() {return m_bRequest ? m_parser.method : m_sRequestMethod;} + USHORT GetUrlFieldSet() {return m_usUrlFieldSet;} + USHORT GetStatusCode() {return m_parser.status_code;} + + EnHttpUpgradeType GetUpgradeType() {return m_enUpgrade;} + + THeaderMap& GetHeaderMap() {return m_headers;} + TCookieMap& GetCookieMap() {return m_cookies;} + + BOOL HasReleased() {return m_bReleased;} + void Release() {m_bReleased = TRUE;} + + LPCSTR GetMethod() + { + int iMethod = GetMethodInt(); + + if(iMethod >= 0 && iMethod <= LLHTTP_MAX_METHOD_NUM) + return ::llhttp_method_name((llhttp_method)iMethod); + + return nullptr; + } + + LPCSTR GetContentType() + { + LPCSTR lpszValue = nullptr; + GetHeader(HTTP_HEADER_CONTENT_TYPE, &lpszValue); + + return lpszValue; + } + + LPCSTR GetContentEncoding() + { + LPCSTR lpszValue = nullptr; + GetHeader(HTTP_HEADER_CONTENT_ENCODING, &lpszValue); + + return lpszValue; + } + + LPCSTR GetTransferEncoding() + { + LPCSTR lpszValue = nullptr; + GetHeader(HTTP_HEADER_TRANSFER_ENCODING, &lpszValue); + + return lpszValue; + } + + LPCSTR GetHost() + { + LPCSTR lpszValue = nullptr; + GetHeader(HTTP_HEADER_HOST, &lpszValue); + + return lpszValue; + } + + USHORT GetParseErrorCode(LPCSTR* lpszErrorDesc = nullptr) + { + USHORT usError = (USHORT)m_parser.error; + + if(lpszErrorDesc) + { + if(usError == HPE_OK) + *lpszErrorDesc = ::llhttp_errno_name((llhttp_errno_t)usError); + else + { + *lpszErrorDesc = ::llhttp_get_error_reason(&m_parser); + + if(::IsStrEmptyA(*lpszErrorDesc) && usError <= LLHTTP_MAX_ERROR_NUM) + *lpszErrorDesc = ::llhttp_errno_name((llhttp_errno_t)usError); + } + } + + return usError; + } + + LPCSTR GetUrlField(EnHttpUrlField enField) + { + ASSERT(m_bRequest && enField < HUF_MAX); + + if(!m_bRequest || enField >= HUF_MAX) + return nullptr; + + return m_pstrUrlFileds[enField]; + } + + LPCSTR GetPath() + { + if(m_bRequest) + return GetUrlField(HUF_PATH); + else + return *m_pstrRequestPath; + } + + LPCSTR GetDomain() + { + ASSERT(!m_bRequest); + + return m_pContext->GetRemoteDomain(m_pSocket); + } + + LPCSTR GetRequestPath() + { + if(m_bRequest) + return nullptr; + + return *m_pstrRequestPath; + } + + void SetRequestPath(LPCSTR lpszMethod, LPCSTR lpszPath) + { + ASSERT(!m_bRequest); + + if(m_bRequest) + return; + + *m_pstrRequestPath = lpszPath; + +#define HTTP_METHOD_GEN(NUM, NAME, STRING) else if(stricmp(lpszMethod, #STRING) == 0) m_sRequestMethod = HTTP_##NAME; + + if(::IsStrEmptyA(lpszMethod)) m_sRequestMethod = -1; + HTTP_METHOD_MAP(HTTP_METHOD_GEN) + else + m_sRequestMethod = -1; + +#undef HTTP_METHOD_GEN + } + + BOOL GetHeader(LPCSTR lpszName, LPCSTR* lpszValue) + { + ASSERT(lpszName); + + BOOL isOK = FALSE; + THeaderMapCI it = m_headers.find(lpszName); + + if(it != m_headers.end()) + { + *lpszValue = it->second; + isOK = TRUE; + } + + return isOK; + } + + BOOL GetHeaders(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount) + { + ASSERT(lpszName); + + if(lpszValue == nullptr || dwCount == 0) + { + dwCount = (DWORD)m_headers.count(lpszName); + return FALSE; + } + + pair range = m_headers.equal_range(lpszName); + + THeaderMapCI it = range.first; + DWORD dwIndex = 0; + + while(it != range.second) + { + if(dwIndex < dwCount) + lpszValue[dwIndex] = it->second; + + ++dwIndex; + ++it; + } + + BOOL isOK = (dwIndex > 0 && dwIndex <= dwCount); + dwCount = dwIndex; + + return isOK; + } + + BOOL GetAllHeaders(THeader lpHeaders[], DWORD& dwCount) + { + DWORD dwSize = (DWORD)m_headers.size(); + + if(lpHeaders == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount) + { + dwCount = dwSize; + return FALSE; + } + + DWORD dwIndex = 0; + + for(THeaderMapCI it = m_headers.begin(), end = m_headers.end(); it != end; ++it, ++dwIndex) + { + lpHeaders[dwIndex].name = it->first; + lpHeaders[dwIndex].value = it->second; + } + + dwCount = dwSize; + return TRUE; + } + + BOOL GetAllHeaderNames(LPCSTR lpszName[], DWORD& dwCount) + { + DWORD dwSize = (DWORD)m_headers.size(); + + if(lpszName == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount) + { + dwCount = dwSize; + return FALSE; + } + + DWORD dwIndex = 0; + + for(THeaderMapCI it = m_headers.begin(), end = m_headers.end(); it != end; ++it, ++dwIndex) + lpszName[dwIndex] = it->first; + + dwCount = dwSize; + return TRUE; + } + + BOOL AddCookie(LPCSTR lpszName, LPCSTR lpszValue, BOOL bRelpace = TRUE) + { + ASSERT(lpszName); + + TCookieMapI it = m_cookies.find(lpszName); + + if(it == m_cookies.end()) + return m_cookies.emplace(move(TCookieMap::value_type(lpszName, lpszValue))).second; + + BOOL isOK = FALSE; + + if(bRelpace) + { + it->second = lpszValue; + isOK = TRUE; + } + + return isOK; + } + + BOOL DeleteCookie(LPCSTR lpszName) + { + ASSERT(lpszName); + + return m_cookies.erase(lpszName) > 0; + } + + void DeleteAllCookies() + { + m_cookies.clear(); + } + + BOOL GetCookie(LPCSTR lpszName, LPCSTR* lpszValue) + { + ASSERT(lpszName); + + BOOL isOK = FALSE; + TCookieMapCI it = m_cookies.find(lpszName); + + if(it != m_cookies.end()) + { + *lpszValue = it->second; + isOK = TRUE; + } + + return isOK; + } + + BOOL GetAllCookies(TCookie lpCookies[], DWORD& dwCount) + { + DWORD dwSize = (DWORD)m_cookies.size(); + + if(lpCookies == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount) + { + dwCount = dwSize; + return FALSE; + } + + DWORD dwIndex = 0; + + for(TCookieMapCI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it, ++dwIndex) + { + lpCookies[dwIndex].name = it->first; + lpCookies[dwIndex].value = it->second; + } + + dwCount = dwSize; + return TRUE; + } + + BOOL ReloadCookies() + { + CCookieMgr* pCookieMgr = m_pContext->GetCookieMgr(); + + if(pCookieMgr == nullptr) + return TRUE; + + DeleteAllCookies(); + + CCookieSet cookies; + + if(!pCookieMgr->GetCookies(cookies, GetDomain(), GetPath(), TRUE, m_pContext->IsSecure())) + return FALSE; + + for(CCookieSetCI it = cookies.begin(), end = cookies.end(); it != end; ++it) + AddCookie(it->name, it->value); + + return TRUE; + } + + BOOL GetWSMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain) + { + if(!m_pwsContext) + return FALSE; + + return m_pwsContext->GetMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain); + } + +public: + THttpObjT (BOOL bRequest, T* pContext, S* pSocket) + : m_pContext (pContext) + , m_pSocket (pSocket) + , m_bRequest (bRequest) + , m_bValid (FALSE) + , m_bReleased (FALSE) + , m_dwFreeTime (0) + , m_usUrlFieldSet (m_bRequest ? 0 : -1) + , m_pstrUrlFileds (nullptr) + , m_enUpgrade (HUT_NONE) + , m_pwsContext (nullptr) + { + if(m_bRequest) + m_pstrUrlFileds = new CStringA[HUF_MAX]; + else + m_pstrRequestPath = new CStringA; + + ResetParser(); + } + + ~THttpObjT() + { + if(m_bRequest) + delete[] m_pstrUrlFileds; + else + delete m_pstrRequestPath; + + ReleaseWSContext(); + } + + static THttpObjT* Construct(BOOL bRequest, T* pContext, S* pSocket) + {return new THttpObjT(bRequest, pContext, pSocket);} + + static void Destruct(THttpObjT* pHttpObj) + {if(pHttpObj) delete pHttpObj;} + + void Reset(BOOL bValid = FALSE) + { + ResetParser(); + ResetHeaderState(); + ReleaseWSContext(); + + m_bValid = bValid; + m_bReleased = FALSE; + m_enUpgrade = HUT_NONE; + m_dwFreeTime = 0; + } + + void Renew(T* pContext, S* pSocket) + { + Reset(TRUE); + + m_pContext = pContext; + m_pSocket = pSocket; + } + + void SetValid(BOOL bValid = TRUE) + { + m_bValid = bValid; + } + + BOOL IsValid() + { + return m_bValid; + } + + BOOL CopyData(const THttpObjT& src) + { + if(&src == this) + return FALSE; + if(m_bRequest != src.m_bRequest) + return FALSE; + + void* p = m_parser.data; + m_parser = src.m_parser; + m_parser.data = p; + + m_headers = src.m_headers; + m_cookies = src.m_cookies; + + if(m_bRequest) + { + m_usUrlFieldSet = src.m_usUrlFieldSet; + + for(int i = 0;i < HUF_MAX; i++) + m_pstrUrlFileds[i] = src.m_pstrUrlFileds[i]; + } + else + { + m_sRequestMethod = src.m_sRequestMethod; + *m_pstrRequestPath = *src.m_pstrRequestPath; + } + + m_enUpgrade = src.m_enUpgrade; + + return TRUE; + } + + BOOL CopyWSContext(const THttpObjT& src) + { + if(&src == this) + return FALSE; + if(m_bRequest != src.m_bRequest) + return FALSE; + + if(!src.m_pwsContext && !m_pwsContext) + ; + else if(!src.m_pwsContext && m_pwsContext) + { + delete m_pwsContext; + m_pwsContext = nullptr; + } + else + { + if(!m_pwsContext) + m_pwsContext = new TWSContext>(this); + + m_pwsContext->CopyData(*src.m_pwsContext); + } + + return TRUE; + } + +private: + + void ResetParser() + { + ::llhttp_init(&m_parser, m_bRequest ? HTTP_REQUEST : HTTP_RESPONSE, &sm_settings); + m_parser.data = this; + } + + void ResetHeaderState(BOOL bClearCookies = TRUE, BOOL bResetRequestData = TRUE) + { + if(m_bRequest) + { + if(m_usUrlFieldSet != 0) + { + m_usUrlFieldSet = 0; + + for(int i = 0; i < HUF_MAX; i++) + m_pstrUrlFileds[i].Empty(); + } + } + else + { + if(bResetRequestData) + { + m_sRequestMethod = -1; + m_pstrRequestPath->Empty(); + } + } + + if(m_bRequest || bClearCookies) + DeleteAllCookies(); + + m_headers.clear(); + ResetHeaderBuffer(); + } + + void ResetHeaderBuffer() + { + ResetBuffer(); + m_strCurHeader.Empty(); + } + + void ReleaseWSContext() + { + if(m_pwsContext) + { + delete m_pwsContext; + m_pwsContext = nullptr; + } + } + + void AppendBuffer(const char* at, size_t length) {m_strBuffer.Append(at, (int)length);} + void ResetBuffer() {m_strBuffer.Empty();} + LPCSTR GetBuffer() {return m_strBuffer;} + CStringA& GetBufferRef() {return m_strBuffer;} + + static THttpObjT* Self(http_parser* p) {return (THttpObjT*)(p->data);} + static T* SelfContext(http_parser* p) {return Self(p)->m_pContext;} + static S* SelfSocketObj(http_parser* p) {return Self(p)->m_pSocket;} + +private: + BOOL m_bValid; + BOOL m_bRequest; + BOOL m_bReleased; + T* m_pContext; + S* m_pSocket; + http_parser m_parser; + THeaderMap m_headers; + TCookieMap m_cookies; + CStringA m_strBuffer; + CStringA m_strCurHeader; + + union + { + USHORT m_usUrlFieldSet; + short m_sRequestMethod; + }; + + union + { + CStringA* m_pstrUrlFileds; + CStringA* m_pstrRequestPath; + }; + + EnHttpUpgradeType m_enUpgrade; + DWORD m_dwFreeTime; + + TWSContext>* m_pwsContext; + + static http_parser_settings sm_settings; +}; + +template http_parser_settings THttpObjT::sm_settings = +{ + on_message_begin, + on_url, + on_status, + nullptr, // on_method + nullptr, // on_version + on_header_field, + on_header_value, + nullptr, // on_chunk_extension_name + nullptr, // on_chunk_extension_value + on_headers_complete, + on_body, + on_message_complete, + on_url_complete, + on_status_complete, + nullptr, // on_method_complete + nullptr, // on_version_complete + on_header_field_complete, + on_header_value_complete, + nullptr, // on_chunk_extension_name_complete + nullptr, // on_chunk_extension_value_complete + on_chunk_header, + on_chunk_complete, + nullptr // on_reset +}; + +// ------------------------------------------------------------------------------------------------------------- // + +template class CHttpObjPoolT +{ + typedef THttpObjT THttpObj; + typedef CRingPool TSSLHttpObjList; + typedef CCASQueue TSSLHttpObjQueue; + +public: + THttpObj* PickFreeHttpObj(T* pContext, S* pSocket) + { + DWORD dwIndex; + THttpObj* pHttpObj = nullptr; + + if(m_lsFreeHttpObj.TryLock(&pHttpObj, dwIndex)) + { + if(::GetTimeGap32(pHttpObj->GetFreeTime()) >= m_dwHttpObjLockTime) + VERIFY(m_lsFreeHttpObj.ReleaseLock(nullptr, dwIndex)); + else + { + VERIFY(m_lsFreeHttpObj.ReleaseLock(pHttpObj, dwIndex)); + pHttpObj = nullptr; + } + } + + if(pHttpObj) + pHttpObj->Renew(pContext, pSocket); + else + { + pHttpObj = THttpObj::Construct(is_request, pContext, pSocket); + ASSERT(pHttpObj); + } + + return pHttpObj; + } + + void PutFreeHttpObj(THttpObj* pHttpObj) + { + pHttpObj->SetFree(); + +#ifndef USE_EXTERNAL_GC + ReleaseGCHttpObj(); +#endif + if(!m_lsFreeHttpObj.TryPut(pHttpObj)) + m_lsGCHttpObj.PushBack(pHttpObj); + } + + void Prepare() + { + m_lsFreeHttpObj.Reset(m_dwHttpObjPoolSize); + } + + void Clear() + { + m_lsFreeHttpObj.Clear(); + + ReleaseGCHttpObj(TRUE); + VERIFY(m_lsGCHttpObj.IsEmpty()); + } + + void ReleaseGCHttpObj(BOOL bForce = FALSE) + { + ::ReleaseGCObj(m_lsGCHttpObj, m_dwHttpObjLockTime, bForce); + } + +public: + void SetHttpObjLockTime (DWORD dwHttpObjLockTime) {m_dwHttpObjLockTime = dwHttpObjLockTime;} + void SetHttpObjPoolSize (DWORD dwHttpObjPoolSize) {m_dwHttpObjPoolSize = dwHttpObjPoolSize;} + void SetHttpObjPoolHold (DWORD dwHttpObjPoolHold) {m_dwHttpObjPoolHold = dwHttpObjPoolHold;} + + DWORD GetHttpObjLockTime() {return m_dwHttpObjLockTime;} + DWORD GetHttpObjPoolSize() {return m_dwHttpObjPoolSize;} + DWORD GetHttpObjPoolHold() {return m_dwHttpObjPoolHold;} + +public: + CHttpObjPoolT( DWORD dwPoolSize = DEFAULT_HTTPOBJ_POOL_SIZE, + DWORD dwPoolHold = DEFAULT_HTTPOBJ_POOL_HOLD, + DWORD dwLockTime = DEFAULT_HTTPOBJ_LOCK_TIME) + : m_dwHttpObjPoolSize(dwPoolSize) + , m_dwHttpObjPoolHold(dwPoolHold) + , m_dwHttpObjLockTime(dwLockTime) + { + + } + + ~CHttpObjPoolT() {Clear();} + + DECLARE_NO_COPY_CLASS(CHttpObjPoolT) + +public: + static const DWORD DEFAULT_HTTPOBJ_LOCK_TIME; + static const DWORD DEFAULT_HTTPOBJ_POOL_SIZE; + static const DWORD DEFAULT_HTTPOBJ_POOL_HOLD; + +private: + DWORD m_dwHttpObjLockTime; + DWORD m_dwHttpObjPoolSize; + DWORD m_dwHttpObjPoolHold; + + TSSLHttpObjList m_lsFreeHttpObj; + TSSLHttpObjQueue m_lsGCHttpObj; +}; + +template const DWORD CHttpObjPoolT::DEFAULT_HTTPOBJ_LOCK_TIME = DEFAULT_OBJECT_CACHE_LOCK_TIME; +template const DWORD CHttpObjPoolT::DEFAULT_HTTPOBJ_POOL_SIZE = DEFAULT_OBJECT_CACHE_POOL_SIZE; +template const DWORD CHttpObjPoolT::DEFAULT_HTTPOBJ_POOL_HOLD = DEFAULT_OBJECT_CACHE_POOL_HOLD; + +// ------------------------------------------------------------------------------------------------------------- // + +extern CStringA& GetHttpVersionStr(EnHttpVersion enVersion, CStringA& strResult); +extern CStringA& AdjustRequestPath(BOOL bConnect, LPCSTR lpszPath, CStringA& strPath); +extern LPCSTR GetHttpDefaultStatusCodeDesc(EnHttpStatusCode enCode); +extern void MakeRequestLine(LPCSTR lpszMethod, LPCSTR lpszPath, EnHttpVersion enVersion, CStringA& strValue); +extern void MakeStatusLine(EnHttpVersion enVersion, USHORT usStatusCode, LPCSTR lpszDesc, CStringA& strValue); +extern void MakeHeaderLines(const THeader lpHeaders[], int iHeaderCount, const TCookieMap* pCookies, int iBodyLength, BOOL bRequest, int iConnFlag, LPCSTR lpszDefaultHost, USHORT usPort, CStringA& strValue); +extern void MakeHttpPacket(const CStringA& strHeader, const BYTE* pBody, int iLength, WSABUF szBuffer[2]); +extern int MakeChunkPackage(const BYTE* pData, int iLength, LPCSTR lpszExtensions, char szLen[12], WSABUF bufs[5]); +extern 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]); +extern BOOL ParseUrl(const CStringA& strUrl, BOOL& bHttps, CStringA& strHost, USHORT& usPort, CStringA& strPath); + +#endif \ No newline at end of file diff --git a/HttpServer.cpp b/HttpServer.cpp new file mode 100644 index 0000000..1f5cc4c --- /dev/null +++ b/HttpServer.cpp @@ -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 BOOL CHttpServerT::Start(LPCTSTR lpszBindAddress, USHORT usPort) +{ + BOOL isOK = __super::Start(lpszBindAddress, usPort); + + if(isOK) VERIFY(m_thCleaner.Start(this, &CHttpServerT::CleanerThreadProc)); + + return isOK; +} + +template BOOL CHttpServerT::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 void CHttpServerT::PrepareStart() +{ + __super::PrepareStart(); + + m_objPool.SetHttpObjLockTime(GetFreeSocketObjLockTime()); + m_objPool.SetHttpObjPoolSize(GetFreeSocketObjPool()); + m_objPool.SetHttpObjPoolHold(GetFreeSocketObjHold()); + + m_objPool.Prepare(); +} + +template BOOL CHttpServerT::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 BOOL CHttpServerT::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 BOOL CHttpServerT::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 BOOL CHttpServerT::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 BOOL CHttpServerT::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 UINT CHttpServerT::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 void CHttpServerT::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 void CHttpServerT::ReleaseDyingConnection() +{ + TDyingConnection* pDyingConn = nullptr; + + while(m_lsDyingQueue.UnsafePopFront(&pDyingConn)) + TDyingConnection::Destruct(pDyingConn); + + VERIFY(m_lsDyingQueue.IsEmpty()); +} + +template EnHandleResult CHttpServerT::FireAccept(TSocketObj* pSocketObj) +{ + return m_bHttpAutoStart ? __super::FireAccept(pSocketObj) : __super::DoFireAccept(pSocketObj); +} + +template EnHandleResult CHttpServerT::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 EnHandleResult CHttpServerT::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 EnHandleResult CHttpServerT::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 EnHandleResult CHttpServerT::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 EnHandleResult CHttpServerT::DoFireShutdown() +{ + EnHandleResult result = __super::DoFireShutdown(); + + m_objPool.Clear(); + WaitForCleanerThreadEnd(); + + return result; +} + +template void CHttpServerT::ReleaseGCSocketObj(BOOL bForce) +{ + __super::ReleaseGCSocketObj(bForce); + +#ifdef USE_EXTERNAL_GC + m_objPool.ReleaseGCHttpObj(bForce); +#endif +} + +template void CHttpServerT::WaitForCleanerThreadEnd() +{ + if(m_thCleaner.IsRunning()) + { + m_evCleaner.Set(); + m_thCleaner.Join(); + m_evCleaner.Reset(); + } +} + +template BOOL CHttpServerT::IsUpgrade(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->IsUpgrade(); +} + +template BOOL CHttpServerT::IsKeepAlive(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->IsKeepAlive(); +} + +template USHORT CHttpServerT::GetVersion(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return 0; + + return pHttpObj->GetVersion(); +} + +template LPCSTR CHttpServerT::GetHost(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetHost(); +} + +template ULONGLONG CHttpServerT::GetContentLength(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return 0; + + return pHttpObj->GetContentLength(); +} + +template LPCSTR CHttpServerT::GetContentType(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetContentType(); +} + +template LPCSTR CHttpServerT::GetContentEncoding(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetContentEncoding(); +} + +template LPCSTR CHttpServerT::GetTransferEncoding(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetTransferEncoding(); +} + +template EnHttpUpgradeType CHttpServerT::GetUpgradeType(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return HUT_NONE; + + return pHttpObj->GetUpgradeType(); +} + +template USHORT CHttpServerT::GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return 0; + + return pHttpObj->GetParseErrorCode(lpszErrorDesc); +} + +template BOOL CHttpServerT::GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetHeader(lpszName, lpszValue); +} + +template BOOL CHttpServerT::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 BOOL CHttpServerT::GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetAllHeaders(lpHeaders, dwCount); +} + +template BOOL CHttpServerT::GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetAllHeaderNames(lpszName, dwCount); +} + +template BOOL CHttpServerT::GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetCookie(lpszName, lpszValue); +} + +template BOOL CHttpServerT::GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return FALSE; + + return pHttpObj->GetAllCookies(lpCookies, dwCount); +} + +template USHORT CHttpServerT::GetUrlFieldSet(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return 0; + + return pHttpObj->GetUrlFieldSet(); +} + +template LPCSTR CHttpServerT::GetUrlField(CONNID dwConnID, EnHttpUrlField enField) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetUrlField(enField); +} + +template LPCSTR CHttpServerT::GetMethod(CONNID dwConnID) +{ + THttpObj* pHttpObj = FindHttpObj(dwConnID); + + if(pHttpObj == nullptr) + return nullptr; + + return pHttpObj->GetMethod(); +} + +template BOOL CHttpServerT::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 inline typename CHttpServerT::THttpObj* CHttpServerT::FindHttpObj(CONNID dwConnID) +{ + THttpObj* pHttpObj = nullptr; + GetConnectionReserved(dwConnID, (PVOID*)&pHttpObj); + + return pHttpObj; +} + +template inline typename CHttpServerT::THttpObj* CHttpServerT::FindHttpObj(TSocketObj* pSocketObj) +{ + THttpObj* pHttpObj = nullptr; + GetConnectionReserved(pSocketObj, (PVOID*)&pHttpObj); + + return pHttpObj; +} + +template BOOL CHttpServerT::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 BOOL CHttpServerT::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 typename CHttpServerT::THttpObj* CHttpServerT::DoStartHttp(TSocketObj* pSocketObj) +{ + THttpObj* pHttpObj = m_objPool.PickFreeHttpObj(this, pSocketObj); + VERIFY(SetConnectionReserved(pSocketObj, pHttpObj)); + + return pHttpObj; +} + +// ------------------------------------------------------------------------------------------------------------- // + +template class CHttpServerT; + +#ifdef _SSL_SUPPORT + +#include "SSLServer.h" + +template class CHttpServerT; + +#endif + +#endif \ No newline at end of file diff --git a/HttpServer.h b/HttpServer.h new file mode 100644 index 0000000..6decd32 --- /dev/null +++ b/HttpServer.h @@ -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 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; + using CHttpObjPool = CHttpObjPoolT; + using THttpObj = THttpObjT; + + 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 m_lsDyingQueue; + + CHttpObjPool m_objPool; +}; + +// ------------------------------------------------------------------------------------------------------------- // + +typedef CHttpServerT CHttpServer; + +#ifdef _SSL_SUPPORT + +#include "SSLServer.h" + +typedef CHttpServerT CHttpsServer; + +#endif + +#endif \ No newline at end of file diff --git a/InternalDef.h b/InternalDef.h new file mode 100644 index 0000000..ee4b4ca --- /dev/null +++ b/InternalDef.h @@ -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;} diff --git a/MiscHelper.cpp b/MiscHelper.cpp new file mode 100644 index 0000000..5a238ec --- /dev/null +++ b/MiscHelper.cpp @@ -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& 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; +} diff --git a/MiscHelper.h b/MiscHelper.h new file mode 100644 index 0000000..4f6beae --- /dev/null +++ b/MiscHelper.h @@ -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 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 TBufferPackInfo; + +BOOL AddPackHeader(const WSABUF * pBuffers, int iCount, unique_ptr& buffers, DWORD dwMaxPackSize, USHORT usPackHeaderFlag, DWORD& dwHeader); + +template 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 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 EnHandleResult ParsePack(T* pThis, TPackInfo* 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 EnHandleResult ParsePack(T* pThis, TPackInfo* 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); +} diff --git a/SSLAgent.cpp b/SSLAgent.cpp new file mode 100644 index 0000000..4b3dd7d --- /dev/null +++ b/SSLAgent.cpp @@ -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 \ No newline at end of file diff --git a/SSLAgent.h b/SSLAgent.h new file mode 100644 index 0000000..901c9ba --- /dev/null +++ b/SSLAgent.h @@ -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 \ No newline at end of file diff --git a/SSLClient.cpp b/SSLClient.cpp new file mode 100644 index 0000000..7dc2ece --- /dev/null +++ b/SSLClient.cpp @@ -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 \ No newline at end of file diff --git a/SSLClient.h b/SSLClient.h new file mode 100644 index 0000000..0f68fd8 --- /dev/null +++ b/SSLClient.h @@ -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 \ No newline at end of file diff --git a/SSLHelper.cpp b/SSLHelper.cpp new file mode 100644 index 0000000..27be3a6 --- /dev/null +++ b/SSLHelper.cpp @@ -0,0 +1,1200 @@ +/* + * 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 "SSLHelper.h" + +#ifdef _SSL_SUPPORT + +#include "SocketHelper.h" +#include "common/FileHelper.h" + +#include "openssl/ssl.h" +#include "openssl/err.h" +#include "openssl/engine.h" +#include "openssl/x509v3.h" + +#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0 + int CSSLInitializer::sm_iLockNum = 0; + CSimpleRWLock* CSSLInitializer::sm_pcsLocks = nullptr; +#endif + +CSSLInitializer CSSLInitializer::sm_instance; + +const DWORD CSSLSessionPool::DEFAULT_ITEM_CAPACITY = CItemPool::DEFAULT_ITEM_CAPACITY; +const DWORD CSSLSessionPool::DEFAULT_ITEM_POOL_SIZE = CItemPool::DEFAULT_POOL_SIZE; +const DWORD CSSLSessionPool::DEFAULT_ITEM_POOL_HOLD = CItemPool::DEFAULT_POOL_HOLD; +const DWORD CSSLSessionPool::DEFAULT_SESSION_LOCK_TIME = DEFAULT_OBJECT_CACHE_LOCK_TIME; +const DWORD CSSLSessionPool::DEFAULT_SESSION_POOL_SIZE = DEFAULT_OBJECT_CACHE_POOL_SIZE; +const DWORD CSSLSessionPool::DEFAULT_SESSION_POOL_HOLD = DEFAULT_OBJECT_CACHE_POOL_HOLD; + +CSSLInitializer::CSSLInitializer() +{ +#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0 + sm_iLockNum = CRYPTO_num_locks(); + + if(sm_iLockNum > 0) + sm_pcsLocks = new CSimpleRWLock[sm_iLockNum]; +/* +#ifdef _DEBUG + CRYPTO_malloc_debug_init(); + CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif +*/ + CRYPTO_set_locking_callback (&ssl_lock_callback); + CRYPTO_set_dynlock_create_callback (&ssl_lock_dyn_create_callback); + CRYPTO_set_dynlock_destroy_callback (&ssl_lock_dyn_destroy_callback); + CRYPTO_set_dynlock_lock_callback (&ssl_lock_dyn_callback); + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); +#else + OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, nullptr); +#endif +} + +CSSLInitializer::~CSSLInitializer() +{ + CleanupThreadState(); + +#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0 + CONF_modules_free(); + ENGINE_cleanup(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); +#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_2 + SSL_COMP_free_compression_methods(); +#endif + + CRYPTO_set_locking_callback (nullptr); + CRYPTO_set_dynlock_create_callback (nullptr); + CRYPTO_set_dynlock_destroy_callback (nullptr); + CRYPTO_set_dynlock_lock_callback (nullptr); + + if(sm_iLockNum > 0) + { + delete[] sm_pcsLocks; + + sm_pcsLocks = nullptr; + sm_iLockNum = 0; + } +#endif +} + +void CSSLInitializer::CleanupThreadState(THR_ID dwThreadID) +{ +#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0 + CRYPTO_THREADID tid = {nullptr, dwThreadID}; + + CRYPTO_THREADID_current(&tid); + ERR_remove_thread_state(&tid); +#else + OPENSSL_thread_stop(); +#endif +} + +#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0 + +void CSSLInitializer::ssl_lock_callback(int mode, int n, const char *file, int line) +{ + mode & CRYPTO_LOCK + ? (mode & CRYPTO_READ + ? sm_pcsLocks[n].lock_shared() + : sm_pcsLocks[n].lock()) + : (mode & CRYPTO_READ + ? sm_pcsLocks[n].unlock_shared() + : sm_pcsLocks[n].unlock()); +} + +CRYPTO_dynlock_value* CSSLInitializer::ssl_lock_dyn_create_callback(const char *file, int line) + { + return new DynamicLock; + } + +void CSSLInitializer::ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line) +{ + mode & CRYPTO_LOCK + ? (mode & CRYPTO_READ + ? l->cs.lock_shared() + : l->cs.lock()) + : (mode & CRYPTO_READ + ? l->cs.unlock_shared() + : l->cs.unlock()); +} + +void CSSLInitializer::ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line) +{ + delete l; +} + +#endif + +BOOL CSSLContext::Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert, HP_Fn_SNI_ServerNameCallback fnServerNameCallback) +{ + ASSERT(!IsValid()); + + if(IsValid()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + m_enSessionMode = enSessionMode; + + if(AddContext(iVerifyMode, bMemory, lpPemCert, lpPemKey, lpKeyPasswod, lpCAPemCert) == 0) + m_sslCtx = GetContext(0); + else + { + EXECUTE_RESTORE_ERROR(Cleanup()); + return FALSE; + } + + SetServerNameCallback(fnServerNameCallback); + + return TRUE; +} + +int CSSLContext::AddServerContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert) +{ + ASSERT(IsValid()); + + if(!IsValid()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + if(m_enSessionMode != SSL_SM_SERVER) + { + ::SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + return AddContext(iVerifyMode, bMemory, lpPemCert, lpPemKey, lpKeyPasswod, lpCAPemCert); +} + +BOOL CSSLContext::BindServerName(LPCTSTR lpszServerName, int iContextIndex) +{ + ASSERT(lpszServerName && iContextIndex >= 0 && !::IsIPAddress(lpszServerName)); + + if(!lpszServerName || iContextIndex < 0 || ::IsIPAddress(lpszServerName)) + { + ::SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + int iLen = lstrlen(lpszServerName); + LPCTSTR lpszSep = ::StrChr(lpszServerName, SSL_DOMAIN_SEP_CHAR); + + if(lpszSep == nullptr || lpszSep == lpszServerName || lpszSep == (lpszServerName + iLen - 1)) + { + ::SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + int iSize = (int)m_lsSslCtxs.size(); + + if(iSize <= iContextIndex) + { + ::SetLastError(ERROR_INVALID_INDEX); + return FALSE; + } + + m_sslServerNames[lpszServerName] = iContextIndex; + + return TRUE; +} + +int CSSLContext::AddContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert) +{ + USES_CONVERSION; + + int iIndex = -1; + +#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0 + SSL_CTX* sslCtx = SSL_CTX_new(SSLv23_method()); +#else + SSL_CTX* sslCtx = SSL_CTX_new(TLS_method()); +#endif + + SSL_CTX_set_quiet_shutdown(sslCtx, 1); + SSL_CTX_set_verify(sslCtx, iVerifyMode, nullptr); + + if(!SSL_CTX_set_cipher_list(sslCtx, T2CA(m_strCipherList))) + ::SetLastError(ERROR_EMPTY); + else + { + if(m_enSessionMode == SSL_SM_SERVER) + { + static volatile ULONG s_session_id_context = 0; + ULONG session_id_context = ::InterlockedIncrement(&s_session_id_context); + + SSL_CTX_set_session_id_context(sslCtx, (BYTE*)&session_id_context, sizeof(session_id_context)); + } + + if(LoadCertAndKey(sslCtx, iVerifyMode, bMemory, lpPemCert, lpPemKey, lpKeyPasswod, lpCAPemCert)) + { + iIndex = (int)m_lsSslCtxs.size(); + m_lsSslCtxs.push_back(sslCtx); + } + } + + if(iIndex < 0) + EXECUTE_RESTORE_ERROR(SSL_CTX_free(sslCtx)); + + return iIndex; +} + +BOOL CSSLContext::LoadCertAndKey(SSL_CTX* sslCtx, int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert) +{ + if(bMemory) + return LoadCertAndKeyByMemory(sslCtx, iVerifyMode, (LPCSTR)lpPemCert, (LPCSTR)lpPemKey, (LPCSTR)lpKeyPasswod, (LPCSTR)lpCAPemCert); + else + return LoadCertAndKeyByFile(sslCtx, iVerifyMode, (LPCTSTR)lpPemCert, (LPCTSTR)lpPemKey, (LPCTSTR)lpKeyPasswod, (LPCTSTR)lpCAPemCert); +} + +BOOL CSSLContext::LoadCertAndKeyByFile(SSL_CTX* sslCtx, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath) +{ + USES_CONVERSION; + + if(::IsStrNotEmpty(lpszCAPemCertFileOrPath)) + { + LPCTSTR lpszCAPemCertFile = nullptr; + LPCTSTR lpszCAPemCertPath = nullptr; + + CFile fCAPemCertFile(lpszCAPemCertFileOrPath, O_RDONLY | O_CLOEXEC); + + if(!fCAPemCertFile.IsExist()) + { + ::SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + if(fCAPemCertFile.IsFile()) + lpszCAPemCertFile = lpszCAPemCertFileOrPath; + else if(fCAPemCertFile.IsDirectory()) + lpszCAPemCertPath = lpszCAPemCertFileOrPath; + else + { + ::SetLastError(ERROR_BAD_FILE_TYPE); + return FALSE; + } + + if(!SSL_CTX_load_verify_locations(sslCtx, T2CA(lpszCAPemCertFile), T2CA(lpszCAPemCertPath))) + { + ::SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + if(!SSL_CTX_set_default_verify_paths(sslCtx)) + { + ::SetLastError(ERROR_FUNCTION_FAILED); + return FALSE; + } + + if(m_enSessionMode == SSL_SM_SERVER && (iVerifyMode & SSL_VM_PEER) && lpszCAPemCertFile != nullptr) + { + STACK_OF(X509_NAME)* caCertNames = SSL_load_client_CA_file(T2CA(lpszCAPemCertFile)); + + if(caCertNames == nullptr) + { + ::SetLastError(ERROR_EMPTY); + return FALSE; + } + + SSL_CTX_set_client_CA_list(sslCtx, caCertNames); + } + } + + if(::IsStrNotEmpty(lpszPemCertFile)) + { + CFile fPemCertFile(lpszPemCertFile, O_RDONLY | O_CLOEXEC); + + if(!fPemCertFile.IsFile()) + { + ::SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + if(::IsStrEmpty(lpszPemKeyFile)) + { + ::SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + CFile fPemKeyFile(lpszPemKeyFile, O_RDONLY | O_CLOEXEC); + + if(!fPemKeyFile.IsFile()) + { + ::SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + if(::IsStrNotEmpty(lpszKeyPassword)) + SSL_CTX_set_default_passwd_cb_userdata(sslCtx, (void*)T2CA(lpszKeyPassword)); + + if(!SSL_CTX_use_PrivateKey_file(sslCtx, T2CA(lpszPemKeyFile), SSL_FILETYPE_PEM)) + { + ::SetLastError(ERROR_INVALID_PASSWORD); + return FALSE; + } + + if(!SSL_CTX_use_certificate_chain_file(sslCtx, T2CA(lpszPemCertFile))) + { + ::SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + + if(!SSL_CTX_check_private_key(sslCtx)) + { + ::SetLastError(ERROR_INVALID_ACCESS); + return FALSE; + } + } + + return TRUE; +} + +BOOL CSSLContext::LoadCertAndKeyByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert) +{ + if(!LoadCAPemCertByMemory(sslCtx, iVerifyMode, lpszCAPemCert)) + return FALSE; + if(!LoadPemCertAndKeyByMemory(sslCtx, lpszPemCert, lpszPemKey, lpszKeyPassword)) + return FALSE; + + return TRUE; +} + +BOOL CSSLContext::LoadCAPemCertByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszCAPemCert) +{ + if(::IsStrEmptyA(lpszCAPemCert)) + return TRUE; + + if(!AddCAPemCertToStoreByMemory(sslCtx, lpszCAPemCert)) + return FALSE; + + if(!SSL_CTX_set_default_verify_paths(sslCtx)) + { + ::SetLastError(ERROR_FUNCTION_FAILED); + return FALSE; + } + + if(m_enSessionMode == SSL_SM_SERVER && (iVerifyMode & SSL_VM_PEER)) + { + if(!SetClientCAListByMemory(sslCtx, lpszCAPemCert)) + return FALSE; + } + + return TRUE; +} + +BOOL CSSLContext::LoadPemCertAndKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword) +{ + if(::IsStrEmptyA(lpszPemCert)) + return TRUE; + + if(::IsStrEmptyA(lpszPemKey)) + { + ::SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if(::IsStrNotEmptyA(lpszKeyPassword)) + SSL_CTX_set_default_passwd_cb_userdata(sslCtx, (void*)(lpszKeyPassword)); + + if(!SetPrivateKeyByMemory(sslCtx, lpszPemKey)) + return FALSE; + + if(!SetCertChainByMemory(sslCtx, lpszPemCert)) + return FALSE; + + if(!SSL_CTX_check_private_key(sslCtx)) + { + ::SetLastError(ERROR_INVALID_ACCESS); + return FALSE; + } + + return TRUE; +} + +BOOL CSSLContext::AddCAPemCertToStoreByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert) +{ + BOOL isOK = FALSE; + int iCount = 0; + BIO* pBIO = BIO_new_mem_buf(lpszPemCert, -1); + X509_STORE* pStore = SSL_CTX_get_cert_store(sslCtx); + STACK_OF(X509_INFO) * pStack = nullptr; + + if(pBIO == nullptr) + { + ::SetLastError(ERROR_CREATE_FAILED); + goto _END; + } + + if(pStore == nullptr) + { + ::SetLastError(ERROR_NOT_FOUND); + goto _END; + } + + pStack = PEM_X509_INFO_read_bio(pBIO, nullptr, nullptr, nullptr); + + if(pStack == nullptr) + { + ::SetLastError(ERROR_NO_DATA); + goto _END; + } + + for(int i = 0; i < sk_X509_INFO_num(pStack); i++) + { + X509_INFO* pInfo = sk_X509_INFO_value(pStack, i); + + if(pInfo->x509) + { + if(!X509_STORE_add_cert(pStore, pInfo->x509)) + { + ::SetLastError(ERROR_INVALID_DATA); + goto _END; + } + + ++iCount; + } + + if(pInfo->crl) + { + if(!X509_STORE_add_crl(pStore, pInfo->crl)) + { + ::SetLastError(ERROR_INVALID_DATA); + goto _END; + } + + ++iCount; + } + } + + if(iCount > 0) + isOK = TRUE; + else + ::SetLastError(ERROR_EMPTY); + +_END: + + if(pStack != nullptr) + sk_X509_INFO_pop_free(pStack, X509_INFO_free); + + if(pBIO != nullptr) + BIO_free(pBIO); + + return isOK; +} + +BOOL CSSLContext::SetClientCAListByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert) +{ + BOOL isOK = FALSE; + X509* pX509 = nullptr; + X509_NAME* pName = nullptr; + STACK_OF(X509_NAME)* pStack = nullptr; + BIO* pBIO = BIO_new_mem_buf(lpszPemCert, -1); + OPENSSL_LHASH* pNameHash = (OPENSSL_LHASH*)OPENSSL_LH_new((OPENSSL_LH_HASHFUNC)FN_X509_NAME_HASH, (OPENSSL_LH_COMPFUNC)X509_NAME_cmp); + + if(pBIO == nullptr || pNameHash == nullptr) + { + ::SetLastError(ERROR_CREATE_FAILED); + goto _ERR; + } + + while(TRUE) + { + if(PEM_read_bio_X509(pBIO, &pX509, nullptr, nullptr) == nullptr) + break; + + if(pStack == nullptr) + { + pStack = sk_X509_NAME_new_null(); + + if(pStack == nullptr) + { + ::SetLastError(ERROR_CREATE_FAILED); + goto _ERR; + } + } + + if((pName = X509_get_subject_name(pX509)) == nullptr) + { + ::SetLastError(ERROR_NO_DATA); + goto _ERR; + } + + if((pName = X509_NAME_dup(pName)) == nullptr) + { + ::SetLastError(ERROR_CREATE_FAILED); + goto _ERR; + } + + if(OPENSSL_LH_retrieve(pNameHash, pName) != nullptr) + { + X509_NAME_free(pName); + pName = nullptr; + } + else + { + OPENSSL_LH_insert(pNameHash, pName); + + if(!sk_X509_NAME_push(pStack, pName)) + { + ::SetLastError(ERROR_WRITE_FAULT); + goto _ERR; + } + } + } + + if(pStack == nullptr) + { + ::SetLastError(ERROR_EMPTY); + goto _ERR; + } + + SSL_CTX_set_client_CA_list(sslCtx, pStack); + + isOK = TRUE; + goto _END; + +_ERR: + + if(pName != nullptr) + X509_NAME_free(pName); + if(pStack != nullptr) + { + sk_X509_NAME_pop_free(pStack, X509_NAME_free); + pStack = nullptr; + } + +_END: + + if(pX509 != nullptr) + X509_free(pX509); + + if(pNameHash != nullptr) + OPENSSL_LH_free(pNameHash); + + if(pBIO != nullptr) + BIO_free(pBIO); + + if(pStack != nullptr) + ERR_clear_error(); + + return isOK; +} + +BOOL CSSLContext::SetPrivateKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemKey) +{ + BOOL isOK = FALSE; + BIO* pBIO = BIO_new_mem_buf(lpszPemKey, -1); + EVP_PKEY* pKey = nullptr; + + if(pBIO == nullptr) + { + ::SetLastError(ERROR_CREATE_FAILED); + goto _END; + } + + pKey = PEM_read_bio_PrivateKey(pBIO, nullptr, SSL_CTX_get_default_passwd_cb(sslCtx), SSL_CTX_get_default_passwd_cb_userdata(sslCtx)); + + if(pKey == nullptr) + { + ::SetLastError(ERROR_INVALID_PASSWORD); + goto _END; + } + + if(!SSL_CTX_use_PrivateKey(sslCtx, pKey)) + { + ::SetLastError(ERROR_INVALID_DATA); + goto _END; + } + + isOK = TRUE; + +_END: + + if(pKey != nullptr) + EVP_PKEY_free(pKey); + + if(pBIO != nullptr) + BIO_free(pBIO); + + return isOK; +} + +BOOL CSSLContext::SetCertChainByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert) +{ + BOOL isOK = FALSE; + ULONG err = 0; + BIO* pBIO = BIO_new_mem_buf(lpszPemCert, -1); + X509* pX509 = nullptr; + + pem_password_cb* cb = SSL_CTX_get_default_passwd_cb(sslCtx); + LPVOID userdata = SSL_CTX_get_default_passwd_cb_userdata(sslCtx); + + if(pBIO == nullptr) + { + ::SetLastError(ERROR_CREATE_FAILED); + goto _END; + } + + pX509 = PEM_read_bio_X509_AUX(pBIO, nullptr, cb, userdata); + + if(pX509 == nullptr) + { + ::SetLastError(ERROR_NO_DATA); + goto _END; + } + + if(!SSL_CTX_use_certificate(sslCtx, pX509) || (ERR_peek_error() != 0)) + { + ::SetLastError(ERROR_INVALID_DATA); + goto _END; + } + + if(!SSL_CTX_clear_chain_certs(sslCtx)) + { + ::SetLastError(ERROR_FUNCTION_FAILED); + goto _END; + } + + X509* pCA; + while((pCA = PEM_read_bio_X509(pBIO, nullptr, cb, userdata)) != nullptr) + { + if(!SSL_CTX_add0_chain_cert(sslCtx, pCA)) + { + X509_free(pCA); + + ::SetLastError(ERROR_FUNCTION_FAILED); + goto _END; + } + } + + err = ERR_peek_last_error(); + + if(ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) + ERR_clear_error(); + else + { + ::SetLastError(ERROR_FUNCTION_FAILED); + goto _END; + } + + isOK = TRUE; + +_END: + + if(pX509 != nullptr) + X509_free(pX509); + + if(pBIO != nullptr) + BIO_free(pBIO); + + return isOK; +} + +void CSSLContext::Cleanup() +{ + if(IsValid()) + { + int iCount = (int)m_lsSslCtxs.size(); + + for(int i = 0; i < iCount; i++) + SSL_CTX_free(m_lsSslCtxs[i]); + + m_lsSslCtxs.clear(); + m_sslServerNames.clear(); + + m_sslCtx = nullptr; + } + + m_fnServerNameCallback = nullptr; + + RemoveThreadLocalState(); +} + +void CSSLContext::SetServerNameCallback(Fn_SNI_ServerNameCallback fn) +{ + if(m_enSessionMode != SSL_SM_SERVER) + return; + + if(fn == nullptr) + m_fnServerNameCallback = DefaultServerNameCallback; + else + m_fnServerNameCallback = fn; + + VERIFY(SSL_CTX_set_tlsext_servername_callback(m_sslCtx, InternalServerNameCallback)); + VERIFY(SSL_CTX_set_tlsext_servername_arg(m_sslCtx, this)); +} + +int CSSLContext::InternalServerNameCallback(SSL* ssl, int* ad, void* arg) +{ + USES_CONVERSION; + + CSSLContext* pThis = (CSSLContext*)arg; + ASSERT(pThis->m_fnServerNameCallback != nullptr); + + const char* lpszServerName = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + + if(lpszServerName == nullptr) + return SSL_TLSEXT_ERR_NOACK; + + int iIndex = pThis->m_fnServerNameCallback(A2CT(lpszServerName), pThis); + + if(iIndex == 0) + return SSL_TLSEXT_ERR_OK; + + if(iIndex < 0) + { + ::SetLastError(ERROR_INVALID_NAME); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + SSL_CTX* sslCtx = pThis->GetContext(iIndex); + + if(sslCtx == nullptr) + { + ::SetLastError(ERROR_INVALID_INDEX); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + SSL_set_SSL_CTX(ssl, sslCtx); + + return SSL_TLSEXT_ERR_OK; +} + +int __HP_CALL CSSLContext::DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext) +{ + CSSLContext* pThis = (CSSLContext*)pContext; + + if(pThis->m_sslServerNames.empty()) + return 0; + + LPCTSTR lpszTmp = lpszServerName; + LPCTSTR lpszSep = ::StrChr(lpszTmp, SSL_DOMAIN_SEP_CHAR); + + while(lpszSep != nullptr) + { + CServerNameMap::const_iterator it = pThis->m_sslServerNames.find(lpszTmp); + + if(it != pThis->m_sslServerNames.end()) + return it->second; + + lpszTmp = (lpszSep + 1); + lpszSep = ::StrChr(lpszTmp, SSL_DOMAIN_SEP_CHAR); + } + + return 0; +} + +SSL_CTX* CSSLContext::GetContext(int i) const +{ + SSL_CTX* sslCtx = nullptr; + + if(i >= 0 && i < (int)m_lsSslCtxs.size()) + sslCtx = m_lsSslCtxs[i]; + + return sslCtx; +} + +BOOL CSSLSession::WriteRecvChannel(const BYTE* pData, int iLength) +{ + ASSERT(pData && iLength > 0); + + BOOL isOK = TRUE; + int bytes = BIO_write(m_bioRecv, pData, iLength); + + if(bytes > 0) + ASSERT(bytes == iLength); + else if(!BIO_should_retry(m_bioRecv)) + { + ::SetLastError(ERROR_INVALID_DATA); + isOK = FALSE; + } + + return isOK; +} + +BOOL CSSLSession::ReadRecvChannel() +{ + BOOL isOK = TRUE; + int bytes = SSL_read(m_ssl, m_bufRecv.buf, m_pitRecv->Capacity()); + + if(bytes > 0) + m_bufRecv.len = bytes; + else if(!IsFatalError(bytes)) + m_bufRecv.len = 0; + else + isOK = FALSE; + + if(isOK && m_enStatus == SSL_HSS_PROC && SSL_is_init_finished(m_ssl)) + m_enStatus = SSL_HSS_SUCC; + + return isOK; +} + +BOOL CSSLSession::WriteSendChannel(const BYTE* pData, int iLength) +{ + ASSERT(IsReady()); + ASSERT(pData && iLength > 0); + + BOOL isOK = TRUE; + int bytes = SSL_write(m_ssl, pData, iLength); + + if(bytes > 0) + ASSERT(bytes == iLength); + else if(IsFatalError(bytes)) + isOK = FALSE; + + return isOK; +} + +BOOL CSSLSession::WriteSendChannel(const WSABUF pBuffers[], int iCount) +{ + ASSERT(pBuffers && iCount > 0); + + BOOL isOK = TRUE; + + for(int i = 0; i < iCount; i++) + { + const WSABUF& buffer = pBuffers[i]; + + if(buffer.len > 0) + { + if(!WriteSendChannel((const BYTE*)buffer.buf, buffer.len)) + { + isOK = FALSE; + break; + } + } + } + + return isOK; +} + +BOOL CSSLSession::ReadSendChannel() +{ + if(BIO_pending(m_bioSend) == 0) + { + m_bufSend.len = 0; + return TRUE; + } + + BOOL isOK = TRUE; + int bytes = BIO_read(m_bioSend, m_bufSend.buf, m_pitSend->Capacity()); + + if(bytes > 0) + m_bufSend.len = bytes; + else if(BIO_should_retry(m_bioSend)) + m_bufSend.len = 0; + else + { + ::SetLastError(ERROR_INVALID_DATA); + isOK = FALSE; + } + + return isOK; +} + +CSSLSession* CSSLSession::Renew(const CSSLContext& sslCtx, LPCSTR lpszHostName) +{ + ASSERT(!IsValid()); + + ResetCount(); + + m_ssl = SSL_new(sslCtx.GetDefaultContext()); + m_bioSend = BIO_new(BIO_s_mem()); + m_bioRecv = BIO_new(BIO_s_mem()); + + SSL_set_bio(m_ssl, m_bioRecv, m_bioSend); + + if(sslCtx.GetSessionMode() == SSL_SM_SERVER) + SSL_accept(m_ssl); + else + { + USES_CONVERSION; + + if(lpszHostName && lpszHostName[0] != 0 && !::IsIPAddress(A2CT(lpszHostName))) + SSL_set_tlsext_host_name(m_ssl, lpszHostName); + + SSL_connect(m_ssl); + } + + m_pitSend = m_itPool.PickFreeItem(); + m_pitRecv = m_itPool.PickFreeItem(); + m_bufSend.buf = m_pitSend->Ptr(); + m_bufRecv.buf = m_pitRecv->Ptr(); + m_enStatus = SSL_HSS_PROC; + + return this; +} + +BOOL CSSLSession::Reset() +{ + BOOL isOK = FALSE; + + if(IsValid()) + { + CCriSecLock locallock(m_csSend); + + if(IsValid()) + { + m_enStatus = SSL_HSS_INIT; + + SSL_shutdown(m_ssl); + SSL_free(m_ssl); + + m_itPool.PutFreeItem(m_pitSend); + m_itPool.PutFreeItem(m_pitRecv); + + m_pitSend = nullptr; + m_pitRecv = nullptr; + m_ssl = nullptr; + m_bioSend = nullptr; + m_bioRecv = nullptr; + m_dwFreeTime= ::TimeGetTime(); + + isOK = TRUE; + } + } + + ERR_clear_error(); + + return isOK; +} + +inline BOOL CSSLSession::IsFatalError(int iBytes) +{ + int iErrorCode = SSL_get_error(m_ssl, iBytes); + + if( iErrorCode == SSL_ERROR_NONE || + iErrorCode == SSL_ERROR_WANT_READ || + iErrorCode == SSL_ERROR_WANT_WRITE || + iErrorCode == SSL_ERROR_WANT_CONNECT || + iErrorCode == SSL_ERROR_WANT_ACCEPT ) + return FALSE; + +#ifdef _DEBUG + char szBuffer[512]; +#endif + + int i = 0; + ULONG iCode = iErrorCode; + + for(; iCode != SSL_ERROR_NONE; i++) + { +#ifdef _DEBUG + ERR_error_string_n(iCode, szBuffer, sizeof(szBuffer)); + TRACE(" > SSL Error: %ld - %s\n", iCode, szBuffer); +#endif + + iCode = ERR_get_error(); + } + + if(iErrorCode == SSL_ERROR_SYSCALL && i == 1) + return FALSE; + + ::SetLastError(ERROR_INVALID_DATA); + + return TRUE; +} + +BOOL CSSLSession::GetSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo) +{ + ASSERT(lppInfo != nullptr); + + *lppInfo = nullptr; + + if(enInfo < SSL_SSI_MIN || enInfo > SSL_SSI_MAX) + { + ::SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if(!IsValid()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + SSL_CTX* pContext = SSL_get_SSL_CTX(m_ssl); + + switch(enInfo) + { + case SSL_SSI_CTX: + { + *lppInfo = (LPVOID)(pContext); + } + break; + case SSL_SSI_CTX_METHOD: + { + if(pContext != nullptr) + *lppInfo = (LPVOID)(SSL_CTX_get_ssl_method(pContext)); + } + break; + case SSL_SSI_CTX_CIPHERS: + { + if(pContext != nullptr) + *lppInfo = (LPVOID)(SSL_CTX_get_ciphers(pContext)); + } + break; + case SSL_SSI_CTX_CERT_STORE: + { + if(pContext != nullptr) + *lppInfo = (LPVOID)(SSL_CTX_get_cert_store(pContext)); + } + break; + case SSL_SSI_SERVER_NAME_TYPE: + { + *lppInfo = (LPVOID)(UINT_PTR)(SSL_get_servername_type(m_ssl)); + } + break; + case SSL_SSI_SERVER_NAME: + { + int type = SSL_get_servername_type(m_ssl); + + if(type != -1) + *lppInfo = (LPVOID)(SSL_get_servername(m_ssl, type)); + } + break; + case SSL_SSI_VERSION: + { + *lppInfo = (LPVOID)(SSL_get_version(m_ssl)); + } + break; + case SSL_SSI_METHOD: + { + *lppInfo = (LPVOID)(SSL_get_ssl_method(m_ssl)); + } + break; + case SSL_SSI_CERT: + { + *lppInfo = (LPVOID)(SSL_get_certificate(m_ssl)); + } + break; + case SSL_SSI_PKEY: + { + *lppInfo = (LPVOID)(SSL_get_privatekey(m_ssl)); + } + break; + case SSL_SSI_CURRENT_CIPHER: + { + *lppInfo = (LPVOID)(SSL_get_current_cipher(m_ssl)); + } + break; + case SSL_SSI_CIPHERS: + { + *lppInfo = (LPVOID)(SSL_get_ciphers(m_ssl)); + } + break; + case SSL_SSI_CLIENT_CIPHERS: + { + *lppInfo = (LPVOID)(SSL_get_client_ciphers(m_ssl)); + } + break; + case SSL_SSI_PEER_CERT: + { + X509* pCert = SSL_get_peer_certificate(m_ssl); + + if(pCert != nullptr) + { + *lppInfo = (LPVOID*)pCert; + X509_free(pCert); + } + } + break; + case SSL_SSI_PEER_CERT_CHAIN: + { + *lppInfo = (LPVOID)(SSL_get_peer_cert_chain(m_ssl)); + } + break; + case SSL_SSI_VERIFIED_CHAIN: + { + *lppInfo = (LPVOID)(SSL_get0_verified_chain(m_ssl)); + } + break; + } + + return TRUE; +} + +CSSLSession* CSSLSessionPool::PickFreeSession(LPCSTR lpszHostName) +{ + DWORD dwIndex; + CSSLSession* pSession = nullptr; + + if(m_lsFreeSession.TryLock(&pSession, dwIndex)) + { + if(::GetTimeGap32(pSession->GetFreeTime()) >= m_dwSessionLockTime) + VERIFY(m_lsFreeSession.ReleaseLock(nullptr, dwIndex)); + else + { + VERIFY(m_lsFreeSession.ReleaseLock(pSession, dwIndex)); + pSession = nullptr; + } + } + + if(!pSession) pSession = CSSLSession::Construct(m_itPool); + + ASSERT(pSession); + return pSession->Renew(m_sslCtx, lpszHostName); +} + +void CSSLSessionPool::PutFreeSession(CSSLSession* pSession) +{ + if(pSession->Reset()) + { +#ifndef USE_EXTERNAL_GC + ReleaseGCSession(); +#endif + if(!m_lsFreeSession.TryPut(pSession)) + m_lsGCSession.PushBack(pSession); + } +} + +void CSSLSessionPool::Prepare() +{ + m_itPool.Prepare(); + m_lsFreeSession.Reset(m_dwSessionPoolSize); +} + +void CSSLSessionPool::Clear() +{ + m_lsFreeSession.Clear(); + + ReleaseGCSession(TRUE); + VERIFY(m_lsGCSession.IsEmpty()); + + m_itPool.Clear(); +} + +void CSSLSessionPool::ReleaseGCSession(BOOL bForce) +{ + ::ReleaseGCObj(m_lsGCSession, m_dwSessionLockTime, bForce); +} + +#endif \ No newline at end of file diff --git a/SSLHelper.h b/SSLHelper.h new file mode 100644 index 0000000..ced52d4 --- /dev/null +++ b/SSLHelper.h @@ -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 CServerNameMap; + +public: + + /* + * 名称:初始化 SSL 环境参数 + * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 + * + * 参数: enSessionMode -- SSL 工作模式(参考 EnSSLSessionMode) + * iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpPemCert -- 证书文件(客户端可选) + * lpPemKey -- 私钥文件(客户端可选) + * lpKeyPasswod -- 私钥密码(没有密码则为空) + * lpCAPemCert -- CA 证书文件或目录(单向验证或客户端可选) + * fnServerNameCallback -- SNI 回调函数指针(可选,只用于服务端,如果为 nullptr 则使用 SNI 默认回调函数) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 ::GetLastError() 获取失败原因 + */ + BOOL Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode = SSL_VM_NONE, BOOL bMemory = FALSE, LPVOID lpPemCert = nullptr, LPVOID lpPemKey = nullptr, LPVOID lpKeyPasswod = nullptr, LPVOID lpCAPemCert = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr); + + /* + * 名称:增加 SNI 主机证书(只用于服务端) + * 描述:SSL 服务端在 Initialize() 成功后可以调用本方法增加多个 SNI 主机证书 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpPemCert -- 证书文件 + * lpPemKey -- 私钥文件 + * lpKeyPasswod -- 私钥密码(没有密码则为空) + * lpCAPemCert -- CA 证书文件或目录(单向验证可选) + * + * 返回值: 正数 -- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机 + * 负数 -- 失败,可通过 ::GetLastError() 获取失败原因 + */ + int AddServerContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod = nullptr, LPVOID lpCAPemCert = nullptr); + + /* + * 名称:绑定 SNI 主机域名 + * 描述:SSL 服务端在 AddServerContext() 成功后可以调用本方法绑定主机域名到 SNI 主机证书 + * + * 参数: lpszServerName -- 主机域名 + * iContextIndex -- SNI 主机证书对应的索引 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 ::GetLastError() 获取失败原因 + */ + virtual BOOL BindServerName(LPCTSTR lpszServerName, int iContextIndex); + + /* + * 名称:清理 SSL 运行环境 + * 描述:清理 SSL 运行环境,回收 SSL 相关内存 + * 1、CSSLContext 的析构函数会自动调用本方法 + * 2、当要重新设置 SSL 环境参数时,需要先调用本方法清理原先的环境参数 + * + * 参数: 无 + * + * 返回值:无 + */ + void Cleanup(); + + /* 获取 SSL 运行环境 SSL_CTX 对象 */ + SSL_CTX* GetContext (int i) const; + /* 获取 SSL 运行环境默认 SSL_CTX 对象 */ + SSL_CTX* GetDefaultContext () const {return m_sslCtx;} + /* 获取 SSL 运行环境的配置模式,配置模式参考:EnSSLSessionMode */ + EnSSLSessionMode GetSessionMode () const {return m_enSessionMode;} + /* 检查 SSL 运行环境是否初始化完成 */ + BOOL IsValid () const {return m_sslCtx != nullptr;} + + /* 设置 SSL 加密算法列表 */ + void SetCipherList(LPCTSTR lpszCipherList) {m_strCipherList = lpszCipherList;} + /* 获取 SSL 加密算法列表 */ + LPCTSTR GetCipherList() {return m_strCipherList;} + +public: + + /* + * 名称:清理线程局部环境 SSL 资源 + * 描述:任何一个操作 SSL 的线程,在通信结束时都需要清理线程局部环境 SSL 资源 + * 1、主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本方法 + * 2、特殊情况下,当自定义线程参与 HP-Socket 通信操作并检查到 SSL 内存泄漏时,需在每次通信结束时自定义线程调用本方法 + * + * 参数: dwThreadID -- 线程 ID(0:当前线程) + * + * 返回值:无 + */ + static void RemoveThreadLocalState(THR_ID dwThreadID = 0) {CSSLInitializer::CleanupThreadState(dwThreadID);} + +public: + + CSSLContext() + : m_strCipherList (DEFAULT_CIPHER_LIST) + , m_enSessionMode (SSL_SM_SERVER) + , m_sslCtx (nullptr) + , m_fnServerNameCallback(nullptr) + { + + } + + ~CSSLContext() {Cleanup();} + +private: + + void SetServerNameCallback(Fn_SNI_ServerNameCallback fn); + int AddContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert); + BOOL LoadCertAndKey(SSL_CTX* sslCtx, int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert); + BOOL LoadCertAndKeyByFile(SSL_CTX* sslCtx, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath); + BOOL LoadCertAndKeyByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert); + BOOL LoadCAPemCertByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszCAPemCert); + BOOL LoadPemCertAndKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword); + BOOL AddCAPemCertToStoreByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert); + BOOL SetClientCAListByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert); + BOOL SetPrivateKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemKey); + BOOL SetCertChainByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert); + +private: + + static int InternalServerNameCallback(SSL* ssl, int* ad, void* arg); + +public: + + /* + * 名称:SNI 默认回调函数 + * 描述:Initialize 方法中如果不指定 SNI 回调函数则使用此 SNI 默认回调函数 + * + * 参数: lpszServerName -- 请求域名 + * pContext -- SSL Context 对象 + * + * 返回值:SNI 主机证书对应的索引 + */ + static int __HP_CALL DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext); + +private: + + CString m_strCipherList; + EnSSLSessionMode m_enSessionMode; + CServerNameMap m_sslServerNames; + vector 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 TSSLSessionList; + typedef CCASQueue 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 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 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 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 \ No newline at end of file diff --git a/SSLServer.cpp b/SSLServer.cpp new file mode 100644 index 0000000..66dbafc --- /dev/null +++ b/SSLServer.cpp @@ -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 \ No newline at end of file diff --git a/SSLServer.h b/SSLServer.h new file mode 100644 index 0000000..70cb89b --- /dev/null +++ b/SSLServer.h @@ -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 \ No newline at end of file diff --git a/SocketHelper.cpp b/SocketHelper.cpp new file mode 100644 index 0000000..05b16f4 --- /dev/null +++ b/SocketHelper.cpp @@ -0,0 +1,1669 @@ +/* +* 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 "SocketHelper.h" + +#include +#include +#include +#include + +#ifdef _ICONV_SUPPORT +#include +#endif + +#ifndef SO_REUSEPORT + #define SO_REUSEPORT 15 +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const BYTE s_szUdpCloseNotify[] = {0xBE, 0xB6, 0x1F, 0xEB, 0xDA, 0x52, 0x46, 0xBA, 0x92, 0x33, 0x59, 0xDB, 0xBF, 0xE6, 0xC8, 0xE4}; +static const int s_iUdpCloseNotifySize = ARRAY_SIZE(s_szUdpCloseNotify); + +const hp_addr hp_addr::ANY_ADDR4(AF_INET, TRUE); +const hp_addr hp_addr::ANY_ADDR6(AF_INET6, TRUE); + +BOOL SetCurrentWorkerThreadName() +{ + return SetWorkerThreadDefaultName(0); +} + +BOOL SetWorkerThreadDefaultName(THR_ID tid) +{ + static volatile UINT _s_uiSeq = MAXUINT; + + return ::SetSequenceThreadName(tid, DEFAULT_WORKER_THREAD_PREFIX, _s_uiSeq); +} + +LPCTSTR GetSocketErrorDesc(EnSocketError enCode) +{ + switch(enCode) + { + case SE_OK: return _T("SUCCESS"); + case SE_ILLEGAL_STATE: return _T("Illegal State"); + case SE_INVALID_PARAM: return _T("Invalid Parameter"); + case SE_SOCKET_CREATE: return _T("Create SOCKET Fail"); + case SE_SOCKET_BIND: return _T("Bind SOCKET Fail"); + case SE_SOCKET_PREPARE: return _T("Prepare SOCKET Fail"); + case SE_SOCKET_LISTEN: return _T("Listen SOCKET Fail"); + case SE_CP_CREATE: return _T("Create IOCP Fail"); + case SE_WORKER_THREAD_CREATE: return _T("Create Worker Thread Fail"); + case SE_DETECT_THREAD_CREATE: return _T("Create Detector Thread Fail"); + case SE_SOCKE_ATTACH_TO_CP: return _T("Attach SOCKET to IOCP Fail"); + case SE_CONNECT_SERVER: return _T("Connect to Server Fail"); + case SE_NETWORK: return _T("Network Error"); + case SE_DATA_PROC: return _T("Process Data Error"); + case SE_DATA_SEND: return _T("Send Data Fail"); + case SE_GC_START: return _T("Start GC Fail"); + + case SE_SSL_ENV_NOT_READY: return _T("SSL environment not ready"); + + default: ASSERT(FALSE); return _T("UNKNOWN ERROR"); + } +} + +ADDRESS_FAMILY DetermineAddrFamily(LPCTSTR lpszAddress) +{ + if (!lpszAddress || lpszAddress[0] == 0) + return AF_UNSPEC; + + if(::StrChr(lpszAddress, IPV6_ADDR_SEPARATOR_CHAR)) + return AF_INET6; + + TCHAR c; + int arr[4]; + + if(stscanf(lpszAddress, _T("%d.%d.%d.%d%c"), &arr[0], &arr[1], &arr[2], &arr[3], &c) != 4) + return AF_UNSPEC; + + for(int i = 0; i < 4; i++) + { + if(arr[i] < 0 || arr[i] > 255) + return AF_UNSPEC; + } + + return AF_INET; +} + +BOOL GetInAddr(LPCTSTR lpszAddress, HP_ADDR& addr) +{ + addr.family = DetermineAddrFamily(lpszAddress); + + if (addr.family == AF_UNSPEC) + return FALSE; + + return (::InetPton(addr.family, lpszAddress, addr.Addr()) == TRUE); +} + +BOOL GetSockAddr(LPCTSTR lpszAddress, USHORT usPort, HP_SOCKADDR& addr) +{ + if(addr.family != AF_INET && addr.family != AF_INET6) + { + ::WSASetLastError(ERROR_ADDRNOTAVAIL); + return FALSE; + } + + if(addr.family == AF_INET6 && StrChr(lpszAddress, IPV6_ZONE_INDEX_CHAR)) + return GetSockAddrByHostNameDirectly(lpszAddress, usPort, addr); + + addr.ZeroAddr(); + + int rs = ::InetPton(addr.family, lpszAddress, addr.SinAddr()); + + if(rs != 1) + { + if(rs == 0) ::WSASetLastError(ERROR_INVALID_PARAMETER); + + return FALSE; + } + + if(usPort != 0) + addr.SetPort(usPort); + + return TRUE; +} + +BOOL IsIPAddress(LPCTSTR lpszAddress, EnIPAddrType* penType) +{ + HP_ADDR addr; + + BOOL isOK = GetInAddr(lpszAddress, addr); + + if(isOK && penType) + *penType = addr.IsIPv4() ? IPT_IPV4 : IPT_IPV6; + + return isOK; +} + +BOOL GetIPAddress(LPCTSTR lpszHost, LPTSTR lpszIP, int& iIPLen, EnIPAddrType& enType) +{ + HP_SOCKADDR addr; + + if(!GetSockAddrByHostName(lpszHost, 0, addr)) + return FALSE; + + enType = addr.IsIPv4() ? IPT_IPV4 : IPT_IPV6; + + USHORT usPort; + ADDRESS_FAMILY usFamily; + return sockaddr_IN_2_A(addr, usFamily, lpszIP, iIPLen, usPort); +} + +BOOL GetSockAddrByHostName(LPCTSTR lpszHost, USHORT usPort, HP_SOCKADDR& addr) +{ + addr.family = DetermineAddrFamily(lpszHost); + + if(addr.family != AF_UNSPEC) + return GetSockAddr(lpszHost, usPort, addr); + + return GetSockAddrByHostNameDirectly(lpszHost, usPort, addr); +} + +BOOL GetSockAddrByHostNameDirectly(LPCTSTR lpszHost, USHORT usPort, HP_SOCKADDR& addr) +{ + addr.ZeroAddr(); + + addrinfo* pInfo = nullptr; + addrinfo hints = {0}; + +#if defined(__ANDROID__) + hints.ai_flags = 0; +#else + hints.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG); +#endif + hints.ai_family = addr.family; + hints.ai_socktype = SOCK_STREAM; + + int rs = ::getaddrinfo(CT2A(lpszHost), nullptr, &hints, &pInfo); + + if(!IS_NO_ERROR(rs)) + { + ::WSASetLastError(ERROR_HOSTUNREACH); + return FALSE; + } + + BOOL isOK = FALSE; + + for(addrinfo* pCur = pInfo; pCur != nullptr; pCur = pCur->ai_next) + { + if(pCur->ai_family == AF_INET || pCur->ai_family == AF_INET6) + { + memcpy(addr.Addr(), pCur->ai_addr, pCur->ai_addrlen); + isOK = TRUE; + + break; + } + } + + EXECUTE_RESTORE_ERROR(::freeaddrinfo(pInfo)); + + if(isOK) + addr.SetPort(usPort); + else + ::WSASetLastError(ERROR_HOSTUNREACH); + + return isOK; +} + +BOOL EnumHostIPAddresses(LPCTSTR lpszHost, EnIPAddrType enType, LPTIPAddr** lpppIPAddr, int& iIPAddrCount) +{ + *lpppIPAddr = nullptr; + iIPAddrCount = 0; + + ADDRESS_FAMILY usFamily = (enType == IPT_ALL ? + AF_UNSPEC : (enType == IPT_IPV4 ? + AF_INET : (enType == IPT_IPV6 ? + AF_INET6 : 0xFF))); + + if(usFamily == 0xFF) + { + ::WSASetLastError(ERROR_AFNOSUPPORT); + return FALSE; + } + + vector vt; + + ADDRESS_FAMILY usFamily2 = DetermineAddrFamily(lpszHost); + + if(usFamily2 != AF_UNSPEC) + { + if(usFamily != AF_UNSPEC && usFamily != usFamily2) + { + ::WSASetLastError(ERROR_HOSTUNREACH); + return FALSE; + } + + HP_SOCKADDR addr(usFamily2); + + if(!GetSockAddr(lpszHost, 0, addr)) + return FALSE; + + vt.emplace_back(&addr); + + return RetrieveSockAddrIPAddresses(vt, lpppIPAddr, iIPAddrCount); + } + + addrinfo* pInfo = nullptr; + addrinfo hints = {0}; + +#if defined(__ANDROID__) + hints.ai_flags = 0; +#else + hints.ai_flags = AI_ALL; +#endif + hints.ai_family = usFamily; + hints.ai_socktype = SOCK_STREAM; + + int rs = ::getaddrinfo(CT2A(lpszHost), nullptr, &hints, &pInfo); + + if(rs != NO_ERROR) + { + ::WSASetLastError(rs); + return FALSE; + } + + for(addrinfo* pCur = pInfo; pCur != nullptr; pCur = pCur->ai_next) + { + if(pCur->ai_family == AF_INET || pCur->ai_family == AF_INET6) + vt.emplace_back((HP_PSOCKADDR)pCur->ai_addr); + } + + BOOL isOK = RetrieveSockAddrIPAddresses(vt, lpppIPAddr, iIPAddrCount); + + ::freeaddrinfo(pInfo); + + if(!isOK) ::WSASetLastError(EHOSTUNREACH); + + return isOK; +} + +BOOL RetrieveSockAddrIPAddresses(const vector& vt, LPTIPAddr** lpppIPAddr, int& iIPAddrCount) +{ + iIPAddrCount = (int)vt.size(); + + if(iIPAddrCount == 0) return FALSE; + + HP_PSOCKADDR pSockAddr; + ADDRESS_FAMILY usFamily; + USHORT usPort; + int iAddrLength; + LPTSTR lpszAddr; + LPTIPAddr lpItem; + + (*lpppIPAddr) = new LPTIPAddr[iIPAddrCount + 1]; + (*lpppIPAddr)[iIPAddrCount] = nullptr; + + for(int i = 0; i < iIPAddrCount; i++) + { + pSockAddr = vt[i]; + iAddrLength = HP_SOCKADDR::AddrMinStrLength(pSockAddr->family); + lpszAddr = new TCHAR[iAddrLength]; + + VERIFY(sockaddr_IN_2_A(*vt[i], usFamily, lpszAddr, iAddrLength, usPort)); + + lpItem = new TIPAddr; + lpItem->type = pSockAddr->IsIPv4() ? IPT_IPV4 : IPT_IPV6; + lpItem->address = lpszAddr; + + (*lpppIPAddr)[i] = lpItem; + } + + return TRUE; +} + +BOOL FreeHostIPAddresses(LPTIPAddr* lppIPAddr) +{ + if(!lppIPAddr) return FALSE; + + LPTIPAddr p; + LPTIPAddr* lppCur = lppIPAddr; + + while((p = *lppCur++) != nullptr) + { + delete[] p->address; + delete p; + } + + delete[] lppIPAddr; + + return TRUE; +} + +BOOL sockaddr_IN_2_A(const HP_SOCKADDR& addr, ADDRESS_FAMILY& usFamily, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort) +{ + BOOL isOK = FALSE; + + usFamily = addr.family; + usPort = addr.Port(); + + if(::InetNtop(addr.family, addr.SinAddr(), lpszAddress, iAddressLen)) + { + iAddressLen = (int)lstrlen(lpszAddress) + 1; + isOK = TRUE; + } + else + { + if(::WSAGetLastError() == ENOSPC) + iAddressLen = HP_SOCKADDR::AddrMinStrLength(usFamily); + } + + return isOK; +} + +BOOL sockaddr_A_2_IN(LPCTSTR lpszAddress, USHORT usPort, HP_SOCKADDR& addr) +{ + addr.family = DetermineAddrFamily(lpszAddress); + return GetSockAddr(lpszAddress, usPort, addr); +} + +BOOL GetSocketAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort, BOOL bLocal) +{ + HP_SOCKADDR addr; + + int addr_len = addr.AddrSize(); + int result = bLocal ? getsockname(socket, addr.Addr(), (socklen_t*)&addr_len) : getpeername(socket, addr.Addr(), (socklen_t*)&addr_len); + + if(result != NO_ERROR) + return FALSE; + + ADDRESS_FAMILY usFamily; + return sockaddr_IN_2_A(addr, usFamily, lpszAddress, iAddressLen, usPort); +} + +BOOL GetSocketLocalAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort) +{ + return GetSocketAddress(socket, lpszAddress, iAddressLen, usPort, TRUE); +} + +BOOL GetSocketRemoteAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort) +{ + return GetSocketAddress(socket, lpszAddress, iAddressLen, usPort, FALSE); +} + +BOOL SetMultiCastSocketOptions(SOCKET sock, const HP_SOCKADDR& bindAddr, const HP_SOCKADDR& castAddr, int iMCTtl, BOOL bMCLoop) +{ + if(castAddr.IsIPv4()) + { + BYTE ttl = (BYTE)iMCTtl; + BYTE loop = (BYTE)bMCLoop; + + VERIFY(::SSO_SetSocketOption(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) != SOCKET_ERROR); + VERIFY(::SSO_SetSocketOption(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) != SOCKET_ERROR); + + ip_mreq mcast; + ::ZeroMemory(&mcast, sizeof(mcast)); + + mcast.imr_multiaddr = castAddr.addr4.sin_addr; + mcast.imr_interface = bindAddr.addr4.sin_addr; + + if(::SSO_SetSocketOption(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast, sizeof(mcast)) == SOCKET_ERROR) + return FALSE; + if(::SSO_SetSocketOption(sock, IPPROTO_IP, IP_MULTICAST_IF, bindAddr.SinAddr(), sizeof(IN_ADDR)) == SOCKET_ERROR) + return FALSE; + } + else + { + INT ttl = (INT)iMCTtl; + UINT loop = (UINT)bMCLoop; + + VERIFY(::SSO_SetSocketOption(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) != SOCKET_ERROR); + VERIFY(::SSO_SetSocketOption(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) != SOCKET_ERROR); + + ipv6_mreq mcast; + ::ZeroMemory(&mcast, sizeof(mcast)); + + mcast.ipv6mr_multiaddr = castAddr.addr6.sin6_addr; + mcast.ipv6mr_interface = bindAddr.addr6.sin6_scope_id; + + if(::SSO_SetSocketOption(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mcast, sizeof(mcast)) == SOCKET_ERROR) + return FALSE; + if(::SSO_SetSocketOption(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (PVOID)(&bindAddr.addr6.sin6_scope_id), sizeof(UINT)) == SOCKET_ERROR) + return FALSE; + } + + return TRUE; +} + +int WaitForSocketWrite(SOCKET sock, DWORD dwTimeout) +{ + timeval tv = {(__time_t)(dwTimeout / 1000), (__suseconds_t)((dwTimeout % 1000) * 1000)}; + + fd_set wfds, efds; + FD_ZERO(&wfds); + FD_ZERO(&efds); + FD_SET(sock, &wfds); + FD_SET(sock, &efds); + + int rs = NO_EINTR_INT(select(sock + 1, nullptr, &wfds, &efds, &tv)); + + if(rs <= 0) return ((rs == 0) ? ERROR_TIMEOUT : ENSURE_ERROR(ERROR_CANT_WAIT)); + + if(FD_ISSET(sock, &efds)) + { + rs = SSO_GetError(sock); + return ((rs != NO_ERROR && rs != SOCKET_ERROR) ? rs : ENSURE_ERROR(ERROR_CANT_WAIT)); + } + + VERIFY(FD_ISSET(sock, &wfds)); + + rs = SSO_GetError(sock); + + if(!IS_NO_ERROR(rs)) + return ((rs != SOCKET_ERROR) ? rs : ENSURE_ERROR(ERROR_CANT_WAIT)); + + return NO_ERROR; +} + +ULONGLONG NToH64(ULONGLONG value) +{ + return (((ULONGLONG)ntohl((UINT)((value << 32) >> 32))) << 32) | ntohl((UINT)(value >> 32)); +} + +ULONGLONG HToN64(ULONGLONG value) +{ + return (((ULONGLONG)htonl((UINT)((value << 32) >> 32))) << 32) | htonl((UINT)(value >> 32)); +} + +BOOL IsLittleEndian() +{ + static const USHORT _s_endian_test_value = 0x0102; + static const BOOL _s_bLE = (*((BYTE*)&_s_endian_test_value) == 0x02); + + return _s_bLE; +} + +USHORT HToLE16(USHORT value) +{ + return IsLittleEndian() ? value : ENDIAN_SWAP_16(value); +} + +USHORT HToBE16(USHORT value) +{ + return IsLittleEndian() ? ENDIAN_SWAP_16(value) : value; +} + +DWORD HToLE32(DWORD value) +{ + return IsLittleEndian() ? value : ENDIAN_SWAP_32(value); +} + +DWORD HToBE32(DWORD value) +{ + return IsLittleEndian() ? ENDIAN_SWAP_32(value) : value; +} + +HRESULT ReadSmallFile(LPCTSTR lpszFileName, CFile& file, CFileMapping& fmap, DWORD dwMaxFileSize) +{ + ASSERT(lpszFileName != nullptr); + + if(file.Open(lpszFileName, O_RDONLY)) + { + SIZE_T dwSize; + if(file.GetSize(dwSize)) + { + if(dwSize > 0 && dwSize <= dwMaxFileSize) + { + if(fmap.Map(file, dwSize)) + return NO_ERROR; + } + else if(dwSize == 0) + ::SetLastError(ERROR_EMPTY); + else + ::SetLastError(ERROR_FILE_TOO_LARGE); + } + } + + HRESULT rs = ::GetLastError(); + + return (!IS_NO_ERROR(rs) ? rs : ERROR_UNKNOWN); +} + +HRESULT MakeSmallFilePackage(LPCTSTR lpszFileName, CFile& file, CFileMapping& fmap, WSABUF szBuf[3], const LPWSABUF pHead, const LPWSABUF pTail) +{ + DWORD dwMaxFileSize = MAX_SMALL_FILE_SIZE - (pHead ? pHead->len : 0) - (pTail ? pTail->len : 0); + ASSERT(dwMaxFileSize <= MAX_SMALL_FILE_SIZE); + + HRESULT hr = ReadSmallFile(lpszFileName, file, fmap, dwMaxFileSize); + + if(IS_NO_ERROR(hr)) + { + szBuf[1].len = (UINT)fmap.Size(); + szBuf[1].buf = fmap; + + if(pHead) memcpy(&szBuf[0], pHead, sizeof(WSABUF)); + else memset(&szBuf[0], 0, sizeof(WSABUF)); + + if(pTail) memcpy(&szBuf[2], pTail, sizeof(WSABUF)); + else memset(&szBuf[2], 0, sizeof(WSABUF)); + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +int SSO_SetSocketOption(SOCKET sock, int level, int name, LPVOID val, int len) +{ + return setsockopt(sock, level, name, val, (socklen_t)len); +} + +int SSO_GetSocketOption(SOCKET sock, int level, int name, LPVOID val, int* len) +{ + return getsockopt(sock, level, name, val, (socklen_t*)len); +} + +int SSO_IoctlSocket(SOCKET sock, long cmd, PVOID arg) +{ + return ioctl(sock, cmd, arg); +} + +int SSO_NoBlock(SOCKET sock, BOOL bNoBlock) +{ + return fcntl_SETFL(sock, O_NONBLOCK, bNoBlock) ? NO_ERROR : SOCKET_ERROR; +} + +int SSO_NoDelay(SOCKET sock, BOOL bNoDelay) +{ + int val = bNoDelay ? 1 : 0; + return setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int)); +} + +int SSO_DontLinger(SOCKET sock, BOOL bDont) +{ + return SSO_Linger(sock, 0, 0); +} + +int SSO_Linger(SOCKET sock, int l_onoff, int l_linger) +{ + linger ln = {l_onoff, l_linger}; + return setsockopt(sock, SOL_SOCKET, SO_LINGER, &ln, sizeof(linger)); +} + +int SSO_KeepAlive(SOCKET sock, BOOL bKeepAlive) +{ + int val = bKeepAlive ? 1 : 0; + return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(int)); +} + +int SSO_KeepAliveVals(SOCKET sock, BOOL bOnOff, DWORD dwIdle, DWORD dwInterval, DWORD dwCount) +{ + if(bOnOff) + { + dwIdle /= 1000; + dwInterval /= 1000; + + if(dwIdle == 0 || dwInterval == 0 || dwCount == 0) + { + ::WSASetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + } + + BOOL isOK = IS_NO_ERROR(SSO_KeepAlive(sock, bOnOff)); + + if(isOK && bOnOff) + { + isOK &= IS_NO_ERROR(setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &dwIdle, sizeof(DWORD))); + isOK &= IS_NO_ERROR(setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &dwInterval, sizeof(DWORD))); + isOK &= IS_NO_ERROR(setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &dwCount, sizeof(DWORD))); + } + + return isOK ? NO_ERROR : SOCKET_ERROR; +} + +int SSO_ReuseAddress(SOCKET sock, EnReuseAddressPolicy opt) +{ + int iSet = 1; + int iUnSet = 0; + int rs = NO_ERROR; + + BOOL bReusePortSupported = +#if defined(__linux) || defined(__linux__) + ::IsKernelVersionAbove(2, 6, 32); +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__APPLE__) || defined(__MACH__) + TRUE; +#else + FALSE; +#endif + + if(opt == RAP_NONE) + { + rs = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &iUnSet, sizeof(int)); + if(bReusePortSupported) + rs |= setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &iUnSet, sizeof(int)); + } + else if(opt == RAP_ADDR_ONLY) + { + rs = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &iSet, sizeof(int)); + if(bReusePortSupported) + rs |= setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &iUnSet, sizeof(int)); + } + else if(opt == RAP_ADDR_AND_PORT) + { + rs = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &iSet, sizeof(int)); + if(bReusePortSupported) + rs |= setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &iSet, sizeof(int)); + } + else + { + ::SetLastError(ERROR_INVALID_PARAMETER); + rs = -1; + } + + return rs; +} + +int SSO_RecvBuffSize(SOCKET sock, int size) +{ + return setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(int)); +} + +int SSO_SendBuffSize(SOCKET sock, int size) +{ + return setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(int)); +} + +int SSO_RecvTimeOut(SOCKET sock, int ms) +{ + timeval tv; + ::MillisecondToTimeval(ms, tv); + + return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval)); +} + +int SSO_SendTimeOut(SOCKET sock, int ms) +{ + timeval tv; + ::MillisecondToTimeval(ms, tv); + + return setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval)); +} + +int SSO_GetError(SOCKET sock) +{ + int e; + socklen_t len = sizeof(e); + + if(IS_NO_ERROR(getsockopt(sock, SOL_SOCKET, SO_ERROR, &e, &len))) + return e; + + return SOCKET_ERROR; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +CONNID GenerateConnectionID() +{ + static volatile CONNID s_dwConnID = 0; + + CONNID dwConnID = ::InterlockedIncrement(&s_dwConnID); + + if(dwConnID == 0) + dwConnID = ::InterlockedIncrement(&s_dwConnID); + + return dwConnID; +} + +int IsUdpCloseNotify(const BYTE* pData, int iLength) +{ + return (iLength == s_iUdpCloseNotifySize && + memcmp(pData, s_szUdpCloseNotify, s_iUdpCloseNotifySize) == 0) ; +} + +int SendUdpCloseNotify(SOCKET sock) +{ + return (int)send(sock, (LPCSTR)s_szUdpCloseNotify, s_iUdpCloseNotifySize, 0); +} + +int SendUdpCloseNotify(SOCKET sock, const HP_SOCKADDR& remoteAddr) +{ + return (int)sendto(sock, (LPCSTR)s_szUdpCloseNotify, s_iUdpCloseNotifySize, 0, remoteAddr.Addr(), remoteAddr.AddrSize()); +} + +int ManualCloseSocket(SOCKET sock, int iShutdownFlag, BOOL bGraceful) +{ + if(!bGraceful) + SSO_Linger(sock, 1, 0); + + if(iShutdownFlag != 0xFF) + shutdown(sock, iShutdownFlag); + + return closesocket(sock); +} + +DWORD GuessBase64EncodeBound(DWORD dwSrcLen) +{ + return 4 * ((dwSrcLen + 2) / 3); +} + +DWORD GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) +{ + if(dwSrcLen < 2) + return 0; + + if(lpszSrc[dwSrcLen - 2] == '=') + dwSrcLen -= 2; + else if(lpszSrc[dwSrcLen - 1] == '=') + --dwSrcLen; + + DWORD dwMod = dwSrcLen % 4; + DWORD dwAdd = dwMod == 2 ? 1 : (dwMod == 3 ? 2 : 0); + + return 3 * (dwSrcLen / 4) + dwAdd; +} + +int Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + static const BYTE CODES[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + DWORD dwRealLen = GuessBase64EncodeBound(dwSrcLen); + + if(lpszDest == nullptr || dwDestLen < dwRealLen) + { + dwDestLen = dwRealLen; + return -5; + } + + BYTE* p = lpszDest; + DWORD leven = 3 * (dwSrcLen / 3); + DWORD i = 0; + + for (; i < leven; i += 3) + { + *p++ = CODES[lpszSrc[0] >> 2]; + *p++ = CODES[((lpszSrc[0] & 3) << 4) + (lpszSrc[1] >> 4)]; + *p++ = CODES[((lpszSrc[1] & 0xf) << 2) + (lpszSrc[2] >> 6)]; + *p++ = CODES[lpszSrc[2] & 0x3f]; + + lpszSrc += 3; + } + + if(i < dwSrcLen) + { + BYTE a = lpszSrc[0]; + BYTE b = (i + 1 < dwSrcLen) ? lpszSrc[1] : 0; + + *p++ = CODES[a >> 2]; + *p++ = CODES[((a & 3) << 4) + (b >> 4)]; + *p++ = (i + 1 < dwSrcLen) ? CODES[((b & 0xf) << 2)] : '='; + *p++ = '='; + } + + ASSERT(dwRealLen == (DWORD)(p - lpszDest)); + + if(dwDestLen > dwRealLen) + { + *p = 0; + dwDestLen = dwRealLen; + } + + return 0; +} + +int Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + static const BYTE MAP[256] = + { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, + 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255 + }; + + DWORD dwRealLen = GuessBase64DecodeBound(lpszSrc, dwSrcLen); + + if(lpszDest == nullptr || dwDestLen < dwRealLen) + { + dwDestLen = dwRealLen; + return -5; + } + + BYTE c; + int g = 3; + DWORD i, x, y, z; + + for(i = x = y = z = 0; i < dwSrcLen || x != 0;) + { + c = i < dwSrcLen ? MAP[lpszSrc[i++]] : 254; + + if(c == 255) {dwDestLen = 0; return -3;} + else if(c == 254) {c = 0; g--;} + else if(c == 253) continue; + + z = (z << 6) | c; + + if(++x == 4) + { + lpszDest[y++] = (BYTE)((z >> 16) & 255); + if (g > 1) lpszDest[y++] = (BYTE)((z >> 8) & 255); + if (g > 2) lpszDest[y++] = (BYTE)(z & 255); + + x = z = 0; + } + } + + BOOL isOK = (y == dwRealLen); + + if(!isOK) + dwDestLen = 0; + else + { + if(dwDestLen > dwRealLen) + { + lpszDest[dwRealLen] = 0; + dwDestLen = dwRealLen; + } + } + + return isOK ? 0 : -3; +} + +DWORD GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) +{ + DWORD dwAdd = 0; + + for(DWORD i = 0; i < dwSrcLen; i++) + { + BYTE c = lpszSrc[i]; + + if(!(isalnum(c) || c == ' ' || c == '.' || c == '-' || c == '_' || c == '*')) + dwAdd += 2; + } + + return dwSrcLen + dwAdd; +} + +DWORD GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen) +{ + DWORD dwPercent = 0; + + for(DWORD i = 0; i < dwSrcLen; i++) + { + if(lpszSrc[i] == '%') + { + ++dwPercent; + i += 2; + } + } + + DWORD dwSub = dwPercent * 2; + + if(dwSrcLen < dwSub) + return 0; + + return dwSrcLen - dwSub; +} + +int UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + BYTE c; + DWORD j = 0; + + if(lpszDest == nullptr || dwDestLen == 0) + goto ERROR_DEST_LEN; + + for(DWORD i = 0; i < dwSrcLen; i++) + { + if(j >= dwDestLen) + goto ERROR_DEST_LEN; + + c = lpszSrc[i]; + + if (isalnum(c) || c == '.' || c == '-' || c == '_' || c == '*') + lpszDest[j++] = c; + else if(c == ' ') + lpszDest[j++] = '+'; + else + { + if(j + 3 >= dwDestLen) + goto ERROR_DEST_LEN; + + lpszDest[j++] = '%'; + HEX_VALUE_TO_DOUBLE_CHAR(lpszDest + j, c); + j += 2; + + } + } + + if(dwDestLen > j) + { + lpszDest[j] = 0; + dwDestLen = j; + } + + return 0; + +ERROR_DEST_LEN: + dwDestLen = GuessUrlEncodeBound(lpszSrc, dwSrcLen); + return -5; +} + +int UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + char c; + DWORD j = 0; + + if(lpszDest == nullptr || dwDestLen == 0) + goto ERROR_DEST_LEN; + + for(DWORD i = 0; i < dwSrcLen; i++) + { + if(j >= dwDestLen) + goto ERROR_DEST_LEN; + + c = lpszSrc[i]; + + if(c == '+') + lpszDest[j++] = ' '; + else if(c != '%') + lpszDest[j++] = c; + else + { + if(i + 2 >= dwSrcLen) + goto ERROR_SRC_DATA; + + lpszDest[j++] = HEX_DOUBLE_CHAR_TO_VALUE(lpszSrc + i + 1); + i += 2; + } + } + + if(dwDestLen > j) + { + lpszDest[j] = 0; + dwDestLen = j; + } + + return 0; + +ERROR_SRC_DATA: + dwDestLen = 0; + return -3; + +ERROR_DEST_LEN: + dwDestLen = GuessUrlDecodeBound(lpszSrc, dwSrcLen); + return -5; +} + +void DestroyCompressor(IHPCompressor* pCompressor) +{ + delete pCompressor; +} + +void DestroyDecompressor(IHPDecompressor* pDecompressor) +{ + delete pDecompressor; +} + +#ifdef _ZLIB_SUPPORT + +CHPZLibCompressor::CHPZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize) +: m_fnCallback (fnCallback) +, m_dwBuffSize (dwBuffSize) +, m_bValid (FALSE) +{ + ASSERT(m_fnCallback != nullptr); + + ::ZeroObject(m_Stream); + + m_bValid = (::deflateInit2(&m_Stream, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy) == Z_OK); +} +CHPZLibCompressor::~CHPZLibCompressor() +{ + if(m_bValid) ::deflateEnd(&m_Stream); +} + +BOOL CHPZLibCompressor::Reset() +{ + return (m_bValid = (::deflateReset(&m_Stream) == Z_OK)); +} + +BOOL CHPZLibCompressor::Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext) +{ + return ProcessEx(pData, iLength, bLast, FALSE, pContext); +} + +BOOL CHPZLibCompressor::ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush, PVOID pContext) +{ + ASSERT(IsValid() && iLength > 0); + + if(!IsValid()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + unique_ptr szBuff = make_unique(m_dwBuffSize); + + m_Stream.next_in = (z_const Bytef*)pData; + m_Stream.avail_in = iLength; + + BOOL isOK = TRUE; + int rs = Z_OK; + int flush = bLast ? Z_FINISH : (bFlush ? Z_SYNC_FLUSH : Z_NO_FLUSH); + + while(m_Stream.avail_in > 0) + { + do + { + m_Stream.next_out = szBuff.get(); + m_Stream.avail_out = m_dwBuffSize; + + rs = ::deflate(&m_Stream, flush); + + if(rs == Z_STREAM_ERROR) + { + ::SetLastError(ERROR_INVALID_DATA); + isOK = FALSE; + + goto ZLIB_COMPRESS_END; + } + + int iRead = (int)(m_dwBuffSize - m_Stream.avail_out); + + if(iRead == 0) + break; + + if(!m_fnCallback(szBuff.get(), iRead, pContext)) + { + ::SetLastError(ERROR_CANCELLED); + isOK = FALSE; + + goto ZLIB_COMPRESS_END; + } + } while(m_Stream.avail_out == 0); + } + +ZLIB_COMPRESS_END: + + ASSERT(!isOK || (rs == Z_OK && !bLast) || (rs == Z_STREAM_END && bLast)); + + if(!isOK || bLast) Reset(); + + return isOK; +} + +CHPZLibDecompressor::CHPZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits, DWORD dwBuffSize) +: m_fnCallback (fnCallback) +, m_dwBuffSize (dwBuffSize) +, m_bValid (FALSE) +{ + ASSERT(m_fnCallback != nullptr); + + ::ZeroObject(m_Stream); + + m_bValid = (::inflateInit2(&m_Stream, iWindowBits) == Z_OK); +} +CHPZLibDecompressor::~CHPZLibDecompressor() +{ + if(m_bValid) ::inflateEnd(&m_Stream); +} + +BOOL CHPZLibDecompressor::Reset() +{ + return (m_bValid = (::inflateReset(&m_Stream) == Z_OK)); +} + +BOOL CHPZLibDecompressor::Process(const BYTE* pData, int iLength, PVOID pContext) +{ + ASSERT(IsValid() && iLength > 0); + + if(!IsValid()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + unique_ptr szBuff = make_unique(m_dwBuffSize); + + m_Stream.next_in = (z_const Bytef*)pData; + m_Stream.avail_in = iLength; + + BOOL isOK = TRUE; + int rs = Z_OK; + + while(m_Stream.avail_in > 0) + { + do + { + m_Stream.next_out = szBuff.get(); + m_Stream.avail_out = m_dwBuffSize; + + rs = ::inflate(&m_Stream, Z_NO_FLUSH); + + if(rs != Z_OK && rs != Z_STREAM_END) + { + ::SetLastError(ERROR_INVALID_DATA); + isOK = FALSE; + + goto ZLIB_DECOMPRESS_END; + } + + int iRead = (int)(m_dwBuffSize - m_Stream.avail_out); + + if(iRead == 0) + break; + + if(!m_fnCallback(szBuff.get(), iRead, pContext)) + { + ::SetLastError(ERROR_CANCELLED); + isOK = FALSE; + + goto ZLIB_DECOMPRESS_END; + } + } while(m_Stream.avail_out == 0); + + if(rs == Z_STREAM_END) + break; + } + +ZLIB_DECOMPRESS_END: + + ASSERT(!isOK || rs == Z_OK || rs == Z_STREAM_END); + + if(!isOK || rs == Z_STREAM_END) Reset(); + + return isOK; +} + +IHPCompressor* CreateZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize) +{ + return new CHPZLibCompressor(fnCallback, iWindowBits, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize); +} + +IHPCompressor* CreateGZipCompressor(Fn_CompressDataCallback fnCallback, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize) +{ + return new CHPZLibCompressor(fnCallback, MAX_WBITS + 16, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize); +} + +IHPDecompressor* CreateZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits, DWORD dwBuffSize) +{ + return new CHPZLibDecompressor(fnCallback, iWindowBits, dwBuffSize); +} + +IHPDecompressor* CreateGZipDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize) +{ + return new CHPZLibDecompressor(fnCallback, MAX_WBITS + 32, dwBuffSize); +} + +int Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + return CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen); +} + +int CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel, int iMethod, int iWindowBits, int iMemLevel, int iStrategy) +{ + z_stream stream; + + stream.next_in = (z_const Bytef*)lpszSrc; + stream.avail_in = dwSrcLen; + stream.next_out = lpszDest; + stream.avail_out = dwDestLen; + stream.zalloc = nullptr; + stream.zfree = nullptr; + stream.opaque = nullptr; + + int err = ::deflateInit2(&stream, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy); + + if(err != Z_OK) return err; + + err = ::deflate(&stream, Z_FINISH); + + if(err != Z_STREAM_END) + { + ::deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + + if(dwDestLen > stream.total_out) + { + lpszDest[stream.total_out] = 0; + dwDestLen = (DWORD)stream.total_out; + } + + return ::deflateEnd(&stream); +} + +int Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + return UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen); +} + +int UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits) +{ + z_stream stream; + + stream.next_in = (z_const Bytef*)lpszSrc; + stream.avail_in = (uInt)dwSrcLen; + stream.next_out = lpszDest; + stream.avail_out = dwDestLen; + stream.zalloc = nullptr; + stream.zfree = nullptr; + + int err = ::inflateInit2(&stream, iWindowBits); + + if(err != Z_OK) return err; + + err = ::inflate(&stream, Z_FINISH); + + if(err != Z_STREAM_END) + { + ::inflateEnd(&stream); + return (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) ? Z_DATA_ERROR : err; + } + + if(dwDestLen > stream.total_out) + { + lpszDest[stream.total_out] = 0; + dwDestLen = (DWORD)stream.total_out; + } + + return inflateEnd(&stream); +} + +DWORD GuessCompressBound(DWORD dwSrcLen, BOOL bGZip) +{ + DWORD dwBound = (DWORD)::compressBound(dwSrcLen); + + if(bGZip) dwBound += 16; + + return dwBound; +} + +int GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + return CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16); +} + +int GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + return UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, MAX_WBITS + 32); +} + +DWORD GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen) +{ + if(dwSrcLen < 20 || *(USHORT*)lpszSrc != 0x8B1F) + return 0; + + return *(DWORD*)(lpszSrc + dwSrcLen - 4); +} + +#endif + +#ifdef _BROTLI_SUPPORT + +CHPBrotliCompressor::CHPBrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality, int iWindow, int iMode, DWORD dwBuffSize) +: m_fnCallback (fnCallback) +, m_iQuality (iQuality) +, m_iWindow (iWindow) +, m_iMode (iMode) +, m_dwBuffSize (dwBuffSize) +, m_bValid (FALSE) +{ + ASSERT(m_fnCallback != nullptr); + + Reset(); +} + +CHPBrotliCompressor::~CHPBrotliCompressor() +{ + if(m_bValid) ::BrotliEncoderDestroyInstance(m_pState); +} + +BOOL CHPBrotliCompressor::Reset() +{ + if(m_bValid) ::BrotliEncoderDestroyInstance(m_pState); + m_pState = ::BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); + + if(m_pState != nullptr) + { + ::BrotliEncoderSetParameter(m_pState, BROTLI_PARAM_QUALITY , (UINT)m_iQuality); + ::BrotliEncoderSetParameter(m_pState, BROTLI_PARAM_LGWIN , (UINT)m_iWindow); + ::BrotliEncoderSetParameter(m_pState, BROTLI_PARAM_MODE , (UINT)m_iMode); + + if (m_iWindow > BROTLI_MAX_WINDOW_BITS) + ::BrotliEncoderSetParameter(m_pState, BROTLI_PARAM_LARGE_WINDOW, BROTLI_TRUE); + } + + return (m_bValid = (m_pState != nullptr)); +} + +BOOL CHPBrotliCompressor::Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext) +{ + return ProcessEx(pData, iLength, bLast, FALSE, pContext); +} + +BOOL CHPBrotliCompressor::ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush, PVOID pContext) +{ + ASSERT(IsValid() && iLength > 0); + + if(!IsValid()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + unique_ptr szBuff = make_unique(m_dwBuffSize); + + const BYTE* pNextInData = pData; + size_t iAvlInLen = (SIZE_T)iLength; + BYTE* pNextOutData = nullptr; + size_t iAvlOutLen = 0; + + BOOL isOK = TRUE; + BrotliEncoderOperation op = bLast ? BROTLI_OPERATION_FINISH : (bFlush ? BROTLI_OPERATION_FLUSH : BROTLI_OPERATION_PROCESS); + + while(iAvlInLen > 0) + { + do + { + pNextOutData = szBuff.get(); + iAvlOutLen = m_dwBuffSize; + + if(!::BrotliEncoderCompressStream(m_pState, op, &iAvlInLen, &pNextInData, &iAvlOutLen, &pNextOutData, nullptr)) + { + ::SetLastError(ERROR_INVALID_DATA); + isOK = FALSE; + + goto BROTLI_COMPRESS_END; + } + + int iRead = (int)(m_dwBuffSize - iAvlOutLen); + + if(iRead == 0) + break; + + if(!m_fnCallback(szBuff.get(), iRead, pContext)) + { + ::SetLastError(ERROR_CANCELLED); + isOK = FALSE; + + goto BROTLI_COMPRESS_END; + } + } while (iAvlOutLen == 0); + } + +BROTLI_COMPRESS_END: + + if(!isOK || bLast) Reset(); + + return isOK; +} + +CHPBrotliDecompressor::CHPBrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize) +: m_fnCallback (fnCallback) +, m_dwBuffSize (dwBuffSize) +, m_bValid (FALSE) +{ + ASSERT(m_fnCallback != nullptr); + + Reset(); +} + +CHPBrotliDecompressor::~CHPBrotliDecompressor() +{ + if(m_bValid) ::BrotliDecoderDestroyInstance(m_pState); +} + +BOOL CHPBrotliDecompressor::Reset() +{ + if(m_bValid) ::BrotliDecoderDestroyInstance(m_pState); + m_pState = ::BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + + return (m_bValid = (m_pState != nullptr)); +} + +BOOL CHPBrotliDecompressor::Process(const BYTE* pData, int iLength, PVOID pContext) +{ + ASSERT(IsValid() && iLength > 0); + + if(!IsValid()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + unique_ptr szBuff = make_unique(m_dwBuffSize); + + const BYTE* pNextInData = pData; + size_t iAvlInLen = (SIZE_T)iLength; + BYTE* pNextOutData = nullptr; + size_t iAvlOutLen = 0; + + BOOL isOK = TRUE; + BrotliDecoderResult rs = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + + do + { + do + { + pNextOutData = szBuff.get(); + iAvlOutLen = m_dwBuffSize; + + rs = ::BrotliDecoderDecompressStream(m_pState, &iAvlInLen, &pNextInData, &iAvlOutLen, &pNextOutData, nullptr); + + if(rs == BROTLI_DECODER_RESULT_ERROR) + { + ::SetLastError(ERROR_INVALID_DATA); + isOK = FALSE; + + goto BROTLI_DECOMPRESS_END; + } + + int iRead = (int)(m_dwBuffSize - iAvlOutLen); + + if(iRead == 0) + break; + + if(!m_fnCallback(szBuff.get(), iRead, pContext)) + { + ::SetLastError(ERROR_CANCELLED); + isOK = FALSE; + + goto BROTLI_DECOMPRESS_END; + } + } while (iAvlOutLen == 0); + + if(rs == BROTLI_DECODER_RESULT_SUCCESS) + break; + + } while(rs == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT); + +BROTLI_DECOMPRESS_END: + + if(!isOK || rs == BROTLI_DECODER_RESULT_SUCCESS) Reset(); + + return isOK; +} + +IHPCompressor* CreateBrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality, int iWindow, int iMode, DWORD dwBuffSize) +{ + return new CHPBrotliCompressor(fnCallback, iQuality, iWindow, iMode, dwBuffSize); +} + +IHPDecompressor* CreateBrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize) +{ + return new CHPBrotliDecompressor(fnCallback, dwBuffSize); +} + +int BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + return BrotliCompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE); +} + +int BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iQuality, int iWindow, int iMode) +{ + size_t stDestLen = (size_t)dwDestLen; + int rs = ::BrotliEncoderCompress(iQuality, iWindow, (BrotliEncoderMode)iMode, (size_t)dwSrcLen, lpszSrc, &stDestLen, lpszDest); + dwDestLen = (DWORD)stDestLen; + + return (rs == 1) ? 0 : ((rs == 3) ? -5 : -3); +} + +int BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen) +{ + size_t stDestLen = (size_t)dwDestLen; + BrotliDecoderResult rs = ::BrotliDecoderDecompress((size_t)dwSrcLen, lpszSrc, &stDestLen, lpszDest); + dwDestLen = (DWORD)stDestLen; + + return (rs == BROTLI_DECODER_RESULT_SUCCESS) ? 0 : ((rs == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) ? -5 : -3); +} + +DWORD BrotliGuessCompressBound(DWORD dwSrcLen) +{ + return (DWORD)::BrotliEncoderMaxCompressedSize((size_t)dwSrcLen); +} + +#endif + +#ifdef _ICONV_SUPPORT + +BOOL CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int& iOutBufLen) +{ + ASSERT(lpszInBuf != nullptr); + + SIZE_T nInBufLeft = iInBufLen; + SIZE_T nOutBufLeft = iOutBufLen; + int iOutBufSize = iOutBufLen; + iOutBufLen = 0; + + if(lpszInBuf == nullptr) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + iconv_t ic = iconv_open(lpszToCharset, lpszFromCharset); + + if(IS_INVALID_PVOID(ic)) + return FALSE; + + SIZE_T rs = iconv(ic, (LPSTR*)&lpszInBuf, &nInBufLeft, &lpszOutBuf, &nOutBufLeft); + iOutBufLen = iOutBufSize - (int)nOutBufLeft; + + EXECUTE_RESTORE_ERROR(iconv_close(ic)); + + return !IS_HAS_ERROR(rs); +} + +BOOL GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength) +{ + int iInBufLen = (int)((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? strlen(szSrc) + 1 : 0)); + int iOutBufLen = (int)(iDestLength * sizeof(WCHAR)); + + BOOL isOK = CharsetConvert(CHARSET_GBK, SYSTEM_CHARSET_UNICODE, szSrc, iInBufLen, (char*)szDest, iOutBufLen); + iDestLength = (int)(iOutBufLen / sizeof(WCHAR)); + + return isOK; +} + +BOOL UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength) +{ + int iInBufLen = (int)(((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? wcslen(szSrc) + 1 : 0)) * sizeof(WCHAR)); + + return CharsetConvert(SYSTEM_CHARSET_UNICODE, CHARSET_GBK, (LPCSTR)szSrc, iInBufLen, szDest, iDestLength); +} + +BOOL Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength) +{ + int iInBufLen = (int)((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? strlen(szSrc) + 1 : 0)); + int iOutBufLen = (int)(iDestLength * sizeof(WCHAR)); + + BOOL isOK = CharsetConvert(CHARSET_UTF_8, SYSTEM_CHARSET_UNICODE, szSrc, iInBufLen, (char*)szDest, iOutBufLen); + iDestLength = (int)(iOutBufLen / sizeof(WCHAR)); + + return isOK; +} + +BOOL UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength) +{ + int iInBufLen = (int)(((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? wcslen(szSrc) + 1 : 0)) * sizeof(WCHAR)); + + return CharsetConvert(SYSTEM_CHARSET_UNICODE, CHARSET_UTF_8, (LPCSTR)szSrc, iInBufLen, szDest, iDestLength); +} + +BOOL GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength) +{ + int iInBufLen = (int)((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? strlen(szSrc) + 1 : 0)); + + return CharsetConvert(CHARSET_GBK, CHARSET_UTF_8, szSrc, iInBufLen, szDest, iDestLength); +} + +BOOL Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength) +{ + int iInBufLen = (int)((iSrcLength > 0) ? iSrcLength : ((szSrc != nullptr) ? strlen(szSrc) + 1 : 0)); + + return CharsetConvert(CHARSET_UTF_8, CHARSET_GBK, szSrc, iInBufLen, szDest, iDestLength); +} + +BOOL GbkToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength) +{ + return GbkToUnicodeEx(szSrc, -1, szDest, iDestLength); +} + +BOOL UnicodeToGbk(const WCHAR szSrc[], char szDest[], int& iDestLength) +{ + return UnicodeToGbkEx(szSrc, -1, szDest, iDestLength); +} + +BOOL Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength) +{ + return Utf8ToUnicodeEx(szSrc, -1, szDest, iDestLength); +} + +BOOL UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int& iDestLength) +{ + return UnicodeToUtf8Ex(szSrc, -1, szDest, iDestLength); +} + +BOOL GbkToUtf8(const char szSrc[], char szDest[], int& iDestLength) +{ + return GbkToUtf8Ex(szSrc, -1, szDest, iDestLength); +} + +BOOL Utf8ToGbk(const char szSrc[], char szDest[], int& iDestLength) +{ + return Utf8ToGbkEx(szSrc, -1, szDest, iDestLength); +} + +#endif diff --git a/SocketHelper.h b/SocketHelper.h new file mode 100644 index 0000000..28e72bb --- /dev/null +++ b/SocketHelper.h @@ -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 +#include +#include +#include + +#ifdef _ZLIB_SUPPORT +#include +#endif + +#ifdef _BROTLI_SUPPORT +#include +#include +#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 TNodeBufferObjPtr; +typedef CNodePoolT CNodeBufferObjPool; +typedef CCASQueue CNodeRecvQueue; +typedef TItemListExT TNodeBufferObjList; + +typedef unique_ptr CNodeCriSecs; +typedef unique_ptr 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 ListenSocketsPtr; + +/* 数据缓冲节点 */ +typedef TItem TBufferObj; +/* 数据缓冲节点智能指针 */ +typedef TItemPtr TBufferObjPtr; +/* 数据缓冲区对象池 */ +typedef CItemPool CBufferObjPool; +/* 数据缓冲区链表模板 */ +typedef TItemListExV TBufferObjList; + +/* 接收缓冲区数组智能指针 */ +typedef unique_ptr CReceiveBuffersPtr; + +/* 线程 ID - 接收缓冲区哈希表 */ +typedef unordered_map 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 TSocketObjPtrPool; +/* 失效 TSocketObj 缓存 */ +typedef CRingPool TSocketObjPtrList; +/* 失效 TSocketObj 垃圾回收结构链表 */ +typedef CCASQueue TSocketObjPtrQueue; + +/* 有效 TSocketObj 缓存 */ +typedef CRingCache2 TAgentSocketObjPtrPool; +/* 失效 TSocketObj 缓存 */ +typedef CRingPool TAgentSocketObjPtrList; +/* 失效 TSocketObj 垃圾回收结构链表 */ +typedef CCASQueue TAgentSocketObjPtrQueue; + +/* 有效 TUdpSocketObj 缓存 */ +typedef CRingCache2 TUdpSocketObjPtrPool; +/* 失效 TUdpSocketObj 缓存 */ +typedef CRingPool TUdpSocketObjPtrList; +/* 失效 TUdpSocketObj 垃圾回收结构链表 */ +typedef CCASQueue 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 + 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& 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 diff --git a/SocketObject4C.h b/SocketObject4C.h new file mode 100644 index 0000000..500f5f0 --- /dev/null +++ b/SocketObject4C.h @@ -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 static inline HP_Object FromFirst(T* pFirst) + { + return (HP_Object)((char*)pFirst - first); + } + + template static inline T* ToFirst(HP_Object pObject) + { + return (T*)((char*)pObject + first); + } + + template static inline HP_Object FromSecond(T* pSecond) + { + return (C_HP_Object*)((char*)pSecond - first - (offset + __DUAL_VPTR_GAP__)); + } + + template 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 C_HP_ObjectT : private C_HP_Object, public T +{ +public: + C_HP_ObjectT(L* pListener) : C_HP_Object(offset), T(pListener) {} +}; + +template class C_HP_ServerListenerT : public L +{ +public: + virtual EnHandleResult OnPrepareListen(T* pSender, SOCKET soListen) + { + return (m_fnOnPrepareListen) + ? m_fnOnPrepareListen(C_HP_Object::FromSecond(pSender), soListen) + : HR_IGNORE; + } + + virtual EnHandleResult OnAccept(T* pSender, CONNID dwConnID, UINT_PTR soClient) + { + return (m_fnOnAccept) + ? m_fnOnAccept(C_HP_Object::FromSecond(pSender), dwConnID, soClient) + : HR_IGNORE; + } + + virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID) + { + return (m_fnOnHandShake) + ? m_fnOnHandShake(C_HP_Object::FromSecond(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(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(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(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(pSender), dwConnID, enOperation, iErrorCode) + : HR_IGNORE; + } + + virtual EnHandleResult OnShutdown(T* pSender) + { + return (m_fnOnShutdown) + ? m_fnOnShutdown(C_HP_Object::FromSecond(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 C_HP_AgentListenerT : public L +{ +public: + virtual EnHandleResult OnPrepareConnect(T* pSender, CONNID dwConnID, SOCKET socket) + { + return (m_fnOnPrepareConnect) + ? m_fnOnPrepareConnect(C_HP_Object::FromSecond(pSender), dwConnID, socket) + : HR_IGNORE; + } + + virtual EnHandleResult OnConnect(T* pSender, CONNID dwConnID) + { + return (m_fnOnConnect) + ? m_fnOnConnect(C_HP_Object::FromSecond(pSender), dwConnID) + : HR_IGNORE; + } + + virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID) + { + return (m_fnOnHandShake) + ? m_fnOnHandShake(C_HP_Object::FromSecond(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(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(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(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(pSender), dwConnID, enOperation, iErrorCode) + : HR_IGNORE; + } + + virtual EnHandleResult OnShutdown(T* pSender) + { + return (m_fnOnShutdown) + ? m_fnOnShutdown(C_HP_Object::FromSecond(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 C_HP_ClientListenerT : public L +{ +public: + virtual EnHandleResult OnPrepareConnect(T* pSender, CONNID dwConnID, SOCKET socket) + { + return (m_fnOnPrepareConnect) + ? m_fnOnPrepareConnect(C_HP_Object::FromSecond(pSender), dwConnID, socket) + : HR_IGNORE; + } + + virtual EnHandleResult OnConnect(T* pSender, CONNID dwConnID) + { + return (m_fnOnConnect) + ? m_fnOnConnect(C_HP_Object::FromSecond(pSender), dwConnID) + : HR_IGNORE; + } + + virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID) + { + return (m_fnOnHandShake) + ? m_fnOnHandShake(C_HP_Object::FromSecond(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(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(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(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(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 C_HP_TcpServerListener; +typedef C_HP_ServerListenerT C_HP_TcpPullServerListener; +typedef C_HP_ServerListenerT C_HP_TcpPackServerListener; + +typedef C_HP_AgentListenerT C_HP_TcpAgentListener; +typedef C_HP_AgentListenerT C_HP_TcpPullAgentListener; +typedef C_HP_AgentListenerT C_HP_TcpPackAgentListener; + +typedef C_HP_ClientListenerT C_HP_TcpClientListener; +typedef C_HP_ClientListenerT C_HP_TcpPullClientListener; +typedef C_HP_ClientListenerT C_HP_TcpPackClientListener; + +#ifdef _UDP_SUPPORT + +template class C_HP_UdpNodeListenerT : public L +{ +public: + virtual EnHandleResult OnPrepareListen(T* pSender, SOCKET soListen) + { + return (m_fnOnPrepareListen) + ? m_fnOnPrepareListen(C_HP_Object::FromSecond(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(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(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(pSender), enOperation, iErrorCode, lpszRemoteAddress, usRemotePort, pData, iLength) + : HR_IGNORE; + } + + virtual EnHandleResult OnShutdown(T* pSender) + { + return (m_fnOnShutdown) + ? m_fnOnShutdown(C_HP_Object::FromSecond(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 C_HP_UdpServerListener; +typedef C_HP_ClientListenerT C_HP_UdpClientListener; +typedef C_HP_ClientListenerT C_HP_UdpCastListener; +typedef C_HP_UdpNodeListenerT C_HP_UdpNodeListener; + +typedef C_HP_ServerListenerT C_HP_UdpArqServerListener; +typedef C_HP_ClientListenerT C_HP_UdpArqClientListener; + +#endif + +#ifdef _HTTP_SUPPORT + +template class C_HP_HttpListenerT : public IHttpListenerT +{ +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 C_HP_HttpServerBaseListener1; +typedef C_HP_HttpListenerT C_HP_HttpAgentBaseListener1; +typedef C_HP_HttpListenerT C_HP_HttpClientBaseListener1; + +typedef C_HP_ServerListenerT C_HP_HttpServerBaseListener2; +typedef C_HP_AgentListenerT C_HP_HttpAgentBaseListener2; +typedef C_HP_ClientListenerT 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; +}; diff --git a/TcpAgent.cpp b/TcpAgent.cpp new file mode 100644 index 0000000..e5bc700 --- /dev/null +++ b/TcpAgent.cpp @@ -0,0 +1,1306 @@ +/* + * 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 "TcpAgent.h" + +#include "./common/FileHelper.h" + +BOOL CTcpAgent::Start(LPCTSTR lpszBindAddress, BOOL bAsyncConnect) +{ + if(!CheckParams() || !CheckStarting()) + return FALSE; + + PrepareStart(); + + if(ParseBindAddress(lpszBindAddress)) + if(CreateWorkerThreads()) + { + m_bAsyncConnect = bAsyncConnect; + m_enState = SS_STARTED; + + return TRUE; + } + + EXECUTE_RESTORE_ERROR(Stop()); + + return FALSE; +} + +void CTcpAgent::SetLastError(EnSocketError code, LPCSTR func, int ec) +{ + m_enLastError = code; + ::SetLastError(ec); +} + +BOOL CTcpAgent::CheckParams() +{ + if ((m_enSendPolicy >= SP_PACK && m_enSendPolicy <= SP_DIRECT) && + (m_enOnSendSyncPolicy >= OSSP_NONE && m_enOnSendSyncPolicy <= OSSP_RECEIVE) && + ((int)m_dwSyncConnectTimeout > 0) && + ((int)m_dwMaxConnectionCount > 0 && m_dwMaxConnectionCount <= MAX_CONNECTION_COUNT) && + ((int)m_dwWorkerThreadCount > 0 && m_dwWorkerThreadCount <= MAX_WORKER_THREAD_COUNT) && + ((int)m_dwSocketBufferSize >= MIN_SOCKET_BUFFER_SIZE) && + ((int)m_dwFreeSocketObjLockTime >= 1000) && + ((int)m_dwFreeSocketObjPool >= 0) && + ((int)m_dwFreeBufferObjPool >= 0) && + ((int)m_dwFreeSocketObjHold >= 0) && + ((int)m_dwFreeBufferObjHold >= 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 CTcpAgent::PrepareStart() +{ + m_bfActiveSockets.Reset(m_dwMaxConnectionCount); + m_lsFreeSocket.Reset(m_dwFreeSocketObjPool); + + m_bfObjPool.SetItemCapacity(m_dwSocketBufferSize); + m_bfObjPool.SetPoolSize(m_dwFreeBufferObjPool); + m_bfObjPool.SetPoolHold(m_dwFreeBufferObjHold); + + m_bfObjPool.Prepare(); + + m_rcBuffers = make_unique(m_dwWorkerThreadCount); + for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwSocketBufferSize);}); +} + +BOOL CTcpAgent::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 CTcpAgent::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 CTcpAgent::ParseBindAddress(LPCTSTR lpszBindAddress) +{ + if(::IsStrEmpty(lpszBindAddress)) + return TRUE; + + HP_SOCKADDR addr; + + if(::sockaddr_A_2_IN(lpszBindAddress, 0, addr)) + { + SOCKET sock = socket(addr.family, SOCK_STREAM, IPPROTO_TCP); + + if(sock != INVALID_SOCKET) + { + if(::bind(sock, addr.Addr(), addr.AddrSize()) != SOCKET_ERROR) + { + addr.Copy(m_soAddr); + return TRUE; + } + else + SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError()); + + ::ManualCloseSocket(sock); + } + else + SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError()); + } + else + SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError()); + + return FALSE; +} + +BOOL CTcpAgent::CreateWorkerThreads() +{ + DWORD dwWorkerThreadCount = m_dwWorkerThreadCount +#ifdef USE_EXTERNAL_GC + + 1 +#endif + ; + + if(!m_ioDispatcher.Start(this, DEFAULT_WORKER_MAX_EVENT_COUNT, dwWorkerThreadCount)) + { + SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + +#ifdef USE_EXTERNAL_GC + m_fdGCTimer = m_ioDispatcher.AddTimer(m_dwWorkerThreadCount, GC_CHECK_INTERVAL, this); + + if(IS_INVALID_FD(m_fdGCTimer)) + { + SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } +#endif + + return TRUE; +} + +BOOL CTcpAgent::Stop() +{ + if(!CheckStoping()) + return FALSE; + + DisconnectClientSocket(); + WaitForClientSocketClose(); + WaitForWorkerThreadEnd(); + + ReleaseClientSocket(); + + FireShutdown(); + + ReleaseFreeSocket(); + + Reset(); + + return TRUE; +} + +void CTcpAgent::DisconnectClientSocket() +{ + ::WaitFor(100); + + if(m_bfActiveSockets.Elements() == 0) + return; + + TAgentSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + Disconnect(*it); +} + +void CTcpAgent::WaitForClientSocketClose() +{ + while(m_bfActiveSockets.Elements() > 0) + ::WaitFor(50); +} + +void CTcpAgent::WaitForWorkerThreadEnd() +{ + m_ioDispatcher.Stop(); +} + +void CTcpAgent::ReleaseClientSocket() +{ + VERIFY(m_bfActiveSockets.IsEmpty()); + m_bfActiveSockets.Reset(); +} + +void CTcpAgent::ReleaseFreeSocket() +{ + m_lsFreeSocket.Clear(); + +#ifdef USE_EXTERNAL_GC + if(IS_VALID_FD(m_fdGCTimer)) + { + close(m_fdGCTimer); + m_fdGCTimer = INVALID_FD; + } +#endif + + ReleaseGCSocketObj(TRUE); + VERIFY(m_lsGCSocket.IsEmpty()); +} + +void CTcpAgent::Reset() +{ + m_bfObjPool.Clear(); + m_phSocket.Reset(); + m_soAddr.Reset(); + + m_rcBuffers = nullptr; + + m_enState = SS_STOPPED; + + m_evWait.SyncNotifyAll(); +} + +BOOL CTcpAgent::Connect(LPCTSTR lpszRemoteAddress, USHORT usPort, CONNID* pdwConnID, PVOID pExtra, USHORT usLocalPort, LPCTSTR lpszLocalAddress) +{ + ASSERT(lpszRemoteAddress && usPort != 0); + + if(!HasStarted()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + if(!pdwConnID) + pdwConnID = CreateLocalObject(CONNID); + + *pdwConnID = 0; + + HP_SOCKADDR addr; + HP_SCOPE_HOST host(lpszRemoteAddress); + SOCKET soClient = INVALID_SOCKET; + + DWORD result = CreateClientSocket(host.addr, usPort, lpszLocalAddress, usLocalPort, soClient, addr); + + if(result == NO_ERROR) + { + result = PrepareConnect(*pdwConnID, soClient); + + if(result == NO_ERROR) + result = ConnectToServer(*pdwConnID, host.name, soClient, addr, pExtra); + } + + if(result != NO_ERROR) + { + if(soClient != INVALID_SOCKET) + ::ManualCloseSocket(soClient); + + ::SetLastError(result); + } + + return (result == NO_ERROR); +} + +int CTcpAgent::CreateClientSocket(LPCTSTR lpszRemoteAddress, USHORT usPort, LPCTSTR lpszLocalAddress, USHORT usLocalPort, SOCKET& soClient, HP_SOCKADDR& addr) +{ + if(!::GetSockAddrByHostName(lpszRemoteAddress, usPort, addr)) + return ERROR_ADDRNOTAVAIL; + + HP_SOCKADDR* lpBindAddr = &m_soAddr; + + if(::IsStrNotEmpty(lpszLocalAddress)) + { + lpBindAddr = CreateLocalObject(HP_SOCKADDR); + + if(!::sockaddr_A_2_IN(lpszLocalAddress, 0, *lpBindAddr)) + return ::WSAGetLastError(); + } + + BOOL bBind = lpBindAddr->IsSpecified(); + + if(bBind && lpBindAddr->family != addr.family) + return ERROR_AFNOSUPPORT; + + int result = NO_ERROR; + soClient = socket(addr.family, SOCK_STREAM, IPPROTO_TCP); + + if(soClient == INVALID_SOCKET) + result = ::WSAGetLastError(); + else + { + BOOL bOnOff = (m_dwKeepAliveTime > 0 && m_dwKeepAliveInterval > 0); + VERIFY(IS_NO_ERROR(::SSO_KeepAliveVals(soClient, bOnOff, m_dwKeepAliveTime, m_dwKeepAliveInterval))); + VERIFY(IS_NO_ERROR(::SSO_ReuseAddress(soClient, m_enReusePolicy))); + VERIFY(IS_NO_ERROR(::SSO_NoDelay(soClient, m_bNoDelay))); + + if(bBind && usLocalPort == 0) + { + if(::bind(soClient, lpBindAddr->Addr(), lpBindAddr->AddrSize()) == SOCKET_ERROR) + result = ::WSAGetLastError(); + } + else if(usLocalPort != 0) + { + HP_SOCKADDR bindAddr = bBind ? *lpBindAddr : HP_SOCKADDR::AnyAddr(addr.family); + + bindAddr.SetPort(usLocalPort); + + if(::bind(soClient, bindAddr.Addr(), bindAddr.AddrSize()) == SOCKET_ERROR) + result = ::WSAGetLastError(); + } + } + + return result; +} + +int CTcpAgent::PrepareConnect(CONNID& dwConnID, SOCKET soClient) +{ + if(!m_bfActiveSockets.AcquireLock(dwConnID)) + return ERROR_CONNECTION_COUNT_LIMIT; + + if(TRIGGER(FirePrepareConnect(dwConnID, soClient)) == HR_ERROR) + { + VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, nullptr)); + return ENSURE_ERROR_CANCELLED; + } + + return NO_ERROR; +} + +int CTcpAgent::ConnectToServer(CONNID dwConnID, LPCTSTR lpszRemoteHostName, SOCKET& soClient, const HP_SOCKADDR& addr, PVOID pExtra) +{ + TAgentSocketObj* pSocketObj = GetFreeSocketObj(dwConnID, soClient); + AddClientSocketObj(dwConnID, pSocketObj, addr, lpszRemoteHostName, pExtra); + + int result = HAS_ERROR; + + VERIFY(::fcntl_SETFL(pSocketObj->socket, O_NOATIME | O_NONBLOCK | O_CLOEXEC)); + + int rc = ::connect(pSocketObj->socket, addr.Addr(), addr.AddrSize()); + + if(IS_NO_ERROR(rc) || IS_IO_PENDING_ERROR()) + { + if(m_bAsyncConnect) + { + if(m_ioDispatcher.AddFD(pSocketObj->socket, EPOLLOUT, pSocketObj)) + result = NO_ERROR; + } + else + { + if(IS_HAS_ERROR(result)) + result = ::WaitForSocketWrite(pSocketObj->socket, m_dwSyncConnectTimeout); + + if(IS_NO_ERROR(result)) + { + pSocketObj->SetConnected(); + + if(TRIGGER(FireConnect(pSocketObj)) == HR_ERROR) + result = ENSURE_ERROR_CANCELLED; + else + { + UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN); + + if(!m_ioDispatcher.AddFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj)) + result = HAS_ERROR; + } + } + } + } + + if(result == HAS_ERROR) + result = ::WSAGetLastError(); + if(result != NO_ERROR) + { + AddFreeSocketObj(pSocketObj, SCF_NONE); + soClient = INVALID_SOCKET; + } + + return result; +} + +TAgentSocketObj* CTcpAgent::GetFreeSocketObj(CONNID dwConnID, SOCKET soClient) +{ + DWORD dwIndex; + TAgentSocketObj* pSocketObj = nullptr; + + if(m_lsFreeSocket.TryLock(&pSocketObj, dwIndex)) + { + if(::GetTimeGap32(pSocketObj->freeTime) >= m_dwFreeSocketObjLockTime) + VERIFY(m_lsFreeSocket.ReleaseLock(nullptr, dwIndex)); + else + { + VERIFY(m_lsFreeSocket.ReleaseLock(pSocketObj, dwIndex)); + pSocketObj = nullptr; + } + } + + if(!pSocketObj) pSocketObj = CreateSocketObj(); + pSocketObj->Reset(dwConnID, soClient); + + return pSocketObj; +} + +TAgentSocketObj* CTcpAgent::CreateSocketObj() +{ + return TAgentSocketObj::Construct(m_phSocket, m_bfObjPool); +} + +void CTcpAgent::DeleteSocketObj(TAgentSocketObj* pSocketObj) +{ + TAgentSocketObj::Destruct(pSocketObj); +} + +void CTcpAgent::AddFreeSocketObj(TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode) +{ + if(!InvalidSocketObj(pSocketObj)) + return; + + CloseClientSocketObj(pSocketObj, enFlag, enOperation, iErrorCode); + + m_bfActiveSockets.Remove(pSocketObj->connID); + TAgentSocketObj::Release(pSocketObj); + +#ifndef USE_EXTERNAL_GC + ReleaseGCSocketObj(); +#endif + + if(!m_lsFreeSocket.TryPut(pSocketObj)) + m_lsGCSocket.PushBack(pSocketObj); +} + +void CTcpAgent::ReleaseGCSocketObj(BOOL bForce) +{ + ::ReleaseGCObj(m_lsGCSocket, m_dwFreeSocketObjLockTime, bForce); +} + +BOOL CTcpAgent::InvalidSocketObj(TAgentSocketObj* pSocketObj) +{ + return TAgentSocketObj::InvalidSocketObj(pSocketObj); +} + +void CTcpAgent::AddClientSocketObj(CONNID dwConnID, TAgentSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr, LPCTSTR lpszRemoteHostName, PVOID pExtra) +{ + ASSERT(FindSocketObj(dwConnID) == nullptr); + + pSocketObj->connTime = ::TimeGetTime(); + pSocketObj->activeTime = pSocketObj->connTime; + pSocketObj->host = lpszRemoteHostName; + pSocketObj->extra = pExtra; + + pSocketObj->SetConnected(CST_CONNECTING); + remoteAddr.Copy(pSocketObj->remoteAddr); + + VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, pSocketObj)); +} + +TAgentSocketObj* CTcpAgent::FindSocketObj(CONNID dwConnID) +{ + TAgentSocketObj* pSocketObj = nullptr; + + if(m_bfActiveSockets.Get(dwConnID, &pSocketObj) != TAgentSocketObjPtrPool::GR_VALID) + pSocketObj = nullptr; + + return pSocketObj; +} + +void CTcpAgent::CloseClientSocketObj(TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode, int iShutdownFlag) +{ + ASSERT(TAgentSocketObj::IsExist(pSocketObj)); + + if(enFlag == SCF_CLOSE) + FireClose(pSocketObj, SO_CLOSE, SE_OK); + else if(enFlag == SCF_ERROR) + FireClose(pSocketObj, enOperation, iErrorCode); + + SOCKET socket = pSocketObj->socket; + pSocketObj->socket = INVALID_SOCKET; + + ::ManualCloseSocket(socket, iShutdownFlag); +} + +BOOL CTcpAgent::GetLocalAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) +{ + ASSERT(lpszAddress != nullptr && iAddressLen > 0); + + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return ::GetSocketLocalAddress(pSocketObj->socket, lpszAddress, iAddressLen, usPort); +} + +BOOL CTcpAgent::GetRemoteAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) +{ + ASSERT(lpszAddress != nullptr && iAddressLen > 0); + + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsExist(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + ADDRESS_FAMILY usFamily; + return ::sockaddr_IN_2_A(pSocketObj->remoteAddr, usFamily, lpszAddress, iAddressLen, usPort); +} + +BOOL CTcpAgent::GetRemoteHost(CONNID dwConnID, TCHAR lpszHost[], int& iHostLen, USHORT& usPort) +{ + ASSERT(lpszHost != nullptr && iHostLen > 0); + + BOOL isOK = FALSE; + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + int iLen = pSocketObj->host.GetLength() + 1; + + if(iHostLen >= iLen) + { + memcpy(lpszHost, CA2CT((LPCSTR)pSocketObj->host), iLen * sizeof(TCHAR)); + usPort = pSocketObj->remoteAddr.Port(); + + isOK = TRUE; + } + + iHostLen = iLen; + } + + return isOK; +} + +BOOL CTcpAgent::GetRemoteHost(CONNID dwConnID, LPCSTR* lpszHost, USHORT* pusPort) +{ + *lpszHost = nullptr; + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *lpszHost = pSocketObj->host; + + if(pusPort) + *pusPort = pSocketObj->remoteAddr.Port(); + } + + return (*lpszHost != nullptr && (*lpszHost)[0] != 0); +} + +BOOL CTcpAgent::SetConnectionExtra(CONNID dwConnID, PVOID pExtra) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionExtra(pSocketObj, pExtra); +} + +BOOL CTcpAgent::SetConnectionExtra(TAgentSocketObj* pSocketObj, PVOID pExtra) +{ + if(!TAgentSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->extra = pExtra; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpAgent::GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionExtra(pSocketObj, ppExtra); +} + +BOOL CTcpAgent::GetConnectionExtra(TAgentSocketObj* pSocketObj, PVOID* ppExtra) +{ + ASSERT(ppExtra != nullptr); + + if(!TAgentSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppExtra = pSocketObj->extra; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpAgent::SetConnectionReserved(CONNID dwConnID, PVOID pReserved) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionReserved(pSocketObj, pReserved); +} + +BOOL CTcpAgent::SetConnectionReserved(TAgentSocketObj* pSocketObj, PVOID pReserved) +{ + if(!TAgentSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->reserved = pReserved; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpAgent::GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionReserved(pSocketObj, ppReserved); +} + +BOOL CTcpAgent::GetConnectionReserved(TAgentSocketObj* pSocketObj, PVOID* ppReserved) +{ + ASSERT(ppReserved != nullptr); + + if(!TAgentSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppReserved = pSocketObj->reserved; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpAgent::SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionReserved2(pSocketObj, pReserved2); +} + +BOOL CTcpAgent::SetConnectionReserved2(TAgentSocketObj* pSocketObj, PVOID pReserved2) +{ + if(!TAgentSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->reserved2 = pReserved2; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpAgent::GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionReserved2(pSocketObj, ppReserved2); +} + +BOOL CTcpAgent::GetConnectionReserved2(TAgentSocketObj* pSocketObj, PVOID* ppReserved2) +{ + ASSERT(ppReserved2 != nullptr); + + if(!TAgentSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppReserved2 = pSocketObj->reserved2; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpAgent::IsPauseReceive(CONNID dwConnID, BOOL& bPaused) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + bPaused = pSocketObj->paused; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpAgent::IsConnected(CONNID dwConnID) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return pSocketObj->HasConnected(); +} + +BOOL CTcpAgent::GetPendingDataLength(CONNID dwConnID, int& iPending) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + iPending = pSocketObj->Pending(); + return TRUE; + } + + return FALSE; +} + +DWORD CTcpAgent::GetConnectionCount() +{ + return m_bfActiveSockets.Elements(); +} + +BOOL CTcpAgent::GetAllConnectionIDs(CONNID pIDs[], DWORD& dwCount) +{ + return m_bfActiveSockets.GetAllElementIndexes(pIDs, dwCount); +} + +BOOL CTcpAgent::GetConnectPeriod(CONNID dwConnID, DWORD& dwPeriod) +{ + BOOL isOK = TRUE; + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TAgentSocketObj::IsValid(pSocketObj)) + dwPeriod = ::GetTimeGap32(pSocketObj->connTime); + else + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + isOK = FALSE; + } + + return isOK; +} + +BOOL CTcpAgent::GetSilencePeriod(CONNID dwConnID, DWORD& dwPeriod) +{ + if(!m_bMarkSilence) + return FALSE; + + BOOL isOK = TRUE; + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TAgentSocketObj::IsValid(pSocketObj)) + dwPeriod = ::GetTimeGap32(pSocketObj->activeTime); + else + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + isOK = FALSE; + } + + return isOK; +} + +BOOL CTcpAgent::Disconnect(CONNID dwConnID, BOOL bForce) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_DISCONNECT, dwConnID, bForce); +} + +BOOL CTcpAgent::DisconnectLongConnections(DWORD dwPeriod, BOOL bForce) +{ + if(dwPeriod > MAX_CONNECTION_PERIOD) + return FALSE; + if(m_bfActiveSockets.Elements() == 0) + return TRUE; + + DWORD now = ::TimeGetTime(); + + TAgentSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + { + CONNID connID = *it; + TAgentSocketObj* pSocketObj = FindSocketObj(connID); + + if(TAgentSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->connTime) >= (int)dwPeriod) + Disconnect(connID, bForce); + } + + return TRUE; +} + +BOOL CTcpAgent::DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce) +{ + if(!m_bMarkSilence) + return FALSE; + if(dwPeriod > MAX_CONNECTION_PERIOD) + return FALSE; + if(m_bfActiveSockets.Elements() == 0) + return TRUE; + + DWORD now = ::TimeGetTime(); + + TAgentSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + { + CONNID connID = *it; + TAgentSocketObj* pSocketObj = FindSocketObj(connID); + + if(TAgentSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->activeTime) >= (int)dwPeriod) + Disconnect(connID, bForce); + } + + return TRUE; +} + +BOOL CTcpAgent::PauseReceive(CONNID dwConnID, BOOL bPause) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + if(!pSocketObj->HasConnected()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + if(pSocketObj->paused == bPause) + return TRUE; + + pSocketObj->paused = bPause; + + if(!bPause) + return m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_UNPAUSE, pSocketObj->connID); + + return TRUE; +} + +BOOL CTcpAgent::OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) +{ + if(pv == this) + { + ReleaseGCSocketObj(FALSE); + ::ReadTimer(m_fdGCTimer); + + return FALSE; + } + + TAgentSocketObj* pSocketObj = (TAgentSocketObj*)(pv); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + return FALSE; + + if(events & _EPOLL_ALL_ERROR_EVENTS) + pSocketObj->SetConnected(FALSE); + + pSocketObj->Increment(); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + { + pSocketObj->Decrement(); + return FALSE; + } + + if(pSocketObj->IsConnecting()) + { + HandleConnect(pContext, pSocketObj, events); + + pSocketObj->Decrement(); + return FALSE; + } + + return TRUE; +} + +VOID CTcpAgent::OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) +{ + TAgentSocketObj* pSocketObj = (TAgentSocketObj*)(pv); + + if(TAgentSocketObj::IsValid(pSocketObj)) + { + ASSERT(rs && !(events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP))); + + UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN); + m_ioDispatcher.ModFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj); + } + + pSocketObj->Decrement(); +} + +VOID CTcpAgent::OnCommand(const TDispContext* pContext, TDispCommand* pCmd) +{ + switch(pCmd->type) + { + case DISP_CMD_SEND: + HandleCmdSend(pContext, (CONNID)(pCmd->wParam)); + break; + case DISP_CMD_UNPAUSE: + HandleCmdUnpause(pContext, (CONNID)(pCmd->wParam)); + break; + case DISP_CMD_DISCONNECT: + HandleCmdDisconnect(pContext, (CONNID)(pCmd->wParam), (BOOL)pCmd->lParam); + break; + } +} + +VOID CTcpAgent::HandleCmdSend(const TDispContext* pContext, CONNID dwConnID) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TAgentSocketObj::IsValid(pSocketObj) && pSocketObj->IsPending()) + m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLOUT); +} + +VOID CTcpAgent::HandleCmdUnpause(const TDispContext* pContext, CONNID dwConnID) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TAgentSocketObj::IsValid(pSocketObj)) + return; + + if(BeforeUnpause(pSocketObj)) + m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLIN); + else + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED); +} + +VOID CTcpAgent::HandleCmdDisconnect(const TDispContext* pContext, CONNID dwConnID, BOOL bForce) +{ + TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TAgentSocketObj::IsValid(pSocketObj)) + m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLHUP); +} + +BOOL CTcpAgent::OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleReceive(pContext, (TAgentSocketObj*)pv, RETRIVE_EVENT_FLAG_H(events)); +} + +BOOL CTcpAgent::OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleSend(pContext, (TAgentSocketObj*)pv, RETRIVE_EVENT_FLAG_H(events)); +} + +BOOL CTcpAgent::OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleClose(pContext, (TAgentSocketObj*)pv, SCF_CLOSE, events); +} + +BOOL CTcpAgent::OnError(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleClose(pContext, (TAgentSocketObj*)pv, SCF_ERROR, events); +} + +VOID CTcpAgent::OnDispatchThreadStart(THR_ID tid) +{ + OnWorkerThreadStart(tid); +} + +VOID CTcpAgent::OnDispatchThreadEnd(THR_ID tid) +{ + OnWorkerThreadEnd(tid); +} + +BOOL CTcpAgent::HandleClose(const TDispContext* pContext, TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag, UINT events) +{ + EnSocketOperation enOperation = SO_CLOSE; + + if(events & _EPOLL_HUNGUP_EVENTS) + enOperation = SO_CLOSE; + else if(events & EPOLLIN) + enOperation = SO_RECEIVE; + else if(events & EPOLLOUT) + enOperation = SO_SEND; + + int iErrorCode = 0; + + if(enFlag == SCF_ERROR) + iErrorCode = ::SSO_GetError(pSocketObj->socket); + + AddFreeSocketObj(pSocketObj, enFlag, enOperation, iErrorCode); + + return TRUE; +} + +BOOL CTcpAgent::HandleConnect(const TDispContext* pContext, TAgentSocketObj* pSocketObj, UINT events) +{ + int code = ::SSO_GetError(pSocketObj->socket); + + if(!IS_NO_ERROR(code) || (events & _EPOLL_ERROR_EVENTS)) + { + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_CONNECT, code); + return FALSE; + } + + if((events & (_EPOLL_HUNGUP_EVENTS | _EPOLL_READ_EVENTS)) || !(events & EPOLLOUT)) + { + AddFreeSocketObj(pSocketObj, SCF_CLOSE, SO_CONNECT, SE_OK); + return FALSE; + } + + pSocketObj->SetConnected(); + + if(TRIGGER(FireConnect(pSocketObj)) == HR_ERROR) + { + AddFreeSocketObj(pSocketObj, SCF_NONE); + return FALSE; + } + + UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN); + + if(!m_ioDispatcher.ModFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj)) + { + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_CONNECT, ::WSAGetLastError()); + return FALSE; + } + + return TRUE; +} + +BOOL CTcpAgent::HandleReceive(const TDispContext* pContext, TAgentSocketObj* pSocketObj, int flag) +{ + ASSERT(TAgentSocketObj::IsValid(pSocketObj)); + + if(m_bMarkSilence) pSocketObj->activeTime = ::TimeGetTime(); + + CBufferPtr& buffer = m_rcBuffers[pContext->GetIndex()]; + + int reads = flag ? -1 : MAX_CONTINUE_READS; + + for(int i = 0; i < reads || reads < 0; i++) + { + if(pSocketObj->paused) + break; + + int rc = (int)read(pSocketObj->socket, buffer.Ptr(), buffer.Size()); + + if(rc > 0) + { + if(TRIGGER(FireReceive(pSocketObj, buffer.Ptr(), rc)) == HR_ERROR) + { + TRACE(" OnReceive() event return 'HR_ERROR', connection will be closed !", pSocketObj->connID); + + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED); + return FALSE; + } + } + else if(rc == 0) + { + AddFreeSocketObj(pSocketObj, SCF_CLOSE, SO_RECEIVE, SE_OK); + return FALSE; + } + else + { + ASSERT(rc == SOCKET_ERROR); + + int code = ::WSAGetLastError(); + + if(code == ERROR_WOULDBLOCK) + break; + + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, code); + return FALSE; + } + } + + return TRUE; +} + +BOOL CTcpAgent::HandleSend(const TDispContext* pContext, TAgentSocketObj* pSocketObj, int flag) +{ + ASSERT(TAgentSocketObj::IsValid(pSocketObj)); + + if(!pSocketObj->IsPending()) + return TRUE; + + BOOL bBlocked = FALSE; + int writes = flag ? -1 : MAX_CONTINUE_WRITES; + + TBufferObjList& sndBuff = pSocketObj->sndBuff; + TItemPtr itPtr(sndBuff); + + for(int i = 0; i < writes || writes < 0; i++) + { + { + CReentrantCriSecLock locallock(pSocketObj->csSend); + itPtr = sndBuff.PopFront(); + } + + if(!itPtr.IsValid()) + break; + + ASSERT(!itPtr->IsEmpty()); + + if(!SendItem(pSocketObj, itPtr, bBlocked)) + return FALSE; + + if(bBlocked) + { + ASSERT(!itPtr->IsEmpty()); + + CReentrantCriSecLock locallock(pSocketObj->csSend); + sndBuff.PushFront(itPtr.Detach()); + + break; + } + } + + return TRUE; +} + +BOOL CTcpAgent::SendItem(TAgentSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked) +{ + while(!pItem->IsEmpty()) + { + int rc = (int)write(pSocketObj->socket, pItem->Ptr(), pItem->Size()); + + if(rc > 0) + { + if(TRIGGER(FireSend(pSocketObj, pItem->Ptr(), rc)) == HR_ERROR) + { + TRACE(" OnSend() event should not return 'HR_ERROR' !!", pSocketObj->connID); + ASSERT(FALSE); + } + + pItem->Reduce(rc); + } + else if(rc == SOCKET_ERROR) + { + int code = ::WSAGetLastError(); + + if(code == ERROR_WOULDBLOCK) + { + bBlocked = TRUE; + break; + } + else + { + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_SEND, code); + return FALSE; + } + } + else + ASSERT(FALSE); + } + + return TRUE; +} + +BOOL CTcpAgent::Send(CONNID dwConnID, 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(dwConnID, &buffer, 1); +} + +BOOL CTcpAgent::DoSendPackets(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; + } + + return DoSendPackets(pSocketObj, pBuffers, iCount); +} + +BOOL CTcpAgent::DoSendPackets(TAgentSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount) +{ + ASSERT(pSocketObj && pBuffers && iCount > 0); + + int result = NO_ERROR; + + if(!pSocketObj->HasConnected()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + if(pBuffers && iCount > 0) + { + CLocalSafeCounter localcounter(*pSocketObj); + CReentrantCriSecLock locallock(pSocketObj->csSend); + + if(TAgentSocketObj::IsValid(pSocketObj)) + result = SendInternal(pSocketObj, pBuffers, iCount); + else + result = ERROR_OBJECT_NOT_FOUND; + } + else + result = ERROR_INVALID_PARAMETER; + + if(result != NO_ERROR) + ::SetLastError(result); + + return (result == NO_ERROR); +} + +int CTcpAgent::SendInternal(TAgentSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount) +{ + BOOL bPending = pSocketObj->IsPending(); + + for(int i = 0; i < iCount; i++) + { + int iBufLen = pBuffers[i].len; + + if(iBufLen > 0) + { + BYTE* pBuffer = (BYTE*)pBuffers[i].buf; + ASSERT(pBuffer); + + pSocketObj->sndBuff.Cat(pBuffer, iBufLen); + ASSERT(pSocketObj->sndBuff.Length() > 0); + } + } + + if(!bPending && pSocketObj->IsPending()) + { + if(!m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_SEND, pSocketObj->connID)) + return ::GetLastError(); + } + + return NO_ERROR; +} + +BOOL CTcpAgent::SendSmallFile(CONNID dwConnID, 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(dwConnID, szBuf, 3); +} diff --git a/TcpAgent.h b/TcpAgent.h new file mode 100644 index 0000000..5acbffc --- /dev/null +++ b/TcpAgent.h @@ -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; +}; diff --git a/TcpClient.cpp b/TcpClient.cpp new file mode 100644 index 0000000..c24edb6 --- /dev/null +++ b/TcpClient.cpp @@ -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(" 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(" 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(); +} diff --git a/TcpClient.h b/TcpClient.h new file mode 100644 index 0000000..bc19b15 --- /dev/null +++ b/TcpClient.h @@ -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 m_thWorker; +}; diff --git a/TcpPackAgent.cpp b/TcpPackAgent.cpp new file mode 100644 index 0000000..bfe5530 --- /dev/null +++ b/TcpPackAgent.cpp @@ -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" diff --git a/TcpPackAgent.h b/TcpPackAgent.h new file mode 100644 index 0000000..ad36e91 --- /dev/null +++ b/TcpPackAgent.h @@ -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 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 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 CTcpPackAgent; + +#ifdef _SSL_SUPPORT + +#include "SSLAgent.h" +typedef CTcpPackAgentT CSSLPackAgent; + +#endif diff --git a/TcpPackClient.cpp b/TcpPackClient.cpp new file mode 100644 index 0000000..b78d7a3 --- /dev/null +++ b/TcpPackClient.cpp @@ -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" diff --git a/TcpPackClient.h b/TcpPackClient.h new file mode 100644 index 0000000..6b1d904 --- /dev/null +++ b/TcpPackClient.h @@ -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 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 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* 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 m_pkInfo; + TItemListEx m_lsBuffer; +}; + +typedef CTcpPackClientT CTcpPackClient; + +#ifdef _SSL_SUPPORT + +#include "SSLClient.h" +typedef CTcpPackClientT CSSLPackClient; + +#endif diff --git a/TcpPackServer.cpp b/TcpPackServer.cpp new file mode 100644 index 0000000..68ddd7b --- /dev/null +++ b/TcpPackServer.cpp @@ -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" diff --git a/TcpPackServer.h b/TcpPackServer.h new file mode 100644 index 0000000..b7dd786 --- /dev/null +++ b/TcpPackServer.h @@ -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 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 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 CTcpPackServer; + +#ifdef _SSL_SUPPORT + +#include "SSLServer.h" +typedef CTcpPackServerT CSSLPackServer; + +#endif diff --git a/TcpPullAgent.cpp b/TcpPullAgent.cpp new file mode 100644 index 0000000..8efdd2c --- /dev/null +++ b/TcpPullAgent.cpp @@ -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" diff --git a/TcpPullAgent.h b/TcpPullAgent.h new file mode 100644 index 0000000..068a004 --- /dev/null +++ b/TcpPullAgent.h @@ -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 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 CTcpPullAgent; + +#ifdef _SSL_SUPPORT + +#include "SSLAgent.h" +typedef CTcpPullAgentT CSSLPullAgent; + +#endif diff --git a/TcpPullClient.cpp b/TcpPullClient.cpp new file mode 100644 index 0000000..cd79d57 --- /dev/null +++ b/TcpPullClient.cpp @@ -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" diff --git a/TcpPullClient.h b/TcpPullClient.h new file mode 100644 index 0000000..e0118c8 --- /dev/null +++ b/TcpPullClient.h @@ -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 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 CTcpPullClient; + +#ifdef _SSL_SUPPORT + +#include "SSLClient.h" +typedef CTcpPullClientT CSSLPullClient; + +#endif diff --git a/TcpPullServer.cpp b/TcpPullServer.cpp new file mode 100644 index 0000000..8ed7731 --- /dev/null +++ b/TcpPullServer.cpp @@ -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" diff --git a/TcpPullServer.h b/TcpPullServer.h new file mode 100644 index 0000000..19213b9 --- /dev/null +++ b/TcpPullServer.h @@ -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 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 CTcpPullServer; + +#ifdef _SSL_SUPPORT + +#include "SSLServer.h" +typedef CTcpPullServerT CSSLPullServer; + +#endif diff --git a/TcpServer.cpp b/TcpServer.cpp new file mode 100644 index 0000000..b051cc0 --- /dev/null +++ b/TcpServer.cpp @@ -0,0 +1,1187 @@ +/* + * 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 "TcpServer.h" + +#include "./common/FileHelper.h" + +BOOL CTcpServer::Start(LPCTSTR lpszBindAddress, USHORT usPort) +{ + if(!CheckParams() || !CheckStarting()) + return FALSE; + + PrepareStart(); + + if(CreateListenSocket(lpszBindAddress, usPort)) + if(CreateWorkerThreads()) + if(StartAccept()) + { + m_enState = SS_STARTED; + return TRUE; + } + + EXECUTE_RESTORE_ERROR(Stop()); + + return FALSE; +} + +void CTcpServer::SetLastError(EnSocketError code, LPCSTR func, int ec) +{ + m_enLastError = code; + ::SetLastError(ec); +} + +BOOL CTcpServer::CheckParams() +{ + if ((m_enSendPolicy >= SP_PACK && m_enSendPolicy <= SP_DIRECT) && + (m_enOnSendSyncPolicy >= OSSP_NONE && m_enOnSendSyncPolicy <= OSSP_RECEIVE) && + ((int)m_dwMaxConnectionCount > 0 && m_dwMaxConnectionCount <= MAX_CONNECTION_COUNT) && + ((int)m_dwWorkerThreadCount > 0 && m_dwWorkerThreadCount <= MAX_WORKER_THREAD_COUNT) && + ((int)m_dwAcceptSocketCount > 0) && + ((int)m_dwSocketBufferSize >= MIN_SOCKET_BUFFER_SIZE) && + ((int)m_dwSocketListenQueue > 0) && + ((int)m_dwFreeSocketObjLockTime >= 1000) && + ((int)m_dwFreeSocketObjPool >= 0) && + ((int)m_dwFreeBufferObjPool >= 0) && + ((int)m_dwFreeSocketObjHold >= 0) && + ((int)m_dwFreeBufferObjHold >= 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 CTcpServer::PrepareStart() +{ + m_bfActiveSockets.Reset(m_dwMaxConnectionCount); + m_lsFreeSocket.Reset(m_dwFreeSocketObjPool); + + m_bfObjPool.SetItemCapacity(m_dwSocketBufferSize); + m_bfObjPool.SetPoolSize(m_dwFreeBufferObjPool); + m_bfObjPool.SetPoolHold(m_dwFreeBufferObjHold); + + m_bfObjPool.Prepare(); + + m_rcBuffers = make_unique(m_dwWorkerThreadCount); + for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwSocketBufferSize);}); + + m_soListens = make_unique(m_dwWorkerThreadCount); + for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock) {sock = INVALID_FD;}); +} + +BOOL CTcpServer::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 CTcpServer::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 CTcpServer::CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort) +{ + if(::IsStrEmpty(lpszBindAddress)) + lpszBindAddress = DEFAULT_IPV4_BIND_ADDRESS; + + HP_SOCKADDR addr; + + if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, addr)) + { + SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + for(DWORD i = 0; i < m_dwWorkerThreadCount; i++) + { + m_soListens[i] = socket(addr.family, SOCK_STREAM, IPPROTO_TCP); + 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); + + BOOL bOnOff = (m_dwKeepAliveTime > 0 && m_dwKeepAliveInterval > 0); + VERIFY(IS_NO_ERROR(::SSO_KeepAliveVals(soListen, bOnOff, m_dwKeepAliveTime, m_dwKeepAliveInterval))); + VERIFY(IS_NO_ERROR(::SSO_ReuseAddress(soListen, m_enReusePolicy))); + VERIFY(IS_NO_ERROR(::SSO_NoDelay(soListen, m_bNoDelay))); + + if(IS_HAS_ERROR(::bind(soListen, addr.Addr(), addr.AddrSize()))) + { + SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + if(TRIGGER(FirePrepareListen(soListen)) == HR_ERROR) + { + SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED); + return FALSE; + } + + if(IS_HAS_ERROR(::listen(soListen, m_dwSocketListenQueue))) + { + SetLastError(SE_SOCKET_LISTEN, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + } + + return TRUE; +} + +BOOL CTcpServer::CreateWorkerThreads() +{ + DWORD dwWorkerThreadCount = m_dwWorkerThreadCount +#ifdef USE_EXTERNAL_GC + + 1 +#endif + ; + + if(!m_ioDispatcher.Start(this, m_dwAcceptSocketCount, dwWorkerThreadCount)) + { + SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + return TRUE; +} + +BOOL CTcpServer::StartAccept() +{ + for(int i = 0; i < (int)m_dwWorkerThreadCount; i++) + { + SOCKET& soListen = m_soListens[i]; + + if(!m_ioDispatcher.AddFD(i, soListen, EPOLLIN | EPOLLET, TO_PVOID(&soListen))) + { + SetLastError(SE_SOCKE_ATTACH_TO_CP, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + } + +#ifdef USE_EXTERNAL_GC + m_fdGCTimer = m_ioDispatcher.AddTimer(m_dwWorkerThreadCount, GC_CHECK_INTERVAL, this); + + if(IS_INVALID_FD(m_fdGCTimer)) + { + SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } +#endif + + return TRUE; +} + +BOOL CTcpServer::Stop() +{ + if(!CheckStoping()) + return FALSE; + + CloseListenSocket(); + + DisconnectClientSocket(); + WaitForClientSocketClose(); + WaitForWorkerThreadEnd(); + + ReleaseClientSocket(); + + FireShutdown(); + + ReleaseFreeSocket(); + + Reset(); + + return TRUE; +} + +void CTcpServer::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 CTcpServer::DisconnectClientSocket() +{ + if(m_bfActiveSockets.Elements() == 0) + return; + + TSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + Disconnect(*it); +} + +void CTcpServer::WaitForClientSocketClose() +{ + while(m_bfActiveSockets.Elements() > 0) + ::WaitFor(50); +} + +void CTcpServer::WaitForWorkerThreadEnd() +{ + m_ioDispatcher.Stop(); +} + +void CTcpServer::ReleaseClientSocket() +{ + VERIFY(m_bfActiveSockets.IsEmpty()); + m_bfActiveSockets.Reset(); +} + +void CTcpServer::ReleaseFreeSocket() +{ + m_lsFreeSocket.Clear(); + +#ifdef USE_EXTERNAL_GC + if(IS_VALID_FD(m_fdGCTimer)) + { + close(m_fdGCTimer); + m_fdGCTimer = INVALID_FD; + } +#endif + + ReleaseGCSocketObj(TRUE); + VERIFY(m_lsGCSocket.IsEmpty()); +} + +void CTcpServer::Reset() +{ + m_phSocket.Reset(); + m_bfObjPool.Clear(); + + m_rcBuffers = nullptr; + m_soListens = nullptr; + + m_enState = SS_STOPPED; + + m_evWait.SyncNotifyAll(); +} + +TSocketObj* CTcpServer::GetFreeSocketObj(CONNID dwConnID, SOCKET soClient) +{ + DWORD dwIndex; + TSocketObj* pSocketObj = nullptr; + + if(m_lsFreeSocket.TryLock(&pSocketObj, dwIndex)) + { + if(::GetTimeGap32(pSocketObj->freeTime) >= m_dwFreeSocketObjLockTime) + VERIFY(m_lsFreeSocket.ReleaseLock(nullptr, dwIndex)); + else + { + VERIFY(m_lsFreeSocket.ReleaseLock(pSocketObj, dwIndex)); + pSocketObj = nullptr; + } + } + + if(!pSocketObj) pSocketObj = CreateSocketObj(); + pSocketObj->Reset(dwConnID, soClient); + + return pSocketObj; +} + +TSocketObj* CTcpServer::CreateSocketObj() +{ + return TSocketObj::Construct(m_phSocket, m_bfObjPool); +} + +void CTcpServer::DeleteSocketObj(TSocketObj* pSocketObj) +{ + TSocketObj::Destruct(pSocketObj); +} + +void CTcpServer::AddFreeSocketObj(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode) +{ + if(!InvalidSocketObj(pSocketObj)) + return; + + CloseClientSocketObj(pSocketObj, enFlag, enOperation, iErrorCode); + + m_bfActiveSockets.Remove(pSocketObj->connID); + TSocketObj::Release(pSocketObj); + +#ifndef USE_EXTERNAL_GC + ReleaseGCSocketObj(); +#endif + + if(!m_lsFreeSocket.TryPut(pSocketObj)) + m_lsGCSocket.PushBack(pSocketObj); +} + +void CTcpServer::ReleaseGCSocketObj(BOOL bForce) +{ + ::ReleaseGCObj(m_lsGCSocket, m_dwFreeSocketObjLockTime, bForce); +} + +BOOL CTcpServer::InvalidSocketObj(TSocketObj* pSocketObj) +{ + return TSocketObj::InvalidSocketObj(pSocketObj); +} + +void CTcpServer::AddClientSocketObj(CONNID dwConnID, TSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr) +{ + ASSERT(FindSocketObj(dwConnID) == nullptr); + + pSocketObj->connTime = ::TimeGetTime(); + pSocketObj->activeTime = pSocketObj->connTime; + + remoteAddr.Copy(pSocketObj->remoteAddr); + pSocketObj->SetConnected(); + + VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, pSocketObj)); +} + +TSocketObj* CTcpServer::FindSocketObj(CONNID dwConnID) +{ + TSocketObj* pSocketObj = nullptr; + + if(m_bfActiveSockets.Get(dwConnID, &pSocketObj) != TSocketObjPtrPool::GR_VALID) + pSocketObj = nullptr; + + return pSocketObj; +} + +void CTcpServer::CloseClientSocketObj(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode, int iShutdownFlag) +{ + ASSERT(TSocketObj::IsExist(pSocketObj)); + + if(enFlag == SCF_CLOSE) + FireClose(pSocketObj, SO_CLOSE, SE_OK); + else if(enFlag == SCF_ERROR) + FireClose(pSocketObj, enOperation, iErrorCode); + + SOCKET socket = pSocketObj->socket; + pSocketObj->socket = INVALID_SOCKET; + + ::ManualCloseSocket(socket, iShutdownFlag); +} + +BOOL CTcpServer::GetListenAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) +{ + ASSERT(lpszAddress != nullptr && iAddressLen > 0); + + if(!HasStarted()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + return ::GetSocketLocalAddress(m_soListens[0], lpszAddress, iAddressLen, usPort); +} + +BOOL CTcpServer::GetLocalAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) +{ + ASSERT(lpszAddress != nullptr && iAddressLen > 0); + + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return ::GetSocketLocalAddress(pSocketObj->socket, lpszAddress, iAddressLen, usPort); +} + +BOOL CTcpServer::GetRemoteAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) +{ + ASSERT(lpszAddress != nullptr && iAddressLen > 0); + + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TSocketObj::IsExist(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + ADDRESS_FAMILY usFamily; + return ::sockaddr_IN_2_A(pSocketObj->remoteAddr, usFamily, lpszAddress, iAddressLen, usPort); +} + +BOOL CTcpServer::SetConnectionExtra(CONNID dwConnID, PVOID pExtra) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionExtra(pSocketObj, pExtra); +} + +BOOL CTcpServer::SetConnectionExtra(TSocketObj* pSocketObj, PVOID pExtra) +{ + if(!TSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->extra = pExtra; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpServer::GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionExtra(pSocketObj, ppExtra); +} + +BOOL CTcpServer::GetConnectionExtra(TSocketObj* pSocketObj, PVOID* ppExtra) +{ + ASSERT(ppExtra != nullptr); + + if(!TSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppExtra = pSocketObj->extra; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpServer::SetConnectionReserved(CONNID dwConnID, PVOID pReserved) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionReserved(pSocketObj, pReserved); +} + +BOOL CTcpServer::SetConnectionReserved(TSocketObj* pSocketObj, PVOID pReserved) +{ + if(!TSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->reserved = pReserved; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpServer::GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionReserved(pSocketObj, ppReserved); +} + +BOOL CTcpServer::GetConnectionReserved(TSocketObj* pSocketObj, PVOID* ppReserved) +{ + ASSERT(ppReserved != nullptr); + + if(!TSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppReserved = pSocketObj->reserved; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpServer::SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionReserved2(pSocketObj, pReserved2); +} + +BOOL CTcpServer::SetConnectionReserved2(TSocketObj* pSocketObj, PVOID pReserved2) +{ + if(!TSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->reserved2 = pReserved2; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpServer::GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionReserved2(pSocketObj, ppReserved2); +} + +BOOL CTcpServer::GetConnectionReserved2(TSocketObj* pSocketObj, PVOID* ppReserved2) +{ + ASSERT(ppReserved2 != nullptr); + + if(!TSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppReserved2 = pSocketObj->reserved2; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpServer::IsPauseReceive(CONNID dwConnID, BOOL& bPaused) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TSocketObj::IsValid(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + bPaused = pSocketObj->paused; + return TRUE; + } + + return FALSE; +} + +BOOL CTcpServer::IsConnected(CONNID dwConnID) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return pSocketObj->HasConnected(); +} + +BOOL CTcpServer::GetPendingDataLength(CONNID dwConnID, int& iPending) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TSocketObj::IsValid(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + iPending = pSocketObj->Pending(); + return TRUE; + } + + return FALSE; +} + +DWORD CTcpServer::GetConnectionCount() +{ + return m_bfActiveSockets.Elements(); +} + +BOOL CTcpServer::GetAllConnectionIDs(CONNID pIDs[], DWORD& dwCount) +{ + return m_bfActiveSockets.GetAllElementIndexes(pIDs, dwCount); +} + +BOOL CTcpServer::GetConnectPeriod(CONNID dwConnID, DWORD& dwPeriod) +{ + BOOL isOK = TRUE; + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TSocketObj::IsValid(pSocketObj)) + dwPeriod = ::GetTimeGap32(pSocketObj->connTime); + else + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + isOK = FALSE; + } + + return isOK; +} + +BOOL CTcpServer::GetSilencePeriod(CONNID dwConnID, DWORD& dwPeriod) +{ + if(!m_bMarkSilence) + return FALSE; + + BOOL isOK = TRUE; + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TSocketObj::IsValid(pSocketObj)) + dwPeriod = ::GetTimeGap32(pSocketObj->activeTime); + else + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + isOK = FALSE; + } + + return isOK; +} + +BOOL CTcpServer::Disconnect(CONNID dwConnID, BOOL bForce) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_DISCONNECT, dwConnID, bForce); +} + +BOOL CTcpServer::DisconnectLongConnections(DWORD dwPeriod, BOOL bForce) +{ + if(dwPeriod > MAX_CONNECTION_PERIOD) + return FALSE; + if(m_bfActiveSockets.Elements() == 0) + return TRUE; + + DWORD now = ::TimeGetTime(); + + TSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + { + CONNID connID = *it; + TSocketObj* pSocketObj = FindSocketObj(connID); + + if(TSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->connTime) >= (int)dwPeriod) + Disconnect(connID, bForce); + } + + return TRUE; +} + +BOOL CTcpServer::DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce) +{ + if(!m_bMarkSilence) + return FALSE; + if(dwPeriod > MAX_CONNECTION_PERIOD) + return FALSE; + if(m_bfActiveSockets.Elements() == 0) + return TRUE; + + DWORD now = ::TimeGetTime(); + + TSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + { + CONNID connID = *it; + TSocketObj* pSocketObj = FindSocketObj(connID); + + if(TSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->activeTime) >= (int)dwPeriod) + Disconnect(connID, bForce); + } + + return TRUE; +} + +BOOL CTcpServer::PauseReceive(CONNID dwConnID, BOOL bPause) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + if(pSocketObj->paused == bPause) + return TRUE; + + pSocketObj->paused = bPause; + + if(!bPause) + return m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_UNPAUSE, pSocketObj->connID); + + return TRUE; +} + +BOOL CTcpServer::OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) +{ + if(pv == &m_soListens[pContext->GetIndex()]) + { + HandleAccept(pContext, events); + return FALSE; + } + else if(pv == this) + { + ReleaseGCSocketObj(FALSE); + ::ReadTimer(m_fdGCTimer); + + return FALSE; + } + + TSocketObj* pSocketObj = (TSocketObj*)(pv); + + if(!TSocketObj::IsValid(pSocketObj)) + return FALSE; + + if(events & _EPOLL_ALL_ERROR_EVENTS) + pSocketObj->SetConnected(FALSE); + + pSocketObj->Increment(); + + if(!TSocketObj::IsValid(pSocketObj)) + { + pSocketObj->Decrement(); + return FALSE; + } + + return TRUE; +} + +VOID CTcpServer::OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) +{ + TSocketObj* pSocketObj = (TSocketObj*)(pv); + + if(TSocketObj::IsValid(pSocketObj)) + { + ASSERT(rs && !(events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP))); + + UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN); + m_ioDispatcher.ModFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj); + } + + pSocketObj->Decrement(); +} + +VOID CTcpServer::OnCommand(const TDispContext* pContext, TDispCommand* pCmd) +{ + switch(pCmd->type) + { + case DISP_CMD_SEND: + HandleCmdSend(pContext, (CONNID)(pCmd->wParam)); + break; + case DISP_CMD_UNPAUSE: + HandleCmdUnpause(pContext, (CONNID)(pCmd->wParam)); + break; + case DISP_CMD_DISCONNECT: + HandleCmdDisconnect(pContext, (CONNID)(pCmd->wParam), (BOOL)pCmd->lParam); + break; + } +} + +VOID CTcpServer::HandleCmdSend(const TDispContext* pContext, CONNID dwConnID) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TSocketObj::IsValid(pSocketObj) && pSocketObj->IsPending()) + m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLOUT); +} + +VOID CTcpServer::HandleCmdUnpause(const TDispContext* pContext, CONNID dwConnID) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TSocketObj::IsValid(pSocketObj)) + return; + + if(BeforeUnpause(pSocketObj)) + m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLIN); + else + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED); +} + +VOID CTcpServer::HandleCmdDisconnect(const TDispContext* pContext, CONNID dwConnID, BOOL bForce) +{ + TSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TSocketObj::IsValid(pSocketObj)) + m_ioDispatcher.ProcessIo(pContext, pSocketObj, EPOLLHUP); +} + +BOOL CTcpServer::OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleReceive(pContext, (TSocketObj*)pv, RETRIVE_EVENT_FLAG_H(events)); +} + +BOOL CTcpServer::OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleSend(pContext, (TSocketObj*)pv, RETRIVE_EVENT_FLAG_H(events)); +} + +BOOL CTcpServer::OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleClose(pContext, (TSocketObj*)pv, SCF_CLOSE, events); +} + +BOOL CTcpServer::OnError(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleClose(pContext, (TSocketObj*)pv, SCF_ERROR, events); +} + +VOID CTcpServer::OnDispatchThreadStart(THR_ID tid) +{ + OnWorkerThreadStart(tid); +} + +VOID CTcpServer::OnDispatchThreadEnd(THR_ID tid) +{ + OnWorkerThreadEnd(tid); +} + +BOOL CTcpServer::HandleClose(const TDispContext* pContext, TSocketObj* pSocketObj, EnSocketCloseFlag enFlag, UINT events) +{ + EnSocketOperation enOperation = SO_CLOSE; + + if(events & _EPOLL_HUNGUP_EVENTS) + enOperation = SO_CLOSE; + else if(events & EPOLLIN) + enOperation = SO_RECEIVE; + else if(events & EPOLLOUT) + enOperation = SO_SEND; + + int iErrorCode = 0; + + if(enFlag == SCF_ERROR) + iErrorCode = ::SSO_GetError(pSocketObj->socket); + + AddFreeSocketObj(pSocketObj, enFlag, enOperation, iErrorCode); + + return TRUE; +} + +BOOL CTcpServer::HandleAccept(const TDispContext* pContext, UINT events) +{ + if(events & _EPOLL_ALL_ERROR_EVENTS) + { + ASSERT(!HasStarted()); + return FALSE; + } + + while(TRUE) + { + HP_SOCKADDR addr; + + socklen_t addrLen = (socklen_t)addr.AddrSize(); + SOCKET soClient = ::accept(m_soListens[pContext->GetIndex()], addr.Addr(), &addrLen); + + if(soClient == INVALID_SOCKET) + { + int code = ::WSAGetLastError(); + + switch(code) + { + case ERROR_WOULDBLOCK : return TRUE; + case ERROR_CONNABORTED : continue; + case ERROR_HANDLES_CLOSED : return FALSE; + default : ERROR_EXIT2(EXIT_CODE_SOFTWARE, code); + } + } + + if(!::fcntl_SETFL(soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC)) + { + ::ManualCloseSocket(soClient, SHUT_RDWR); + continue; + } + + CONNID dwConnID = 0; + + if(!m_bfActiveSockets.AcquireLock(dwConnID)) + { + ::ManualCloseSocket(soClient, SHUT_RDWR); + continue; + } + + TSocketObj* pSocketObj = GetFreeSocketObj(dwConnID, soClient); + + AddClientSocketObj(dwConnID, pSocketObj, addr); + + if(TRIGGER(FireAccept(pSocketObj)) == HR_ERROR) + { + AddFreeSocketObj(pSocketObj, SCF_NONE); + continue; + } + + UINT evts = (pSocketObj->IsPending() ? EPOLLOUT : 0) | (pSocketObj->IsPaused() ? 0 : EPOLLIN); + + if(!m_ioDispatcher.AddFD(pSocketObj->socket, evts | EPOLLRDHUP, pSocketObj)) + { + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_ACCEPT, ::WSAGetLastError()); + continue; + } + } + + return TRUE; +} + +BOOL CTcpServer::HandleReceive(const TDispContext* pContext, TSocketObj* pSocketObj, int flag) +{ + ASSERT(TSocketObj::IsValid(pSocketObj)); + + if(m_bMarkSilence) pSocketObj->activeTime = ::TimeGetTime(); + + CBufferPtr& buffer = m_rcBuffers[pContext->GetIndex()]; + + int reads = flag ? -1 : MAX_CONTINUE_READS; + + for(int i = 0; i < reads || reads < 0; i++) + { + if(pSocketObj->paused) + break; + + int rc = (int)read(pSocketObj->socket, buffer.Ptr(), buffer.Size()); + + if(rc > 0) + { + if(TRIGGER(FireReceive(pSocketObj, buffer.Ptr(), rc)) == HR_ERROR) + { + TRACE(" OnReceive() event return 'HR_ERROR', connection will be closed !", pSocketObj->connID); + + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED); + return FALSE; + } + } + else if(rc == 0) + { + AddFreeSocketObj(pSocketObj, SCF_CLOSE, SO_RECEIVE, SE_OK); + return FALSE; + } + else + { + ASSERT(rc == SOCKET_ERROR); + + int code = ::WSAGetLastError(); + + if(code == ERROR_WOULDBLOCK) + break; + + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, code); + return FALSE; + } + } + + return TRUE; +} + +BOOL CTcpServer::HandleSend(const TDispContext* pContext, TSocketObj* pSocketObj, int flag) +{ + ASSERT(TSocketObj::IsValid(pSocketObj)); + + if(!pSocketObj->IsPending()) + return TRUE; + + BOOL bBlocked = FALSE; + int writes = flag ? -1 : MAX_CONTINUE_WRITES; + + TBufferObjList& sndBuff = pSocketObj->sndBuff; + TItemPtr itPtr(sndBuff); + + for(int i = 0; i < writes || writes < 0; i++) + { + { + CReentrantCriSecLock locallock(pSocketObj->csSend); + itPtr = sndBuff.PopFront(); + } + + if(!itPtr.IsValid()) + break; + + ASSERT(!itPtr->IsEmpty()); + + if(!SendItem(pSocketObj, itPtr, bBlocked)) + return FALSE; + + if(bBlocked) + { + ASSERT(!itPtr->IsEmpty()); + + CReentrantCriSecLock locallock(pSocketObj->csSend); + sndBuff.PushFront(itPtr.Detach()); + + break; + } + } + + return TRUE; +} + +BOOL CTcpServer::SendItem(TSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked) +{ + while(!pItem->IsEmpty()) + { + int rc = (int)write(pSocketObj->socket, pItem->Ptr(), pItem->Size()); + + if(rc > 0) + { + if(TRIGGER(FireSend(pSocketObj, pItem->Ptr(), rc)) == HR_ERROR) + { + TRACE(" OnSend() event should not return 'HR_ERROR' !!", pSocketObj->connID); + ASSERT(FALSE); + } + + pItem->Reduce(rc); + } + else if(rc == SOCKET_ERROR) + { + int code = ::WSAGetLastError(); + + if(code == ERROR_WOULDBLOCK) + { + bBlocked = TRUE; + break; + } + else + { + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_SEND, code); + return FALSE; + } + } + else + ASSERT(FALSE); + } + + return TRUE; +} + +BOOL CTcpServer::Send(CONNID dwConnID, 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(dwConnID, &buffer, 1); +} + +BOOL CTcpServer::DoSendPackets(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; + } + + return DoSendPackets(pSocketObj, pBuffers, iCount); +} + +BOOL CTcpServer::DoSendPackets(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount) +{ + ASSERT(pSocketObj && pBuffers && iCount > 0); + + int result = NO_ERROR; + + if(pBuffers && iCount > 0) + { + CLocalSafeCounter localcounter(*pSocketObj); + CReentrantCriSecLock locallock(pSocketObj->csSend); + + if(TSocketObj::IsValid(pSocketObj)) + result = SendInternal(pSocketObj, pBuffers, iCount); + else + result = ERROR_OBJECT_NOT_FOUND; + } + else + result = ERROR_INVALID_PARAMETER; + + if(result != NO_ERROR) + ::SetLastError(result); + + return (result == NO_ERROR); +} + +int CTcpServer::SendInternal(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount) +{ + BOOL bPending = pSocketObj->IsPending(); + + for(int i = 0; i < iCount; i++) + { + int iBufLen = pBuffers[i].len; + + if(iBufLen > 0) + { + BYTE* pBuffer = (BYTE*)pBuffers[i].buf; + ASSERT(pBuffer); + + pSocketObj->sndBuff.Cat(pBuffer, iBufLen); + ASSERT(pSocketObj->sndBuff.Length() > 0); + } + } + + if(!bPending && pSocketObj->IsPending()) + { + if(!m_ioDispatcher.SendCommandByFD(pSocketObj->socket, DISP_CMD_SEND, pSocketObj->connID)) + return ::GetLastError(); + } + + return NO_ERROR; +} + +BOOL CTcpServer::SendSmallFile(CONNID dwConnID, 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(dwConnID, szBuf, 3); +} diff --git a/TcpServer.h b/TcpServer.h new file mode 100644 index 0000000..a13f44f --- /dev/null +++ b/TcpServer.h @@ -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; +}; diff --git a/UdpArqClient.cpp b/UdpArqClient.cpp new file mode 100644 index 0000000..9b6aa65 --- /dev/null +++ b/UdpArqClient.cpp @@ -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 diff --git a/UdpArqClient.h b/UdpArqClient.h new file mode 100644 index 0000000..d9e8cb2 --- /dev/null +++ b/UdpArqClient.h @@ -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; + + 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 diff --git a/UdpArqServer.cpp b/UdpArqServer.cpp new file mode 100644 index 0000000..6eac8d4 --- /dev/null +++ b/UdpArqServer.cpp @@ -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 diff --git a/UdpArqServer.h b/UdpArqServer.h new file mode 100644 index 0000000..2572606 --- /dev/null +++ b/UdpArqServer.h @@ -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; + using CArqSessionEx = CArqSessionExT; + using CArqSessionPool = CArqSessionPoolT; + using CRecvBufferMap = unordered_map; + + 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 diff --git a/UdpCast.cpp b/UdpCast.cpp new file mode 100644 index 0000000..43d22f1 --- /dev/null +++ b/UdpCast.cpp @@ -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(" 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(" 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 diff --git a/UdpCast.h b/UdpCast.h new file mode 100644 index 0000000..6ee3b91 --- /dev/null +++ b/UdpCast.h @@ -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 m_thWorker; +}; + +#endif diff --git a/UdpClient.cpp b/UdpClient.cpp new file mode 100644 index 0000000..82eab12 --- /dev/null +++ b/UdpClient.cpp @@ -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 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(" send 0 bytes (detect package succ)", m_dwConnID); + } + else + { + TRACE(" 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(" 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(" 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(" 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 diff --git a/UdpClient.h b/UdpClient.h new file mode 100644 index 0000000..6c5b40b --- /dev/null +++ b/UdpClient.h @@ -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 m_thWorker; +}; + +#endif diff --git a/UdpNode.cpp b/UdpNode.cpp new file mode 100644 index 0000000..d7b3732 --- /dev/null +++ b/UdpNode.cpp @@ -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(m_dwWorkerThreadCount); + + m_rcBuffers = make_unique(m_dwWorkerThreadCount); + for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwMaxDatagramSize);}); + + m_soListens = make_unique(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 diff --git a/UdpNode.h b/UdpNode.h new file mode 100644 index 0000000..67b2ca9 --- /dev/null +++ b/UdpNode.h @@ -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 diff --git a/UdpServer.cpp b/UdpServer.cpp new file mode 100644 index 0000000..af7510e --- /dev/null +++ b/UdpServer.cpp @@ -0,0 +1,1231 @@ +/* + * 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 "UdpServer.h" + +#ifdef _UDP_SUPPORT + +EnHandleResult CUdpServer::TriggerFireAccept(TUdpSocketObj* pSocketObj) +{ + EnHandleResult rs = TRIGGER(FireAccept(pSocketObj)); + + return rs; +} + +BOOL CUdpServer::Start(LPCTSTR lpszBindAddress, USHORT usPort) +{ + if(!CheckParams() || !CheckStarting()) + return FALSE; + + PrepareStart(); + + if(CreateListenSocket(lpszBindAddress, usPort)) + if(CreateWorkerThreads()) + if(StartAccept()) + { + m_enState = SS_STARTED; + return TRUE; + } + + EXECUTE_RESTORE_ERROR(Stop()); + + return FALSE; +} + +void CUdpServer::SetLastError(EnSocketError code, LPCSTR func, int ec) +{ + m_enLastError = code; + ::SetLastError(ec); +} + +BOOL CUdpServer::CheckParams() +{ + if ((m_enSendPolicy >= SP_PACK && m_enSendPolicy <= SP_DIRECT) && + (m_enOnSendSyncPolicy >= OSSP_NONE && m_enOnSendSyncPolicy <= OSSP_RECEIVE) && + ((int)m_dwMaxConnectionCount > 0 && m_dwMaxConnectionCount <= MAX_CONNECTION_COUNT) && + ((int)m_dwWorkerThreadCount > 0 && m_dwWorkerThreadCount <= MAX_WORKER_THREAD_COUNT) && + ((int)m_dwFreeSocketObjLockTime >= 1000) && + ((int)m_dwFreeSocketObjPool >= 0) && + ((int)m_dwFreeBufferObjPool >= 0) && + ((int)m_dwFreeSocketObjHold >= 0) && + ((int)m_dwFreeBufferObjHold >= 0) && + ((int)m_dwMaxDatagramSize > 0 && m_dwMaxDatagramSize <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE) && + ((int)m_dwPostReceiveCount > 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 CUdpServer::PrepareStart() +{ + m_bfActiveSockets.Reset(m_dwMaxConnectionCount); + m_lsFreeSocket.Reset(m_dwFreeSocketObjPool); + + m_bfObjPool.SetItemCapacity(m_dwMaxDatagramSize); + m_bfObjPool.SetPoolSize(m_dwFreeBufferObjPool); + m_bfObjPool.SetPoolHold(m_dwFreeBufferObjHold); + + m_bfObjPool.Prepare(); + + m_quSends = make_unique(m_dwWorkerThreadCount); + + m_rcBuffers = make_unique(m_dwWorkerThreadCount); + for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwMaxDatagramSize);}); + + m_soListens = make_unique(m_dwWorkerThreadCount); + for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock) {sock = INVALID_FD;}); +} + +BOOL CUdpServer::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 CUdpServer::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 CUdpServer::CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort) +{ + if(::IsStrEmpty(lpszBindAddress)) + lpszBindAddress = DEFAULT_IPV4_BIND_ADDRESS; + + HP_SOCKADDR addr; + + if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, addr)) + { + SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + for(DWORD i = 0; i < m_dwWorkerThreadCount; i++) + { + m_soListens[i] = socket(addr.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, addr.Addr(), addr.AddrSize()))) + { + SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + if(TRIGGER(FirePrepareListen(soListen)) == HR_ERROR) + { + SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED); + return FALSE; + } + } + + return TRUE; +} + +BOOL CUdpServer::CreateWorkerThreads() +{ + DWORD dwWorkerThreadCount = m_dwWorkerThreadCount +#ifdef USE_EXTERNAL_GC + + 1 +#endif + ; + + if(!m_ioDispatcher.Start(this, m_dwPostReceiveCount, dwWorkerThreadCount)) + { + SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + +#ifdef USE_EXTERNAL_GC + m_fdGCTimer = m_ioDispatcher.AddTimer(m_dwWorkerThreadCount, GC_CHECK_INTERVAL, this); + + if(IS_INVALID_FD(m_fdGCTimer)) + { + SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } +#endif + + return TRUE; +} + +BOOL CUdpServer::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))) + { + SetLastError(SE_SOCKE_ATTACH_TO_CP, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + } + +#ifdef USE_EXTERNAL_GC + m_fdGCTimer = m_ioDispatcher.AddTimer(m_dwWorkerThreadCount, GC_CHECK_INTERVAL, this); + + if(IS_INVALID_FD(m_fdGCTimer)) + { + SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } +#endif + + return TRUE; +} + +BOOL CUdpServer::Stop() +{ + if(!CheckStoping()) + return FALSE; + + SendCloseNotify(); + + CloseListenSocket(); + + DisconnectClientSocket(); + WaitForClientSocketClose(); + + WaitForWorkerThreadEnd(); + + ReleaseClientSocket(); + + FireShutdown(); + + ReleaseFreeSocket(); + ClearSendQueues(); + + Reset(); + + return TRUE; +} + +void CUdpServer::SendCloseNotify() +{ + if(!m_soListens) + return; + if(m_bfActiveSockets.Elements() == 0) + return; + + TUdpSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + { + CONNID connID = *it; + TUdpSocketObj* pSocketObj = FindSocketObj(connID); + + if(TUdpSocketObj::IsValid(pSocketObj)) + ::SendUdpCloseNotify(m_soListens[pSocketObj->index], pSocketObj->remoteAddr); + } + + ::WaitFor(30); +} + +void CUdpServer::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(70); + } +} + +void CUdpServer::DisconnectClientSocket() +{ + if(m_bfActiveSockets.Elements() == 0) + return; + + TUdpSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + Disconnect(*it); +} + +void CUdpServer::WaitForClientSocketClose() +{ + while(m_bfActiveSockets.Elements() > 0) + ::WaitFor(50); +} + +void CUdpServer::WaitForWorkerThreadEnd() +{ + m_ioDispatcher.Stop(); +} + +void CUdpServer::ReleaseClientSocket() +{ + VERIFY(m_bfActiveSockets.IsEmpty()); + m_bfActiveSockets.Reset(); + + CWriteLock locallock(m_csClientSocket); + m_mpClientAddr.clear(); +} + +void CUdpServer::ReleaseFreeSocket() +{ + m_lsFreeSocket.Clear(); + +#ifdef USE_EXTERNAL_GC + if(IS_VALID_FD(m_fdGCTimer)) + { + close(m_fdGCTimer); + m_fdGCTimer = INVALID_FD; + } +#endif + + ReleaseGCSocketObj(TRUE); + VERIFY(m_lsGCSocket.IsEmpty()); +} + +void CUdpServer::ClearSendQueues() +{ + if(m_quSends) for_each(m_quSends.get(), m_quSends.get() + m_dwWorkerThreadCount, [](CSendQueue& queue) {queue.UnsafeClear();}); +} + +void CUdpServer::Reset() +{ + m_phSocket.Reset(); + m_bfObjPool.Clear(); + + m_soListens = nullptr; + m_rcBuffers = nullptr; + m_quSends = nullptr; + + m_enState = SS_STOPPED; + + m_evWait.SyncNotifyAll(); +} + +TUdpSocketObj* CUdpServer::GetFreeSocketObj(CONNID dwConnID) +{ + DWORD dwIndex; + TUdpSocketObj* pSocketObj = nullptr; + + if(m_lsFreeSocket.TryLock(&pSocketObj, dwIndex)) + { + if(::GetTimeGap32(pSocketObj->freeTime) >= m_dwFreeSocketObjLockTime) + VERIFY(m_lsFreeSocket.ReleaseLock(nullptr, dwIndex)); + else + { + VERIFY(m_lsFreeSocket.ReleaseLock(pSocketObj, dwIndex)); + pSocketObj = nullptr; + } + } + + if(!pSocketObj) pSocketObj = CreateSocketObj(); + pSocketObj->Reset(dwConnID); + + return pSocketObj; +} + +TUdpSocketObj* CUdpServer::CreateSocketObj() +{ + return TUdpSocketObj::Construct(m_phSocket, m_bfObjPool); +} + +void CUdpServer::DeleteSocketObj(TUdpSocketObj* pSocketObj) +{ + TUdpSocketObj::Destruct(pSocketObj); +} + +void CUdpServer::AddFreeSocketObj(TUdpSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode, BOOL bNotify) +{ + if(!InvalidSocketObj(pSocketObj)) + return; + + CloseClientSocketObj(pSocketObj, enFlag, enOperation, iErrorCode, bNotify); + + { + m_bfActiveSockets.Remove(pSocketObj->connID); + + CWriteLock locallock(m_csClientSocket); + m_mpClientAddr.erase(&pSocketObj->remoteAddr); + } + + m_ioDispatcher.DelTimer(pSocketObj->index, pSocketObj->fdTimer); + TUdpSocketObj::Release(pSocketObj); + +#ifndef USE_EXTERNAL_GC + ReleaseGCSocketObj(); +#endif + + if(!m_lsFreeSocket.TryPut(pSocketObj)) + m_lsGCSocket.PushBack(pSocketObj); +} + +void CUdpServer::ReleaseGCSocketObj(BOOL bForce) +{ + ::ReleaseGCObj(m_lsGCSocket, m_dwFreeSocketObjLockTime, bForce); +} + +BOOL CUdpServer::InvalidSocketObj(TUdpSocketObj* pSocketObj) +{ + return TUdpSocketObj::InvalidSocketObj(pSocketObj); +} + +void CUdpServer::AddClientSocketObj(int idx, CONNID dwConnID, TUdpSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr) +{ + ASSERT(FindSocketObj(dwConnID) == nullptr); + + pSocketObj->index = idx; + pSocketObj->pHolder = this; + pSocketObj->connTime = ::TimeGetTime(); + pSocketObj->activeTime = pSocketObj->connTime; + + if(IsNeedDetectConnection()) + pSocketObj->fdTimer = m_ioDispatcher.AddTimer(pSocketObj->index, m_dwDetectInterval, pSocketObj); + + remoteAddr.Copy(pSocketObj->remoteAddr); + pSocketObj->SetConnected(); + + VERIFY(m_bfActiveSockets.ReleaseLock(dwConnID, pSocketObj)); + + CWriteLock locallock(m_csClientSocket); + m_mpClientAddr[&pSocketObj->remoteAddr] = dwConnID; +} + +TUdpSocketObj* CUdpServer::FindSocketObj(CONNID dwConnID) +{ + TUdpSocketObj* pSocketObj = nullptr; + + if(m_bfActiveSockets.Get(dwConnID, &pSocketObj) != TUdpSocketObjPtrPool::GR_VALID) + pSocketObj = nullptr; + + return pSocketObj; +} + +CONNID CUdpServer::FindConnectionID(const HP_SOCKADDR* pAddr) +{ + CONNID dwConnID = 0; + + CReadLock locallock(m_csClientSocket); + + TSockAddrMapCI it = m_mpClientAddr.find(pAddr); + if(it != m_mpClientAddr.end()) + dwConnID = it->second; + + return dwConnID; +} + +void CUdpServer::CloseClientSocketObj(TUdpSocketObj* pSocketObj, EnSocketCloseFlag enFlag, EnSocketOperation enOperation, int iErrorCode, BOOL bNotify) +{ + ASSERT(TUdpSocketObj::IsExist(pSocketObj)); + + SOCKET soListen = m_soListens[pSocketObj->index]; + + if(bNotify && soListen != INVALID_SOCKET) + ::SendUdpCloseNotify(soListen, pSocketObj->remoteAddr); + + if(enFlag == SCF_CLOSE) + FireClose(pSocketObj, SO_CLOSE, SE_OK); + else if(enFlag == SCF_ERROR) + FireClose(pSocketObj, enOperation, iErrorCode); +} + +BOOL CUdpServer::GetListenAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) +{ + ASSERT(lpszAddress != nullptr && iAddressLen > 0); + + if(!HasStarted()) + { + ::SetLastError(ERROR_INVALID_STATE); + return FALSE; + } + + return ::GetSocketLocalAddress(m_soListens[0], lpszAddress, iAddressLen, usPort); +} + +BOOL CUdpServer::GetLocalAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) +{ + ASSERT(lpszAddress != nullptr && iAddressLen > 0); + + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TUdpSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return ::GetSocketLocalAddress(m_soListens[pSocketObj->index], lpszAddress, iAddressLen, usPort); +} + +BOOL CUdpServer::GetRemoteAddress(CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) +{ + ASSERT(lpszAddress != nullptr && iAddressLen > 0); + + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TUdpSocketObj::IsExist(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + ADDRESS_FAMILY usFamily; + return ::sockaddr_IN_2_A(pSocketObj->remoteAddr, usFamily, lpszAddress, iAddressLen, usPort); +} + +BOOL CUdpServer::SetConnectionExtra(CONNID dwConnID, PVOID pExtra) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionExtra(pSocketObj, pExtra); +} + +BOOL CUdpServer::SetConnectionExtra(TUdpSocketObj* pSocketObj, PVOID pExtra) +{ + if(!TUdpSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->extra = pExtra; + return TRUE; + } + + return FALSE; +} + +BOOL CUdpServer::GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionExtra(pSocketObj, ppExtra); +} + +BOOL CUdpServer::GetConnectionExtra(TUdpSocketObj* pSocketObj, PVOID* ppExtra) +{ + ASSERT(ppExtra != nullptr); + + if(!TUdpSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppExtra = pSocketObj->extra; + return TRUE; + } + + return FALSE; +} + +BOOL CUdpServer::SetConnectionReserved(CONNID dwConnID, PVOID pReserved) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionReserved(pSocketObj, pReserved); +} + +BOOL CUdpServer::SetConnectionReserved(TUdpSocketObj* pSocketObj, PVOID pReserved) +{ + if(!TUdpSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->reserved = pReserved; + return TRUE; + } + + return FALSE; +} + +BOOL CUdpServer::GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionReserved(pSocketObj, ppReserved); +} + +BOOL CUdpServer::GetConnectionReserved(TUdpSocketObj* pSocketObj, PVOID* ppReserved) +{ + ASSERT(ppReserved != nullptr); + + if(!TUdpSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppReserved = pSocketObj->reserved; + return TRUE; + } + + return FALSE; +} + +BOOL CUdpServer::SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + return SetConnectionReserved2(pSocketObj, pReserved2); +} + +BOOL CUdpServer::SetConnectionReserved2(TUdpSocketObj* pSocketObj, PVOID pReserved2) +{ + if(!TUdpSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + pSocketObj->reserved2 = pReserved2; + return TRUE; + } + + return FALSE; +} + +BOOL CUdpServer::GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + return GetConnectionReserved2(pSocketObj, ppReserved2); +} + +BOOL CUdpServer::GetConnectionReserved2(TUdpSocketObj* pSocketObj, PVOID* ppReserved2) +{ + ASSERT(ppReserved2 != nullptr); + + if(!TUdpSocketObj::IsExist(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + *ppReserved2 = pSocketObj->reserved2; + return TRUE; + } + + return FALSE; +} + +BOOL CUdpServer::IsPauseReceive(CONNID dwConnID, BOOL& bPaused) +{ + ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + + bPaused = FALSE; + + return FALSE; +} + +BOOL CUdpServer::IsConnected(CONNID dwConnID) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TUdpSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return pSocketObj->HasConnected(); +} + +BOOL CUdpServer::GetPendingDataLength(CONNID dwConnID, int& iPending) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TUdpSocketObj::IsValid(pSocketObj)) + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + else + { + iPending = pSocketObj->Pending(); + return TRUE; + } + + return FALSE; +} + +DWORD CUdpServer::GetConnectionCount() +{ + return m_bfActiveSockets.Elements(); +} + +BOOL CUdpServer::GetAllConnectionIDs(CONNID pIDs[], DWORD& dwCount) +{ + return m_bfActiveSockets.GetAllElementIndexes(pIDs, dwCount); +} + +BOOL CUdpServer::GetConnectPeriod(CONNID dwConnID, DWORD& dwPeriod) +{ + BOOL isOK = TRUE; + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TUdpSocketObj::IsValid(pSocketObj)) + dwPeriod = ::GetTimeGap32(pSocketObj->connTime); + else + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + isOK = FALSE; + } + + return isOK; +} + +BOOL CUdpServer::GetSilencePeriod(CONNID dwConnID, DWORD& dwPeriod) +{ + if(!m_bMarkSilence) + return FALSE; + + BOOL isOK = TRUE; + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(TUdpSocketObj::IsValid(pSocketObj)) + dwPeriod = ::GetTimeGap32(pSocketObj->activeTime); + else + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + isOK = FALSE; + } + + return isOK; +} + +BOOL CUdpServer::Disconnect(CONNID dwConnID, BOOL bForce) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TUdpSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + return m_ioDispatcher.SendCommandByIndex(pSocketObj->index, DISP_CMD_DISCONNECT, dwConnID, bForce); +} + +BOOL CUdpServer::DisconnectLongConnections(DWORD dwPeriod, BOOL bForce) +{ + if(dwPeriod > MAX_CONNECTION_PERIOD) + return FALSE; + if(m_bfActiveSockets.Elements() == 0) + return TRUE; + + DWORD now = ::TimeGetTime(); + + TUdpSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + { + CONNID connID = *it; + TUdpSocketObj* pSocketObj = FindSocketObj(connID); + + if(TUdpSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->connTime) >= (int)dwPeriod) + Disconnect(connID, bForce); + } + + return TRUE; +} + +BOOL CUdpServer::DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce) +{ + if(!m_bMarkSilence) + return FALSE; + if(dwPeriod > MAX_CONNECTION_PERIOD) + return FALSE; + if(m_bfActiveSockets.Elements() == 0) + return TRUE; + + DWORD now = ::TimeGetTime(); + + TUdpSocketObjPtrPool::IndexSet indexes; + m_bfActiveSockets.CopyIndexes(indexes); + + for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it) + { + CONNID connID = *it; + TUdpSocketObj* pSocketObj = FindSocketObj(connID); + + if(TUdpSocketObj::IsValid(pSocketObj) && (int)(now - pSocketObj->activeTime) >= (int)dwPeriod) + Disconnect(connID, bForce); + } + + return TRUE; +} + +BOOL CUdpServer::PauseReceive(CONNID dwConnID, BOOL bPause) +{ + ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL CUdpServer::OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) +{ + if(pv == &m_soListens[pContext->GetIndex()]) + return TRUE; + else if(pv == this) + { + ReleaseGCSocketObj(FALSE); + ::ReadTimer(m_fdGCTimer); + + return FALSE; + } + + if(!(events & _EPOLL_ALL_ERROR_EVENTS)) + DetectConnection(pv); + + return FALSE; +} + +VOID CUdpServer::OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) +{ + +} + +VOID CUdpServer::OnCommand(const TDispContext* pContext, TDispCommand* pCmd) +{ + CONNID dwConnID = (CONNID)(pCmd->wParam); + + switch(pCmd->type) + { + case DISP_CMD_SEND: + HandleCmdSend(dwConnID, (int)(pCmd->lParam)); + break; + case DISP_CMD_DISCONNECT: + HandleCmdDisconnect(dwConnID, (BOOL)pCmd->lParam); + break; + case DISP_CMD_TIMEOUT: + HandleCmdTimeout(dwConnID); + break; + } +} + +VOID CUdpServer::HandleCmdDisconnect(CONNID dwConnID, BOOL bForce) +{ + AddFreeSocketObj(FindSocketObj(dwConnID), SCF_CLOSE); +} + +VOID CUdpServer::HandleCmdTimeout(CONNID dwConnID) +{ + AddFreeSocketObj(FindSocketObj(dwConnID), SCF_CLOSE, SO_UNKNOWN, 0, FALSE); +} + +BOOL CUdpServer::OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleReceive(pContext, RETRIVE_EVENT_FLAG_H(events)); +} + +BOOL CUdpServer::OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleSend(pContext, RETRIVE_EVENT_FLAG_H(events)); +} + +BOOL CUdpServer::OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleClose(nullptr, SO_CLOSE, 0); +} + +BOOL CUdpServer::OnError(const TDispContext* pContext, PVOID pv, UINT events) +{ + return HandleClose(nullptr, SO_CLOSE, -1); +} + +VOID CUdpServer::OnDispatchThreadStart(THR_ID tid) +{ + OnWorkerThreadStart(tid); +} + +VOID CUdpServer::OnDispatchThreadEnd(THR_ID tid) +{ + OnWorkerThreadEnd(tid); +} + +BOOL CUdpServer::HandleClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode) +{ + if(!HasStarted()) + return FALSE; + else if(pSocketObj == nullptr) + { + TRACE("HandleClose() -> OP: %d, EC: %d", enOperation, iErrorCode); + return TRUE; + } + + if(iErrorCode == -1) + iErrorCode = ::SSO_GetError(m_soListens[pSocketObj->index]); + + EnSocketCloseFlag enFlag = IS_NO_ERROR(iErrorCode) ? SCF_CLOSE : SCF_ERROR; + + AddFreeSocketObj(pSocketObj, enFlag, enOperation, iErrorCode); + + return FALSE; +} + +BOOL CUdpServer::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) + { + CONNID dwConnID = FindConnectionID(&addr); + + if(dwConnID == 0) + { + if(rc > iBufferLen) + continue; + + if((dwConnID = HandleAccept(pContext, addr)) == 0) + continue; + } + + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + ASSERT(pSocketObj->index == idx); + + if(!TUdpSocketObj::IsValid(pSocketObj)) + continue; + + if(rc == 0) + { + HandleZeroBytes(pSocketObj); + continue; + } + + if(rc > iBufferLen) + { + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ERROR_BAD_LENGTH); + continue; + } + + if(::IsUdpCloseNotify(buffer.Ptr(), rc)) + { + AddFreeSocketObj(pSocketObj, SCF_CLOSE, SO_CLOSE, SE_OK, FALSE); + continue; + } + + if(TRIGGER(FireReceive(pSocketObj, buffer.Ptr(), rc)) == HR_ERROR) + { + TRACE(" OnReceive() event return 'HR_ERROR', connection will be closed !", dwConnID); + + AddFreeSocketObj(pSocketObj, SCF_ERROR, SO_RECEIVE, ENSURE_ERROR_CANCELLED);; + continue; + } + } + else if(rc == SOCKET_ERROR) + { + int code = ::WSAGetLastError(); + + if(code == ERROR_WOULDBLOCK) + break; + else if(!HandleClose(nullptr, SO_RECEIVE, code)) + return FALSE; + } + else + { + ASSERT(FALSE); + } + } + + return TRUE; +} + +CONNID CUdpServer::HandleAccept(const TDispContext* pContext, HP_SOCKADDR& addr) +{ + int idx = pContext->GetIndex(); + CONNID dwConnID = 0; + TUdpSocketObj* pSocketObj = nullptr; + + if(!m_bfActiveSockets.AcquireLock(dwConnID)) + { + ::SendUdpCloseNotify(m_soListens[idx], addr); + return 0; + } + + pSocketObj = GetFreeSocketObj(dwConnID); + AddClientSocketObj(idx, dwConnID, pSocketObj, addr); + + if(TriggerFireAccept(pSocketObj) == HR_ERROR) + { + AddFreeSocketObj(pSocketObj); + dwConnID = 0; + } + + return dwConnID; +} + +void CUdpServer::HandleZeroBytes(TUdpSocketObj* pSocketObj) +{ + TRACE(" recv 0 bytes (detect package)", pSocketObj->connID); + + pSocketObj->detectFails = 0; + +#if defined(DEBUG_TRACE) + int rc = (int) +#endif + sendto(m_soListens[pSocketObj->index], nullptr, 0, 0, pSocketObj->remoteAddr.Addr(), pSocketObj->remoteAddr.AddrSize()); + + TRACE(" send 0 bytes (detect ack package - %s)", pSocketObj->connID, IS_HAS_ERROR(rc) ? "fail" : "succ"); +} + +BOOL CUdpServer::HandleSend(const TDispContext* pContext, int flag) +{ + CSendQueue& quSend = m_quSends[pContext->GetIndex()]; + CONNID dwConnID = 0; + + while(quSend.PopFront(&dwConnID)) + HandleCmdSend(dwConnID, flag); + + return TRUE; +} + +VOID CUdpServer::HandleCmdSend(CONNID dwConnID, int flag) +{ + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TUdpSocketObj::IsValid(pSocketObj) || !pSocketObj->IsPending()) + return; + + BOOL bBlocked = FALSE; + int writes = flag ? -1 : MAX_CONTINUE_WRITES; + + TBufferObjList& sndBuff = pSocketObj->sndBuff; + TItemPtr itPtr(sndBuff); + + for(int i = 0; i < writes || writes < 0; i++) + { + { + CReentrantCriSecLock locallock(pSocketObj->csSend); + itPtr = sndBuff.PopFront(); + } + + if(!itPtr.IsValid()) + break; + + ASSERT(!itPtr->IsEmpty()); + + if(!SendItem(pSocketObj, itPtr, bBlocked)) + return; + + if(bBlocked) + { + { + CReentrantCriSecLock locallock(pSocketObj->csSend); + sndBuff.PushFront(itPtr.Detach()); + } + + m_quSends[pSocketObj->index].PushBack(dwConnID); + + break; + } + } + + if(!bBlocked && pSocketObj->IsPending()) + VERIFY(m_ioDispatcher.SendCommandByIndex(pSocketObj->index, DISP_CMD_SEND, dwConnID)); +} + +BOOL CUdpServer::SendItem(TUdpSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked) +{ + int rc = (int)sendto(m_soListens[pSocketObj->index], pItem->Ptr(), pItem->Size(), 0, pSocketObj->remoteAddr.Addr(), pSocketObj->remoteAddr.AddrSize()); + + if(rc > 0) + { + ASSERT(rc == pItem->Size()); + + if(TRIGGER(FireSend(pSocketObj, pItem->Ptr(), rc)) == HR_ERROR) + { + TRACE(" OnSend() event should not return 'HR_ERROR' !!", pSocketObj->connID); + ASSERT(FALSE); + } + } + else if(rc == SOCKET_ERROR) + { + int code = ::WSAGetLastError(); + + if(code == ERROR_WOULDBLOCK) + bBlocked = TRUE; + else if(!HandleClose(pSocketObj, SO_SEND, code)) + return FALSE; + } + else + ASSERT(FALSE); + + return TRUE; +} + +BOOL CUdpServer::Send(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset) +{ + ASSERT(pBuffer && iLength > 0 && iLength <= (int)m_dwMaxDatagramSize); + + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + return DoSend(pSocketObj, pBuffer, iLength, iOffset); +} + +BOOL CUdpServer::DoSend(TUdpSocketObj* pSocketObj, const BYTE* pBuffer, int iLength, int iOffset) +{ + int result = NO_ERROR; + + if(TUdpSocketObj::IsValid(pSocketObj)) + { + if(pBuffer && iLength > 0 && iLength <= (int)m_dwMaxDatagramSize) + { + if(iOffset != 0) pBuffer += iOffset; + + TItemPtr itPtr(m_bfObjPool, m_bfObjPool.PickFreeItem()); + itPtr->Cat(pBuffer, iLength); + + result = SendInternal(pSocketObj, itPtr); + } + else + result = ERROR_INVALID_PARAMETER; + } + else + result = ERROR_OBJECT_NOT_FOUND; + + if(result != NO_ERROR) + ::SetLastError(result); + + return (result == NO_ERROR); +} + +BOOL CUdpServer::SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount) +{ + ASSERT(pBuffers && iCount > 0); + + if(!pBuffers || iCount <= 0) + return ERROR_INVALID_PARAMETER; + + TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID); + + if(!TUdpSocketObj::IsValid(pSocketObj)) + { + ::SetLastError(ERROR_OBJECT_NOT_FOUND); + return FALSE; + } + + int result = NO_ERROR; + int iLength = 0; + int iMaxLen = (int)m_dwMaxDatagramSize; + + TItemPtr itPtr(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) + itPtr->Cat(pBuffer, iBufLen); + else + break; + } + } + + if(iLength > 0 && iLength <= iMaxLen) + result = SendInternal(pSocketObj, itPtr); + else + result = ERROR_INCORRECT_SIZE; + + if(result != NO_ERROR) + ::SetLastError(result); + + return (result == NO_ERROR); + +} + +int CUdpServer::SendInternal(TUdpSocketObj* pSocketObj, TItemPtr& itPtr) +{ + BOOL bPending; + + { + CLocalSafeCounter localcounter(*pSocketObj); + CReentrantCriSecLock locallock(pSocketObj->csSend); + + if(!TUdpSocketObj::IsValid(pSocketObj)) + return ERROR_OBJECT_NOT_FOUND; + + bPending = pSocketObj->IsPending(); + + pSocketObj->sndBuff.PushBack(itPtr.Detach()); + ASSERT(pSocketObj->sndBuff.Length() > 0); + } + + if(!bPending && pSocketObj->IsPending()) + VERIFY(m_ioDispatcher.SendCommandByIndex(pSocketObj->index, DISP_CMD_SEND, pSocketObj->connID)); + + return NO_ERROR; +} + +void CUdpServer::DetectConnection(PVOID pv) +{ + TUdpSocketObj* pSocketObj = (TUdpSocketObj*)pv; + + if(TUdpSocketObj::IsValid(pSocketObj)) + { + CUdpServer* pServer = (CUdpServer*)pSocketObj->pHolder; + + if(pSocketObj->detectFails >= pServer->m_dwDetectAttempts) + VERIFY(m_ioDispatcher.SendCommandByIndex(pSocketObj->index, DISP_CMD_TIMEOUT, pSocketObj->connID)); + else + ::InterlockedIncrement(&pSocketObj->detectFails); + + ::ReadTimer(pSocketObj->fdTimer); + } +} + +#endif diff --git a/UdpServer.h b/UdpServer.h new file mode 100644 index 0000000..b42caa9 --- /dev/null +++ b/UdpServer.h @@ -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; + using CSendQueue = CCASSimpleQueue; + using CSendQueuesPtr = unique_ptr; + +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 diff --git a/brotli/decode.h b/brotli/decode.h new file mode 100644 index 0000000..af1aa23 --- /dev/null +++ b/brotli/decode.h @@ -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 +#include +#include + +#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_ */ diff --git a/brotli/encode.h b/brotli/encode.h new file mode 100644 index 0000000..46860cd --- /dev/null +++ b/brotli/encode.h @@ -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 +#include +#include + +#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_ */ diff --git a/brotli/port.h b/brotli/port.h new file mode 100644 index 0000000..0d50019 --- /dev/null +++ b/brotli/port.h @@ -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 */ + +/* >>> >>> >>> 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_ */ diff --git a/brotli/shared_dictionary.h b/brotli/shared_dictionary.h new file mode 100644 index 0000000..2970c2d --- /dev/null +++ b/brotli/shared_dictionary.h @@ -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 +#include + +#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_ */ diff --git a/brotli/types.h b/brotli/types.h new file mode 100644 index 0000000..eff1a3c --- /dev/null +++ b/brotli/types.h @@ -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 /* 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 +#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_ */ diff --git a/common/BufferPool.cpp b/common/BufferPool.cpp new file mode 100644 index 0000000..447b698 --- /dev/null +++ b/common/BufferPool.cpp @@ -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(); +} diff --git a/common/BufferPool.h b/common/BufferPool.h new file mode 100644 index 0000000..6957f2d --- /dev/null +++ b/common/BufferPool.h @@ -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 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 void DestructItemT(T* pItem) +{ + ASSERT(pItem != nullptr); + + CPrivateHeap& heap = pItem->GetPrivateHeap(); + + ::DestructObject(pItem); + heap.Free(pItem); +} + +struct TItem +{ + template friend struct TSimpleList; + template friend class CNodePoolT; + template 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 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& Shift(TSimpleList& 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) + +private: + void Reset() + { + pFront = nullptr; + pBack = nullptr; + size = 0; + } + +private: + int size; + T* pFront; + T* pBack; +}; + +template class CNodePoolT +{ +public: + void PutFreeItem(T* pItem) + { + ASSERT(pItem != nullptr); + + if(!m_lsFreeItem.TryPut(pItem)) + T::Destruct(pItem); + } + + void PutFreeItem(TSimpleList& 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 m_lsFreeItem; +}; + +template const DWORD CNodePoolT::DEFAULT_ITEM_CAPACITY = TItem::DEFAULT_ITEM_CAPACITY; +template const DWORD CNodePoolT::DEFAULT_POOL_SIZE = DEFAULT_BUFFER_CACHE_POOL_SIZE; +template const DWORD CNodePoolT::DEFAULT_POOL_HOLD = DEFAULT_BUFFER_CACHE_POOL_HOLD; + +using CItemPool = CNodePoolT; + +template struct TItemListT : public TSimpleList +{ + using __super = TSimpleList; + +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& 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& GetItemPool() {return itPool;} + +public: + TItemListT(CNodePoolT& pool) : itPool(pool) + { + } + +private: + CNodePoolT& itPool; +}; + +using TItemList = TItemListT; + +template::type>::value>> +struct TItemListExT : public TItemListT +{ + using __super = TItemListT; + +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& 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& 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::type Length() const {return length;} + + int IncreaseLength (int length) {return (this->length += length);} + int ReduceLength (int length) {return (this->length -= length);} + +public: + TItemListExT(CNodePoolT& pool) : TItemListT(pool), length(0) + { + } + + ~TItemListExT() + { + ASSERT(length >= 0); + } + + DECLARE_NO_COPY_CLASS(TItemListExT) + +private: + length_t length; +}; + +using TItemListEx = TItemListExT; +using TItemListExV = TItemListExT; + +template 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& pool, T* pItem = nullptr) + : itPool(pool), m_pItem(pItem) + { + + } + + TItemPtrT(TItemListT& ls, T* pItem = nullptr) + : itPool(ls.GetItemPool()), m_pItem(pItem) + { + + } + + ~TItemPtrT() + { + Reset(); + } + + DECLARE_NO_COPY_CLASS(TItemPtrT) + +private: + CNodePoolT& itPool; + T* m_pItem; +}; + +using TItemPtr = TItemPtrT; + +class CBufferPool; + +struct TBuffer +{ + template 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; + using TBufferQueue = CCASQueue; + using TBufferCache = CRingCache; + +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; +}; diff --git a/common/BufferPtr.h b/common/BufferPtr.h new file mode 100644 index 0000000..8e0eb0c --- /dev/null +++ b/common/BufferPtr.h @@ -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 +#include + +template +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 CBufferPtrT(const CBufferPtrT& 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 CBufferPtrT& Copy(const CBufferPtrT& 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 CBufferPtrT& Cat(const CBufferPtrT& 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 bool Equal(const CBufferPtrT& 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 bool operator == (const CBufferPtrT& other) {return Equal(other);} + CBufferPtrT& operator = (const CBufferPtrT& other) {return Copy(other);} + template CBufferPtrT& operator = (const CBufferPtrT& 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 CCharBufferPtr; +typedef CBufferPtrT CWCharBufferPtr; +typedef CBufferPtrT CByteBufferPtr; +typedef CByteBufferPtr CBufferPtr; + +#ifdef _UNICODE + typedef CWCharBufferPtr CTCharBufferPtr; +#else + typedef CCharBufferPtr CTCharBufferPtr; +#endif diff --git a/common/CriSec.h b/common/CriSec.h new file mode 100644 index 0000000..dbcecf9 --- /dev/null +++ b/common/CriSec.h @@ -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 +#include + +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 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 CLocalLock +{ +public: + CLocalLock(CLockObj& obj) : m_lock(obj) {m_lock.Lock();} + ~CLocalLock() {m_lock.Unlock();} +private: + CLockObj& m_lock; +}; + +template 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 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; +using CReentrantSpinLock = CLocalLock; +using CFakeLock = CLocalLock; + +using CCriSec = mutex; +using CCriSecLock = lock_guard; +using CCriSecLock2 = unique_lock; +using CCriSecTryLock = CMTXTryLock; + +using CMTX = CCriSec; +using CMutexLock = CCriSecLock; +using CMutexLock2 = CCriSecLock2; +using CMutexTryLock = CCriSecTryLock; + +using CReentrantCriSec = recursive_mutex; +using CReentrantCriSecLock = lock_guard; +using CReentrantCriSecLock2 = unique_lock; +using CReentrantCriSecTryLock = CMTXTryLock; + +using CReentrantMTX = CReentrantCriSec; +using CReentrantMutexLock = CReentrantCriSecLock; +using CReentrantMutexLock2 = CReentrantCriSecLock2; +using CReentrantMutexTryLock = CReentrantCriSecTryLock; + +template::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::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 CLocalCounter +{ +public: + CLocalCounter(CCounter& obj) : m_counter(obj) {m_counter.Increment();} + ~CLocalCounter() {m_counter.Decrement();} +private: + CCounter& m_counter; +}; + +using CSafeCounter = CSafeCounterT; +using CSafeBigCounter = CSafeCounterT; +using CUnsafeCounter = CUnsafeCounterT; +using CUnsafeBigCounter = CUnsafeCounterT; + +using CLocalSafeCounter = CLocalCounter; +using CLocalSafeBigCounter = CLocalCounter; +using CLocalUnsafeCounter = CLocalCounter; +using CLocalUnsafeBigCounter = CLocalCounter; diff --git a/common/Event.cpp b/common/Event.cpp new file mode 100644 index 0000000..9cc69fe --- /dev/null +++ b/common/Event.cpp @@ -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" diff --git a/common/Event.h b/common/Event.h new file mode 100644 index 0000000..5b54fbb --- /dev/null +++ b/common/Event.h @@ -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 +#include +#include + +#include +#include +#include + +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 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; +using CSemaphoreEvent = CCounterEvent; +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; +}; diff --git a/common/FileHelper.cpp b/common/FileHelper.cpp new file mode 100644 index 0000000..a8154ca --- /dev/null +++ b/common/FileHelper.cpp @@ -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 + +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)); +} diff --git a/common/FileHelper.h b/common/FileHelper.h new file mode 100644 index 0000000..4f71c8e --- /dev/null +++ b/common/FileHelper.h @@ -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 +#include +#include + +#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; +}; diff --git a/common/FuncHelper.cpp b/common/FuncHelper.cpp new file mode 100644 index 0000000..85c24e3 --- /dev/null +++ b/common/FuncHelper.cpp @@ -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 +#include +#include +#include +#include + +#if !defined(__ANDROID__) + #include + #include + #ifdef __GNUC__ + #include + #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; +} diff --git a/common/FuncHelper.h b/common/FuncHelper.h new file mode 100644 index 0000000..e522994 --- /dev/null +++ b/common/FuncHelper.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 char (&_ArraySizeHelper(T(&arr)[N]))[N]; +template 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::value>> +inline bool IS_INFINITE(T v) +{ + return v == (T)INFINITE; +} + +template::value>> +inline bool IS_HAS_ERROR(T v) +{ + return v == (T)HAS_ERROR; +} + +template::value>> +inline bool IS_NO_ERROR(T v) +{ + return v == (T)NO_ERROR; +} + +template +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, decay_t>::value && is_same, decay_t>::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 +inline T* ConstructObject(T* p, A&& ... args) +{ + return new (p) T(forward(args) ...); +} + +template +inline void DestructObject(T* p) +{ + p->T::~T(); +} + +template, decay_t>::value>> +inline void CopyPlainObject(T1* p1, const T2* p2) +{ + CopyMemory(p1, p2, sizeof(T1)); +} + +template::value && (is_same::value || is_same::value)>> +C* _n_2_c(T value, C* lpszDest, int radix) +{ + static const C* dig = "0123456789abcdefghijklmnopqrstuvwxyz"; + + bool neg = false; + + if(is_signed::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((v), (p), (r)) +#define ltoa(v, p, r) _n_2_c((v), (p), (r)) +#define lltoa(v, p, r) _n_2_c((v), (p), (r)) +#define uitoa(v, p, r) _n_2_c((v), (p), (r)) +#define ultoa(v, p, r) _n_2_c((v), (p), (r)) +#define ulltoa(v, p, r) _n_2_c((v), (p), (r)) +#define itow(v, p, r) _n_2_c((v), (p), (r)) +#define ltow(v, p, r) _n_2_c((v), (p), (r)) +#define lltow(v, p, r) _n_2_c((v), (p), (r)) +#define uitow(v, p, r) _n_2_c((v), (p), (r)) +#define ultow(v, p, r) _n_2_c((v), (p), (r)) +#define ulltow(v, p, r) _n_2_c((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));} diff --git a/common/GeneralHelper.h b/common/GeneralHelper.h new file mode 100644 index 0000000..a683fb1 --- /dev/null +++ b/common/GeneralHelper.h @@ -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" diff --git a/common/IODispatcher.cpp b/common/IODispatcher.cpp new file mode 100644 index 0000000..45719c5 --- /dev/null +++ b/common/IODispatcher.cpp @@ -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 +#include + +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(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(); + + 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 pEvents = make_unique(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; +} diff --git a/common/IODispatcher.h b/common/IODispatcher.h new file mode 100644 index 0000000..2bc6622 --- /dev/null +++ b/common/IODispatcher.h @@ -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 +#include +#include + +#include + +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; + using CWorkerThread = CThread; + +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 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, TDispCommand*>::value>> + BOOL SendCommandsByIndex(int idx, const _List& cmds) + { + TDispContext& ctx = GetContextByIndex(idx); + return SendCommands(ctx, cmds); + } + + template, TDispCommand*>::value>> + BOOL SendCommandsByFD(FD fd, const _List& cmds) + { + TDispContext& ctx = GetContextByFD(fd); + return SendCommands(ctx, cmds); + } + + template, 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 m_pContexts; +}; diff --git a/common/PollHelper.cpp b/common/PollHelper.cpp new file mode 100644 index 0000000..57b3cf8 --- /dev/null +++ b/common/PollHelper.cpp @@ -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; + } +} diff --git a/common/PollHelper.h b/common/PollHelper.h new file mode 100644 index 0000000..d06119c --- /dev/null +++ b/common/PollHelper.h @@ -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 +#include + +#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); diff --git a/common/PrivateHeap.h b/common/PrivateHeap.h new file mode 100644 index 0000000..b865cf5 --- /dev/null +++ b/common/PrivateHeap.h @@ -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 + +#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 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; +using CPrivateHeapStrBuffer = CPrivateHeapBuffer; diff --git a/common/RWLock.cpp b/common/RWLock.cpp new file mode 100644 index 0000000..e7a0687 --- /dev/null +++ b/common/RWLock.cpp @@ -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(); + } +} diff --git a/common/RWLock.h b/common/RWLock.h new file mode 100644 index 0000000..8294056 --- /dev/null +++ b/common/RWLock.h @@ -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 +#include + +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 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 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; +using CWriteLock = lock_guard; +using CWriteLock2 = unique_lock; + +#if !defined(_USE_MUTEX_RW_LOCK) + using CRWLock = CSEMRWLock; +#else + using CRWLock = CMutexRWLock; +#endif + +using CReentrantReadLock = CLocalReadLock; +using CReentrantWriteLock = CLocalWriteLock; diff --git a/common/RingBuffer.h b/common/RingBuffer.h new file mode 100644 index 0000000..3b9b281 --- /dev/null +++ b/common/RingBuffer.h @@ -0,0 +1,1731 @@ +/* +* 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 "FuncHelper.h" +#include "RWLock.h" + +using namespace std; + +#define CACHE_LINE 64 +#define PACK_SIZE_OF(T) (CACHE_LINE - sizeof(T) % CACHE_LINE) + +#if __WORDSIZE == 32 + #pragma pack(push, 4) +#endif + +// ------------------------------------------------------------------------------------------------------------- // + +template class CRingCache +{ +public: + + enum EnGetResult {GR_FAIL = -1, GR_INVALID = 0, GR_VALID = 1}; + + typedef T* TPTR; + typedef volatile T* VTPTR; + + typedef unordered_set IndexSet; + typedef typename IndexSet::const_iterator IndexSetCI; + typedef typename IndexSet::iterator IndexSetI; + + static TPTR const E_EMPTY; + static TPTR const E_LOCKED; + static TPTR const E_MAX_STATUS; + +public: + + static index_type& INDEX_INC(index_type& dwIndex) {if(adjust_index) ++dwIndex; return dwIndex;} + static index_type& INDEX_DEC(index_type& dwIndex) {if(adjust_index) --dwIndex; return dwIndex;} + +private: + + index_type& INDEX_V2R(index_type& dwIndex) {dwIndex %= m_dwSize; if(dwIndex == 0) dwIndex = m_dwSize; return dwIndex;} + VTPTR& INDEX_VAL(index_type dwIndex) {return *(m_pv + dwIndex);} + +public: + + BOOL Put(TPTR pElement, index_type& dwIndex) + { + ASSERT(pElement != nullptr); + + if(!IsValid()) return FALSE; + + BOOL isOK = FALSE; + + while(true) + { + if(!HasSpace()) + break; + + DWORD dwCurSeq = m_dwCurSeq; + index_type dwCurIndex = dwCurSeq % m_dwSize; + VTPTR& pValue = INDEX_VAL(dwCurIndex); + + if(pValue == E_EMPTY) + { + if(::InterlockedCompareExchangePointer(&pValue, pElement, E_EMPTY) == E_EMPTY) + { + ::InterlockedIncrement(&m_dwCount); + ::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq); + + dwIndex = INDEX_INC(dwCurIndex); + isOK = TRUE; + + if(pElement != E_LOCKED) + EmplaceIndex(dwIndex); + + break; + } + } + + ::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq); + } + + return isOK; + } + + EnGetResult GetEx(index_type dwIndex, TPTR* ppElement) + { + return Get(INDEX_V2R(dwIndex), ppElement); + } + + EnGetResult Get(index_type dwIndex, TPTR* ppElement) + { + ASSERT(dwIndex <= m_dwSize); + ASSERT(ppElement != nullptr); + + if(!IsValid() || INDEX_DEC(dwIndex) >= m_dwSize) + { + *ppElement = nullptr; + return GR_FAIL; + } + + *ppElement = (TPTR)INDEX_VAL(dwIndex); + + return IsValidElement(*ppElement) ? GR_VALID : GR_INVALID; + } + + BOOL SetEx(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr) + { + return Set(INDEX_V2R(dwIndex), pElement, ppOldElement); + } + + BOOL Set(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr) + { + TPTR pElement2 = nullptr; + + if(Get(dwIndex, &pElement2) == GR_FAIL) + return FALSE; + + if(ppOldElement != nullptr) + *ppOldElement = pElement2; + + if(pElement == pElement2) + return FALSE; + + int f1 = 0; + int f2 = 0; + + if(pElement == E_EMPTY) + { + if(pElement2 == E_LOCKED) + f1 = -1; + else + f1 = f2 = -1; + } + else if(pElement == E_LOCKED) + { + if(pElement2 == E_EMPTY) + f1 = 1; + else + f2 = -1; + } + else + { + if(pElement2 == E_EMPTY) + f1 = f2 = 1; + else if(pElement2 == E_LOCKED) + f2 = 1; + } + + BOOL bSetValueFirst = (f1 + f2 >= 0); + index_type dwOuterIndex = dwIndex; + + INDEX_DEC(dwIndex); + + if(bSetValueFirst) INDEX_VAL(dwIndex) = pElement; + if(f1 > 0) ::InterlockedIncrement(&m_dwCount); + if(f2 != 0) (f2 > 0) ? EmplaceIndex(dwOuterIndex) : EraseIndex(dwOuterIndex); + if(f1 < 0) ::InterlockedDecrement(&m_dwCount); + if(!bSetValueFirst) INDEX_VAL(dwIndex) = pElement; + + ASSERT(Spaces() <= Size()); + + return TRUE; + } + + BOOL RemoveEx(index_type dwIndex, TPTR* ppElement = nullptr) + { + return Remove(INDEX_V2R(dwIndex), ppElement); + } + + BOOL Remove(index_type dwIndex, TPTR* ppElement = nullptr) + { + return Set(dwIndex, E_EMPTY, ppElement); + } + + BOOL AcquireLock(index_type& dwIndex) + { + return Put(E_LOCKED, dwIndex); + } + + BOOL ReleaseLock(index_type dwIndex, TPTR pElement) + { + ASSERT(pElement == nullptr || IsValidElement(pElement)); + + TPTR pElement2 = nullptr; + Get(dwIndex, &pElement2); + + ASSERT(pElement2 == E_LOCKED); + + if(pElement2 != E_LOCKED) + return FALSE; + + return Set(dwIndex, pElement); + } + +public: + + void Reset(DWORD dwSize = 0) + { + if(IsValid()) + Destroy(); + if(dwSize > 0) + Create(dwSize); + } + + BOOL GetAllElementIndexes(index_type ids[], DWORD& dwCount, BOOL bCopy = TRUE) + { + DWORD dwSize = Elements(); + + if(ids == nullptr || dwCount == 0) + { + dwCount = dwSize; + return FALSE; + } + + if(dwSize == 0) + { + dwCount = 0; + return TRUE; + } + + IndexSet* pIndexes = &m_indexes; + + if(bCopy) + { + pIndexes = new IndexSet; + CopyIndexes(*pIndexes); + } + + DWORD i = 0; + + for(auto it = pIndexes->begin(), end = pIndexes->end(); i < dwCount && it != end; ++i, ++it) + ids[i] = *it; + + if(bCopy) delete pIndexes; + + dwCount = i; + return TRUE; + } + + unique_ptr GetAllElementIndexes(DWORD& dwCount, BOOL bCopy = TRUE) + { + dwCount = (DWORD)m_indexes.size(); + unique_ptr ids(new index_type[dwCount]); + + if(dwCount > 0) + GetAllElementIndexes(ids.get(), dwCount, bCopy); + + return ids; + } + + IndexSet& CopyIndexes(IndexSet& indexes) + { + { + CReadLock locallock(m_cs); + indexes = m_indexes; + } + + return indexes; + } + + static BOOL IsValidElement(TPTR pElement) {return pElement > E_MAX_STATUS;} + + IndexSet& Indexes () { return m_indexes;} + DWORD Size () {return m_dwSize;} + DWORD Elements () {return (DWORD)m_indexes.size();} + DWORD Spaces () {return m_dwSize - m_dwCount;} + BOOL HasSpace () {return m_dwCount < m_dwSize;} + BOOL IsEmpty () {return m_dwCount == 0;} + BOOL IsValid () {return m_pv != nullptr;} + +private: + + void Create(DWORD dwSize) + { + ASSERT(!IsValid() && dwSize > 0); + + m_dwCurSeq = 0; + m_dwCount = 0; + m_dwSize = dwSize; + m_pv = (VTPTR*)malloc(m_dwSize * sizeof(TPTR)); + + ::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR)); + } + + void Destroy() + { + ASSERT(IsValid()); + + m_indexes.clear(); + free((void*)m_pv); + + m_pv = nullptr; + m_dwSize = 0; + m_dwCount = 0; + m_dwCurSeq = 0; + } + + void EmplaceIndex(index_type dwIndex) + { + CWriteLock locallock(m_cs); + m_indexes.emplace(dwIndex); + } + + void EraseIndex(index_type dwIndex) + { + CWriteLock locallock(m_cs); + m_indexes.erase(dwIndex); + } + +public: + CRingCache (DWORD dwSize = 0) + : m_pv (nullptr) + , m_dwSize (0) + , m_dwCount (0) + , m_dwCurSeq(0) + { + Reset(dwSize); + } + + ~CRingCache() + { + Reset(0); + } + +private: + CRingCache(const CRingCache&); + CRingCache operator = (const CRingCache&); + +private: + DWORD m_dwSize; + VTPTR* m_pv; + char pack1[PACK_SIZE_OF(VTPTR*)]; + volatile DWORD m_dwCurSeq; + char pack2[PACK_SIZE_OF(DWORD)]; + volatile DWORD m_dwCount; + char pack3[PACK_SIZE_OF(DWORD)]; + + CSimpleRWLock m_cs; + IndexSet m_indexes; +}; + +template T* const CRingCache::E_EMPTY = (T*)0x00; +template T* const CRingCache::E_LOCKED = (T*)0x01; +template T* const CRingCache::E_MAX_STATUS = (T*)0x0F; + +// ------------------------------------------------------------------------------------------------------------- // + +template class CRingCache2 +{ +public: + + enum EnGetResult {GR_FAIL = -1, GR_INVALID = 0, GR_VALID = 1}; + + typedef T* TPTR; + typedef volatile T* VTPTR; + + typedef unordered_set IndexSet; + typedef typename IndexSet::const_iterator IndexSetCI; + typedef typename IndexSet::iterator IndexSetI; + + static TPTR const E_EMPTY; + static TPTR const E_LOCKED; + static TPTR const E_MAX_STATUS; + static DWORD const MAX_SIZE; + +public: + + static index_type& INDEX_INC(index_type& dwIndex) {if(adjust_index) ++dwIndex; return dwIndex;} + static index_type& INDEX_DEC(index_type& dwIndex) {if(adjust_index) --dwIndex; return dwIndex;} + + index_type& INDEX_R2V(index_type& dwIndex) {dwIndex += *(m_px + dwIndex) * m_dwSize; return dwIndex;} + + BOOL INDEX_V2R(index_type& dwIndex) + { + index_type m = dwIndex % m_dwSize; + BYTE x = *(m_px + m); + + if(dwIndex / m_dwSize != x) + return FALSE; + + dwIndex = m; + return TRUE; + } + + +private: + + VTPTR& INDEX_VAL(index_type dwIndex) {return *(m_pv + dwIndex);} + +public: + + BOOL Put(TPTR pElement, index_type& dwIndex) + { + ASSERT(pElement != nullptr); + + if(!IsValid()) return FALSE; + + BOOL isOK = FALSE; + + while(true) + { + if(!HasSpace()) + break; + + DWORD dwCurSeq = m_dwCurSeq; + index_type dwCurIndex = dwCurSeq % m_dwSize; + VTPTR& pValue = INDEX_VAL(dwCurIndex); + + if(pValue == E_EMPTY) + { + if(::InterlockedCompareExchangePointer(&pValue, pElement, E_EMPTY) == E_EMPTY) + { + ::InterlockedIncrement(&m_dwCount); + ::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq); + + dwIndex = INDEX_INC(INDEX_R2V(dwCurIndex)); + isOK = TRUE; + + if(pElement != E_LOCKED) + EmplaceIndex(dwIndex); + + break; + } + } + + ::InterlockedCompareExchange(&m_dwCurSeq, dwCurSeq + 1, dwCurSeq); + } + + return isOK; + } + + EnGetResult Get(index_type dwIndex, TPTR* ppElement, index_type* pdwRealIndex = nullptr) + { + ASSERT(ppElement != nullptr); + + if(!IsValid() || !INDEX_V2R(INDEX_DEC(dwIndex))) + { + *ppElement = nullptr; + return GR_FAIL; + } + + *ppElement = (TPTR)INDEX_VAL(dwIndex); + if(pdwRealIndex) *pdwRealIndex = dwIndex; + + return IsValidElement(*ppElement) ? GR_VALID : GR_INVALID; + } + + BOOL Set(index_type dwIndex, TPTR pElement, TPTR* ppOldElement = nullptr, index_type* pdwRealIndex = nullptr) + { + TPTR pElement2 = nullptr; + + if(pdwRealIndex == nullptr) + pdwRealIndex = CreateLocalObject(index_type); + + if(Get(dwIndex, &pElement2, pdwRealIndex) == GR_FAIL) + return FALSE; + + if(ppOldElement != nullptr) + *ppOldElement = pElement2; + + if(pElement == pElement2) + return FALSE; + + int f1 = 0; + int f2 = 0; + + if(pElement == E_EMPTY) + { + if(pElement2 == E_LOCKED) + f1 = -1; + else + f1 = f2 = -1; + } + else if(pElement == E_LOCKED) + { + if(pElement2 == E_EMPTY) + f1 = 1; + else + f2 = -1; + } + else + { + if(pElement2 == E_EMPTY) + f1 = f2 = 1; + else if(pElement2 == E_LOCKED) + f2 = 1; + } + + BOOL bSetValueFirst = (f1 + f2 >= 0); + index_type dwRealIndex = *pdwRealIndex; + + if(bSetValueFirst) INDEX_VAL(dwRealIndex) = pElement; + if(f1 > 0) ::InterlockedIncrement(&m_dwCount); + if(f2 != 0) (f2 > 0) ? EmplaceIndex(dwIndex) : EraseIndex(dwIndex); + if(f1 < 0) {::InterlockedDecrement(&m_dwCount); ++(*(m_px + dwRealIndex));} + if(!bSetValueFirst) INDEX_VAL(dwRealIndex) = pElement; + + ASSERT(Spaces() <= Size()); + + return TRUE; + } + + BOOL Remove(index_type dwIndex, TPTR* ppElement = nullptr) + { + return Set(dwIndex, E_EMPTY, ppElement); + } + + BOOL AcquireLock(index_type& dwIndex) + { + return Put(E_LOCKED, dwIndex); + } + + BOOL ReleaseLock(index_type dwIndex, TPTR pElement) + { + ASSERT(pElement == nullptr || IsValidElement(pElement)); + + TPTR pElement2 = nullptr; + Get(dwIndex, &pElement2); + + ASSERT(pElement2 == E_LOCKED); + + if(pElement2 != E_LOCKED) + return FALSE; + + return Set(dwIndex, pElement); + } + +public: + + void Reset(DWORD dwSize = 0) + { + if(IsValid()) + Destroy(); + if(dwSize > 0) + Create(dwSize); + } + + BOOL GetAllElementIndexes(index_type ids[], DWORD& dwCount, BOOL bCopy = TRUE) + { + DWORD dwSize = Elements(); + + if(ids == nullptr || dwCount == 0) + { + dwCount = dwSize; + return FALSE; + } + + if(dwSize == 0) + { + dwCount = 0; + return TRUE; + } + + IndexSet* pIndexes = &m_indexes; + + if(bCopy) + { + pIndexes = new IndexSet; + CopyIndexes(*pIndexes); + } + + DWORD i = 0; + + for(auto it = pIndexes->begin(), end = pIndexes->end(); i < dwCount && it != end; ++i, ++it) + ids[i] = *it; + + if(bCopy) delete pIndexes; + + dwCount = i; + return TRUE; + } + + unique_ptr GetAllElementIndexes(DWORD& dwCount, BOOL bCopy = TRUE) + { + dwCount = (DWORD)m_indexes.size(); + unique_ptr ids(new index_type[dwCount]); + + if(dwCount > 0) + GetAllElementIndexes(ids.get(), dwCount, bCopy); + + return ids; + } + + IndexSet& CopyIndexes(IndexSet& indexes) + { + { + CReadLock locallock(m_cs); + indexes = m_indexes; + } + + return indexes; + } + + static BOOL IsValidElement(TPTR pElement) {return pElement > E_MAX_STATUS;} + + IndexSet& Indexes () {return m_indexes;} + DWORD Size () {return m_dwSize;} + DWORD Elements () {return (DWORD)m_indexes.size();} + DWORD Spaces () {return m_dwSize - m_dwCount;} + BOOL HasSpace () {return m_dwCount < m_dwSize;} + BOOL IsEmpty () {return m_dwCount == 0;} + BOOL IsValid () {return m_pv != nullptr;} + +private: + + void Create(DWORD dwSize) + { + ASSERT(!IsValid() && dwSize > 0 && dwSize <= MAX_SIZE); + + m_dwCurSeq = 0; + m_dwCount = 0; + m_dwSize = dwSize; + m_pv = (VTPTR*)malloc(m_dwSize * sizeof(TPTR)); + m_px = (BYTE*)malloc(m_dwSize * sizeof(BYTE)); + + ::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR)); + ::ZeroMemory(m_px, m_dwSize * sizeof(BYTE)); + } + + void Destroy() + { + ASSERT(IsValid()); + + m_indexes.clear(); + free((void*)m_pv); + free((void*)m_px); + + m_pv = nullptr; + m_px = nullptr; + m_dwSize = 0; + m_dwCount = 0; + m_dwCurSeq = 0; + } + + void EmplaceIndex(index_type dwIndex) + { + CWriteLock locallock(m_cs); + m_indexes.emplace(dwIndex); + } + + void EraseIndex(index_type dwIndex) + { + CWriteLock locallock(m_cs); + m_indexes.erase(dwIndex); + } + +public: + CRingCache2 (DWORD dwSize = 0) + : m_pv (nullptr) + , m_px (nullptr) + , m_dwSize (0) + , m_dwCount (0) + , m_dwCurSeq(0) + { + Reset(dwSize); + } + + ~CRingCache2() + { + Reset(0); + } + + DECLARE_NO_COPY_CLASS(CRingCache2) + +private: + DWORD m_dwSize; + VTPTR* m_pv; + char pack1[PACK_SIZE_OF(VTPTR*)]; + BYTE* m_px; + char pack2[PACK_SIZE_OF(BYTE*)]; + volatile DWORD m_dwCurSeq; + char pack3[PACK_SIZE_OF(DWORD)]; + volatile DWORD m_dwCount; + char pack4[PACK_SIZE_OF(DWORD)]; + + CSimpleRWLock m_cs; + IndexSet m_indexes; +}; + +template T* const CRingCache2::E_EMPTY = (T*)0x00; +template T* const CRingCache2::E_LOCKED = (T*)0x01; +template T* const CRingCache2::E_MAX_STATUS = (T*)0x0F; + +template DWORD const CRingCache2::MAX_SIZE = +#if __WORDSIZE == 32 + 0x00FFFFFF +#else + 0xFFFFFFFF +#endif + ; +// ------------------------------------------------------------------------------------------------------------- // + +template class CRingPool +{ +private: + + typedef T* TPTR; + typedef volatile T* VTPTR; + + static TPTR const E_EMPTY; + static TPTR const E_LOCKED; + static TPTR const E_MAX_STATUS; + +private: + + VTPTR& INDEX_VAL(DWORD dwIndex) {return *(m_pv + dwIndex);} + +public: + + BOOL TryPut(TPTR pElement) + { + ASSERT(pElement != nullptr); + + if(!IsValid()) return FALSE; + + BOOL isOK = FALSE; + + for(DWORD i = 0; i < m_dwSize; i++) + { + DWORD seqPut = m_seqPut; + + if(!HasPutSpace(seqPut)) + break; + + DWORD dwIndex = seqPut % m_dwSize; + VTPTR& pValue = INDEX_VAL(dwIndex); + TPTR pCurrent = (TPTR)pValue; + + if(pCurrent == E_EMPTY) + { + if(::InterlockedCompareExchangePointer(&pValue, pElement, pCurrent) == pCurrent) + { + ::InterlockedCompareExchange(&m_seqPut, seqPut + 1, seqPut); + + isOK = TRUE; + + break; + } + } + + ::InterlockedCompareExchange(&m_seqPut, seqPut + 1, seqPut); + } + + return isOK; + } + + BOOL TryGet(TPTR* ppElement) + { + ASSERT(ppElement != nullptr); + + if(!IsValid()) return FALSE; + + BOOL isOK = FALSE; + + while(true) + { + DWORD seqGet = m_seqGet; + + if(!HasGetSpace(seqGet)) + break; + + DWORD dwIndex = seqGet % m_dwSize; + VTPTR& pValue = INDEX_VAL(dwIndex); + TPTR pCurrent = (TPTR)pValue; + + if(pCurrent > E_MAX_STATUS) + { + if(::InterlockedCompareExchangePointer(&pValue, E_EMPTY, pCurrent) == pCurrent) + { + ::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet); + + *(ppElement) = pCurrent; + isOK = TRUE; + + break; + } + } + + ::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet); + } + + return isOK; + } + + BOOL TryLock(TPTR* ppElement, DWORD& dwIndex) + { + ASSERT(ppElement != nullptr); + + if(!IsValid()) return FALSE; + + BOOL isOK = FALSE; + + while(true) + { + DWORD seqGet = m_seqGet; + + if(!HasGetSpace(seqGet)) + break; + + dwIndex = seqGet % m_dwSize; + VTPTR& pValue = INDEX_VAL(dwIndex); + TPTR pCurrent = (TPTR)pValue; + + if(pCurrent > E_MAX_STATUS) + { + if(::InterlockedCompareExchangePointer(&pValue, E_LOCKED, pCurrent) == pCurrent) + { + ::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet); + + *(ppElement) = pCurrent; + isOK = TRUE; + + break; + } + } + + ::InterlockedCompareExchange(&m_seqGet, seqGet + 1, seqGet); + } + + return isOK; + } + + BOOL ReleaseLock(TPTR pElement, DWORD dwIndex) + { + ASSERT(dwIndex < m_dwSize); + ASSERT(pElement == nullptr || pElement > E_MAX_STATUS); + + if(!IsValid()) return FALSE; + + VTPTR& pValue = INDEX_VAL(dwIndex); + ENSURE(pValue == E_LOCKED); + + if(pElement == nullptr) + pValue = E_EMPTY; + else + pValue = pElement; + + return TRUE; + } + +public: + + void Reset(DWORD dwSize = 0) + { + if(IsValid()) + Destroy(); + if(dwSize > 0) + Create(dwSize); + } + + void Clear() + { + for(DWORD dwIndex = 0; dwIndex < m_dwSize; dwIndex++) + { + VTPTR& pValue = INDEX_VAL(dwIndex); + + if(pValue > E_MAX_STATUS) + { + T::Destruct((TPTR)pValue); + pValue = E_EMPTY; + } + } + + Reset(); + } + + DWORD Size() {return m_dwSize;} + DWORD Elements() {return m_seqPut - m_seqGet;} + BOOL IsFull() {return Elements() == Size();} + BOOL IsEmpty() {return Elements() == 0;} + BOOL IsValid() {return m_pv != nullptr;} + +private: + + BOOL HasPutSpace(DWORD seqPut) + { + return ((int)(seqPut - m_seqGet) < (int)m_dwSize); + } + + BOOL HasGetSpace(DWORD seqGet) + { + return ((int)(m_seqPut - seqGet) > 0); + } + + void Create(DWORD dwSize) + { + ASSERT(!IsValid() && dwSize > 0); + + m_seqPut = 0; + m_seqGet = 0; + m_dwSize = dwSize; + m_pv = (VTPTR*)malloc(m_dwSize * sizeof(TPTR)); + + ::ZeroMemory(m_pv, m_dwSize * sizeof(TPTR)); + } + + void Destroy() + { + ASSERT(IsValid()); + + free((void*)m_pv); + m_pv = nullptr; + m_dwSize = 0; + m_seqPut = 0; + m_seqGet = 0; + } + +public: + CRingPool(DWORD dwSize = 0) + : m_pv(nullptr) + , m_dwSize(0) + , m_seqPut(0) + , m_seqGet(0) + { + Reset(dwSize); + } + + ~CRingPool() + { + Reset(0); + } + +private: + CRingPool(const CRingPool&); + CRingPool operator = (const CRingPool&); + +private: + DWORD m_dwSize; + VTPTR* m_pv; + char pack1[PACK_SIZE_OF(VTPTR*)]; + volatile DWORD m_seqPut; + char pack2[PACK_SIZE_OF(DWORD)]; + volatile DWORD m_seqGet; + char pack3[PACK_SIZE_OF(DWORD)]; +}; + +template T* const CRingPool::E_EMPTY = (T*)0x00; +template T* const CRingPool::E_LOCKED = (T*)0x01; +template T* const CRingPool::E_MAX_STATUS = (T*)0x0F; + +// ------------------------------------------------------------------------------------------------------------- // + +template class CCASQueueX +{ +private: + struct Node; + typedef Node* NPTR; + typedef volatile Node* VNPTR; + typedef volatile UINT VUINT; + + struct Node + { + T* pValue; + VNPTR pNext; + + Node(T* val, NPTR next = nullptr) + : pValue(val), pNext(next) + { + + } + }; + +public: + + void PushBack(T* pVal) + { + ASSERT(pVal != nullptr); + + VNPTR pTail = nullptr; + NPTR pNode = new Node(pVal); + + while(true) + { + pTail = m_pTail; + + if(::InterlockedCompareExchangePointer(&m_pTail, pNode, pTail) == pTail) + { + pTail->pNext = pNode; + break; + } + } + + ::InterlockedIncrement(&m_iSize); + } + + void UnsafePushBack(T* pVal) + { + ASSERT(pVal != nullptr); + + NPTR pNode = new Node(pVal); + m_pTail->pNext = pNode; + m_pTail = pNode; + + ::InterlockedIncrement(&m_iSize); + } + + BOOL PopFront(T** ppVal) + { + ASSERT(ppVal != nullptr); + + if(IsEmpty()) + return FALSE; + + BOOL isOK = FALSE; + NPTR pHead = nullptr; + NPTR pNext = nullptr; + + while(true) + { + Lock(); + + pHead = (NPTR)m_pHead; + pNext = (NPTR)pHead->pNext; + + if(pNext == nullptr) + { + Unlock(); + break; + } + + *ppVal = pNext->pValue; + m_pHead = pNext; + + Unlock(); + + isOK = TRUE; + + ::InterlockedDecrement(&m_iSize); + + delete pHead; + break; + } + + return isOK; + } + + BOOL UnsafePopFront(T** ppVal) + { + if(!UnsafePeekFront(ppVal)) + return FALSE; + + UnsafePopFrontNotCheck(); + + return TRUE; + } + + BOOL UnsafePeekFront(T** ppVal) + { + ASSERT(ppVal != nullptr); + + NPTR pNext = (NPTR)m_pHead->pNext; + + if(pNext == nullptr) + return FALSE; + + *ppVal = pNext->pValue; + + return TRUE; + } + + void UnsafePopFrontNotCheck() + { + NPTR pHead = (NPTR)m_pHead; + NPTR pNext = (NPTR)pHead->pNext; + m_pHead = pNext; + + ::InterlockedDecrement(&m_iSize); + + delete pHead; + } + + void UnsafeClear() + { + ASSERT(m_pHead != nullptr); + + m_dwCheckTime = 0; + + while(m_pHead->pNext != nullptr) + UnsafePopFrontNotCheck(); + } + +public: + + UINT Size() {return m_iSize;} + BOOL IsEmpty() {return m_iSize == 0;} + + void Lock() {while(!TryLock()) ::YieldProcessor();} + void Unlock() {m_iLock = 0;} + BOOL TryLock() {return (::InterlockedCompareExchange(&m_iLock, 1u, 0u) == 0);} + + DWORD GetCheckTime() + { + return m_dwCheckTime; + } + + void UpdateCheckTime(DWORD dwCurrent = 0) + { + if(dwCurrent == 0) + dwCurrent = ::TimeGetTime(); + + m_dwCheckTime = dwCurrent; + } + + int GetCheckTimeGap(DWORD dwCurrent = 0) + { + int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent); + + if(rs < -60 * 1000) + rs = MAXINT; + + return rs; + } + +public: + + CCASQueueX() : m_iLock(0), m_iSize(0), m_dwCheckTime(0) + { + m_pHead = m_pTail = new Node(nullptr); + } + + ~CCASQueueX() + { + ASSERT(m_iLock == 0); + ASSERT(m_iSize == 0); + ASSERT(m_pTail == m_pHead); + ASSERT(m_pHead != nullptr); + ASSERT(m_pHead->pNext == nullptr); + + UnsafeClear(); + + delete m_pHead; + } + + DECLARE_NO_COPY_CLASS(CCASQueueX) + +private: + VUINT m_iLock; + VUINT m_iSize; + VNPTR m_pHead; + VNPTR m_pTail; + + volatile DWORD m_dwCheckTime; +}; + +template class CCASSimpleQueueX +{ +private: + struct Node; + typedef Node* NPTR; + typedef volatile Node* VNPTR; + typedef volatile UINT VUINT; + + struct Node + { + T tValue; + VNPTR pNext; + + Node(T val, NPTR next = nullptr) + : tValue(val), pNext(next) + { + + } + }; + +public: + + void PushBack(T tVal) + { + VNPTR pTail = nullptr; + NPTR pNode = new Node(tVal); + + while(true) + { + pTail = m_pTail; + + if(::InterlockedCompareExchangePointer(&m_pTail, pNode, pTail) == pTail) + { + pTail->pNext = pNode; + break; + } + } + + ::InterlockedIncrement(&m_iSize); + } + + void UnsafePushBack(T tVal) + { + NPTR pNode = new Node(tVal); + m_pTail->pNext = pNode; + m_pTail = pNode; + + ::InterlockedIncrement(&m_iSize); + } + + BOOL PopFront(T* ptVal) + { + ASSERT(ptVal != nullptr); + + if(IsEmpty()) + return FALSE; + + BOOL isOK = FALSE; + NPTR pHead = nullptr; + NPTR pNext = nullptr; + + while(true) + { + Lock(); + + pHead = (NPTR)m_pHead; + pNext = (NPTR)pHead->pNext; + + if(pNext == nullptr) + { + Unlock(); + break; + } + + *ptVal = pNext->tValue; + m_pHead = pNext; + + Unlock(); + + isOK = TRUE; + + ::InterlockedDecrement(&m_iSize); + + delete pHead; + break; + } + + return isOK; + } + + BOOL UnsafePopFront(T* ptVal) + { + if(!UnsafePeekFront(ptVal)) + return FALSE; + + UnsafePopFrontNotCheck(); + + return TRUE; + } + + BOOL UnsafePeekFront(T* ptVal) + { + ASSERT(ptVal != nullptr); + + NPTR pNext = (NPTR)m_pHead->pNext; + + if(pNext == nullptr) + return FALSE; + + *ptVal = pNext->pValue; + + return TRUE; + } + + void UnsafePopFrontNotCheck() + { + NPTR pHead = (NPTR)m_pHead; + NPTR pNext = (NPTR)pHead->pNext; + m_pHead = pNext; + + ::InterlockedDecrement(&m_iSize); + + delete pHead; + } + + void UnsafeClear() + { + ASSERT(m_pHead != nullptr); + + m_dwCheckTime = 0; + + while(m_pHead->pNext != nullptr) + UnsafePopFrontNotCheck(); + } + +public: + + UINT Size() {return m_iSize;} + BOOL IsEmpty() {return m_iSize == 0;} + + void Lock() {while(!TryLock()) ::YieldProcessor();} + void Unlock() {m_iLock = 0;} + BOOL TryLock() {return (::InterlockedCompareExchange(&m_iLock, 1u, 0u) == 0);} + + DWORD GetCheckTime() + { + return m_dwCheckTime; + } + + void UpdateCheckTime(DWORD dwCurrent = 0) + { + if(dwCurrent == 0) + dwCurrent = ::TimeGetTime(); + + m_dwCheckTime = dwCurrent; + } + + int GetCheckTimeGap(DWORD dwCurrent = 0) + { + int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent); + + if(rs < -60 * 1000) + rs = MAXINT; + + return rs; + } + +public: + + CCASSimpleQueueX() : m_iLock(0), m_iSize(0), m_dwCheckTime(0) + { + m_pHead = m_pTail = new Node(0); + } + + ~CCASSimpleQueueX() + { + ASSERT(m_iLock == 0); + ASSERT(m_iSize == 0); + ASSERT(m_pTail == m_pHead); + ASSERT(m_pHead != nullptr); + ASSERT(m_pHead->pNext == nullptr); + + UnsafeClear(); + + delete m_pHead; + } + + DECLARE_NO_COPY_CLASS(CCASSimpleQueueX) + +private: + VUINT m_iLock; + VUINT m_iSize; + VNPTR m_pHead; + VNPTR m_pTail; + + volatile DWORD m_dwCheckTime; +}; + +template class CCASQueueY +{ +public: + + void PushBack(T* pVal) + { + CCriSecLock locallock(m_csGuard); + + UnsafePushBack(pVal); + } + + void UnsafePushBack(T* pVal) + { + ASSERT(pVal != nullptr); + + m_lsItems.push_back(pVal); + } + + void PushFront(T* pVal) + { + CCriSecLock locallock(m_csGuard); + + UnsafePushFront(pVal); + } + + void UnsafePushFront(T* pVal) + { + ASSERT(pVal != nullptr); + + m_lsItems.push_front(pVal); + } + + BOOL PopFront(T** ppVal) + { + CCriSecLock locallock(m_csGuard); + + return UnsafePopFront(ppVal); + } + + BOOL UnsafePopFront(T** ppVal) + { + if(!UnsafePeekFront(ppVal)) + return FALSE; + + UnsafePopFrontNotCheck(); + + return TRUE; + } + + BOOL PeekFront(T** ppVal) + { + CCriSecLock locallock(m_csGuard); + + return UnsafePeekFront(ppVal); + } + + BOOL UnsafePeekFront(T** ppVal) + { + ASSERT(ppVal != nullptr); + + if(m_lsItems.empty()) + return FALSE; + + *ppVal = m_lsItems.front(); + + return TRUE; + } + + void UnsafePopFrontNotCheck() + { + m_lsItems.pop_front(); + } + + void Clear() + { + CCriSecLock locallock(m_csGuard); + + UnsafeClear(); + } + + void UnsafeClear() + { + m_dwCheckTime = 0; + + m_lsItems.clear(); + } + +public: + + ULONG Size() {return (ULONG)m_lsItems.size();} + BOOL IsEmpty() {return (BOOL)m_lsItems.empty();} + + void Lock() {m_csGuard.lock();} + void Unlock() {m_csGuard.unlock();} + BOOL TryLock() {return m_csGuard.try_lock();} + CCriSec& Guard(){return m_csGuard;} + + DWORD GetCheckTime() + { + return m_dwCheckTime; + } + + void UpdateCheckTime(DWORD dwCurrent = 0) + { + if(dwCurrent == 0) + dwCurrent = ::TimeGetTime(); + + m_dwCheckTime = dwCurrent; + } + + int GetCheckTimeGap(DWORD dwCurrent = 0) + { + int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent); + + if(rs < -60 * 1000) + rs = MAXINT; + + return rs; + } + +public: + + CCASQueueY() + : m_dwCheckTime(0) + { + + } + + ~CCASQueueY() + { + ASSERT(IsEmpty()); + + UnsafeClear(); + } + + DECLARE_NO_COPY_CLASS(CCASQueueY) + +private: + CCriSec m_csGuard; + deque m_lsItems; + + volatile DWORD m_dwCheckTime; +}; + +template class CCASSimpleQueueY +{ +public: + + void PushBack(T tVal) + { + CCriSecLock locallock(m_csGuard); + + UnsafePushBack(tVal); + } + + void UnsafePushBack(T tVal) + { + m_lsItems.push_back(tVal); + } + + void PushFront(T tVal) + { + CCriSecLock locallock(m_csGuard); + + UnsafePushFront(tVal); + } + + void UnsafePushFront(T tVal) + { + m_lsItems.push_front(tVal); + } + + BOOL PopFront(T* ptVal) + { + CCriSecLock locallock(m_csGuard); + + return UnsafePopFront(ptVal); + } + + BOOL UnsafePopFront(T* ptVal) + { + if(!UnsafePeekFront(ptVal)) + return FALSE; + + UnsafePopFrontNotCheck(); + + return TRUE; + } + + BOOL PeekFront(T* ptVal) + { + CCriSecLock locallock(m_csGuard); + + return UnsafePeekFront(ptVal); + } + + BOOL UnsafePeekFront(T* ptVal) + { + ASSERT(ptVal != nullptr); + + if(m_lsItems.empty()) + return FALSE; + + *ptVal = m_lsItems.front(); + + return TRUE; + } + + void UnsafePopFrontNotCheck() + { + m_lsItems.pop_front(); + } + + void Clear() + { + CCriSecLock locallock(m_csGuard); + + UnsafeClear(); + } + + void UnsafeClear() + { + m_dwCheckTime = 0; + + m_lsItems.clear(); + } + +public: + + ULONG Size() {return (ULONG)m_lsItems.size();} + BOOL IsEmpty() {return (BOOL)m_lsItems.empty();} + + void Lock() {m_csGuard.lock();} + void Unlock() {m_csGuard.unlock();} + BOOL TryLock() {return m_csGuard.try_lock();} + CCriSec& Guard(){return m_csGuard;} + + DWORD GetCheckTime() + { + return m_dwCheckTime; + } + + void UpdateCheckTime(DWORD dwCurrent = 0) + { + if(dwCurrent == 0) + dwCurrent = ::TimeGetTime(); + + m_dwCheckTime = dwCurrent; + } + + int GetCheckTimeGap(DWORD dwCurrent = 0) + { + int rs = (int)GetTimeGap32(m_dwCheckTime, dwCurrent); + + if(rs < -60 * 1000) + rs = MAXINT; + + return rs; + } + +public: + + CCASSimpleQueueY() + : m_dwCheckTime(0) + { + + } + + ~CCASSimpleQueueY() + { + ASSERT(IsEmpty()); + + UnsafeClear(); + } + + DECLARE_NO_COPY_CLASS(CCASSimpleQueueY) + +private: + CCriSec m_csGuard; + deque m_lsItems; + + volatile DWORD m_dwCheckTime; +}; + +template using CCASQueue = CCASQueueX; +template using CCASSimpleQueue = CCASSimpleQueueX; + +template +void ReleaseGCObj(CCASQueue& lsGC, DWORD dwLockTime, BOOL bForce = FALSE) +{ + static const int MIN_CHECK_INTERVAL = 1 * 1000; + static const int MAX_CHECK_INTERVAL = 15 * 1000; + + T* pObj = nullptr; + + if(bForce) + { + CLocalLock> locallock(lsGC); + + while(lsGC.UnsafePeekFront(&pObj)) + { + lsGC.UnsafePopFrontNotCheck(); + T::Destruct(pObj); + } + } + else + { + if(lsGC.IsEmpty() || lsGC.GetCheckTimeGap() < MAX(MIN((int)(dwLockTime / 3), MAX_CHECK_INTERVAL), MIN_CHECK_INTERVAL)) + return; + + T* pFirst = nullptr; + BOOL bFirst = TRUE; + DWORD now = 0; + + while(TRUE) + { + ASSERT((pObj = nullptr) == nullptr); + + { + CLocalTryLock> locallock(lsGC); + + if(!locallock.IsValid()) + break; + + if(bFirst) + { + bFirst = FALSE; + now = ::TimeGetTime(); + + lsGC.UpdateCheckTime(now); + } + + if(!lsGC.UnsafePeekFront(&pObj)) + break; + + if((int)(now - pObj->GetFreeTime()) < (int)dwLockTime) + break; + + lsGC.UnsafePopFrontNotCheck(); + + if(pObj->GetCount() > 0) + { + lsGC.PushBack(pObj); + + if(pFirst == nullptr) + pFirst = pObj; + else if(pFirst == pObj) + break; + + continue; + } + } + + ASSERT(pObj != nullptr); + T::Destruct(pObj); + } + } +} + +#if __WORDSIZE == 32 + #pragma pack(pop) +#endif diff --git a/common/STLHelper.h b/common/STLHelper.h new file mode 100644 index 0000000..675e514 --- /dev/null +++ b/common/STLHelper.h @@ -0,0 +1,1068 @@ +/* +* 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 "StringT.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define hash_set unordered_set +#define hash_map unordered_map +#define hash_multimap unordered_multimap + +using namespace std; + +typedef list short_list; +typedef list int_list; +typedef list long_list; +typedef list int64_list; +typedef list ushort_list; +typedef list uint_list; +typedef list ulong_list; +typedef list uint64_list; +typedef list float_list; +typedef list double_list; +typedef stack short_stack; +typedef stack int_stack; +typedef stack long_stack; +typedef stack int64_stack; +typedef stack ushort_stack; +typedef stack uint_stack; +typedef stack ulong_stack; +typedef stack uint64_stack; +typedef stack float_stack; +typedef stack double_stack; +typedef queue short_queue; +typedef queue int_queue; +typedef queue long_queue; +typedef queue int64_queue; +typedef queue ushort_queue; +typedef queue uint_queue; +typedef queue ulong_queue; +typedef queue uint64_queue; +typedef queue float_queue; +typedef queue double_queue; +typedef deque short_deque; +typedef deque int_deque; +typedef deque long_deque; +typedef deque int64_deque; +typedef deque ushort_deque; +typedef deque uint_deque; +typedef deque ulong_deque; +typedef deque uint64_deque; +typedef deque float_deque; +typedef deque double_deque; +typedef vector short_vector; +typedef vector int_vector; +typedef vector long_vector; +typedef vector int64_vector; +typedef vector ushort_vector; +typedef vector uint_vector; +typedef vector ulong_vector; +typedef vector uint64_vector; +typedef vector float_vector; +typedef vector double_vector; +typedef set short_set; +typedef set int_set; +typedef set long_set; +typedef set int64_set; +typedef set ushort_set; +typedef set uint_set; +typedef set ulong_set; +typedef set uint64_set; +typedef set float_set; +typedef set double_set; +typedef hash_set short_hash_set; +typedef hash_set int_hash_set; +typedef hash_set long_hash_set; +typedef hash_set int64_hash_set; +typedef hash_set ushort_hash_set; +typedef hash_set uint_hash_set; +typedef hash_set ulong_hash_set; +typedef hash_set uint64_hash_set; +typedef hash_set float_hash_set; +typedef hash_set double_hash_set; +typedef unordered_set short_unordered_set; +typedef unordered_set int_unordered_set; +typedef unordered_set long_unordered_set; +typedef unordered_set int64_unordered_set; +typedef unordered_set ushort_unordered_set; +typedef unordered_set uint_unordered_set; +typedef unordered_set ulong_unordered_set; +typedef unordered_set uint64_unordered_set; +typedef unordered_set float_unordered_set; +typedef unordered_set double_unordered_set; + +typedef list int_ptr_list; +typedef list long_ptr_list; +typedef list uint_ptr_list; +typedef list ulong_ptr_list; +typedef stack int_ptr_stack; +typedef stack long_ptr_stack; +typedef stack uint_ptr_stack; +typedef stack ulong_ptr_stack; +typedef queue int_ptr_queue; +typedef queue long_ptr_queue; +typedef queue uint_ptr_queue; +typedef queue ulong_ptr_queue; +typedef deque int_ptr_deque; +typedef deque long_ptr_deque; +typedef deque uint_ptr_deque; +typedef deque ulong_ptr_deque; +typedef vector int_ptr_vector; +typedef vector long_ptr_vector; +typedef vector uint_ptr_vector; +typedef vector ulong_ptr_vector; +typedef set int_ptr_set; +typedef set long_ptr_set; +typedef set uint_ptr_set; +typedef set ulong_ptr_set; +typedef hash_set int_ptr_hash_set; +typedef hash_set long_ptr_hash_set; +typedef hash_set uint_ptr_hash_set; +typedef hash_set ulong_ptr_hash_set; +typedef unordered_set int_ptr_unordered_set; +typedef unordered_set long_ptr_unordered_set; +typedef unordered_set uint_ptr_unordered_set; +typedef unordered_set ulong_ptr_unordered_set; + +/*****************************************************************************/ +/******************************** 容器操作函数 *******************************/ + +/********************************** +描述: 清除普通集合 , 适用于 vector / list +参数: + v : vector / list / set + +返回值: +**********************************/ +template void ClearSet(Set& v) +{ + v.clear(); +} + +template struct Set_Cleaner +{ + static void Clear(Set& v) {ClearSet(v);} +}; + +/********************************** +描述: 清除指针集合 (清除前先释放指针), 适用于 vector / list +参数: + v : vector / list / set + +返回值: +**********************************/ +template void ClearPtrSet(PtrSet& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + delete (*it); + + v.clear(); +} + +template struct PtrSet_Cleaner +{ + static void Clear(PtrSet& v) {ClearPtrSet(v);} +}; + +/********************************** +描述: 清除指针集合 (指针同时又指向数组), 适用于 vector / list +参数: + v : vector / list / set + +返回值: +**********************************/ +template void ClearPtrArraySet(PtrArraySet& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + delete[] (*it); + + v.clear(); +} + +template struct PtrArraySet_Cleaner +{ + static void Clear(PtrArraySet& v) {ClearPtrArraySet(v);} +}; + +/********************************** +描述: 清除普通影射 , 适用于 map +参数: + v : map + +返回值: +**********************************/ +template void ClearMap(Map& v) +{ + v.clear(); +} + +template struct Map_Cleaner +{ + static void Clear(Map& v) {ClearMap(v);} +}; + +/********************************** +描述: 清除指针影射 (清除前先释放指针), 适用于 map +参数: + v : map + +返回值: +**********************************/ +template void ClearPtrMap(PtrMap& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + delete it->second; + + v.clear(); +} + +template struct PtrMap_Cleaner +{ + static void Clear(PtrMap& v) {ClearPtrMap(v);} +}; + +/********************************** +描述: 清除指针影射 (指针同时又指向数组), 适用于 map +参数: + v : map + +返回值: +**********************************/ +template void ClearPtrArrayMap(PtrArrayMap& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + delete[] it->second; + + v.clear(); +} + +template struct PtrArrayMap_Cleaner +{ + static void Clear(PtrArrayMap& v) {ClearPtrArrayMap(v);} +}; + +/********************************** +描述: 清除集合-集合 (清除前先清除内部集合), 适用于 set*> +参数: + v : vector / list / set + +返回值: +**********************************/ +template void ClearSetSet(SetSet& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + (*it)->clear(); + delete (*it); + } + + v.clear(); +} + +template struct SetSet_Cleaner +{ + static void Clear(SetSet& v) {ClearSetSet(v);} +}; + +/********************************** +描述: 清除指针集合-集合 (清除前先清除内部指针集合), 适用于 set*> +参数: + v : vector / list / set + +返回值: +**********************************/ +template void ClearPtrSetSet(PtrSetSet& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + ClearPtrSet(**it); + delete (*it); + } + + v.clear(); +} + +template struct PtrSetSet_Cleaner +{ + static void Clear(PtrSetSet& v) {ClearPtrSetSet(v);} +}; + +/********************************** +描述: 清除指针数组集合影射 (清除前先清除指针数组集合), 适用于 map*> +参数: + v : vector / list / set + +返回值: +**********************************/ +template void ClearPtrArraySetSet(PtrArraySetSet& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + ClearPtrArraySet(**it); + delete (*it); + } + + v.clear(); +} + +template struct PtrArraySetSet_Cleaner +{ + static void Clear(PtrArraySetSet& v) {ClearPtrArraySetSet(v);} +}; + +/********************************** +描述: 清除集合影射 (清除前先清除集合), 适用于 map*> +参数: + v : map + +返回值: +**********************************/ +template void ClearSetMap(SetMap& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + it->second->clear(); + delete it->second; + } + + v.clear(); +} + +template struct SetMap_Cleaner +{ + static void Clear(SetMap& v) {ClearSetMap(v);} +}; + +/********************************** +描述: 清除指针集合影射 (清除前先清除指针集合), 适用于 map*> +参数: + v : map + +返回值: +**********************************/ +template void ClearPtrSetMap(PtrSetMap& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + ClearPtrSet(*(it->second)); + delete it->second; + } + + v.clear(); +} + +template struct PtrSetMap_Cleaner +{ + static void Clear(PtrSetMap& v) {ClearPtrSetMap(v);} +}; + +/********************************** +描述: 清除指针数组集合影射 (清除前先清除指针数组集合), 适用于 map*> +参数: + v : map + +返回值: +**********************************/ +template void ClearPtrArraySetMap(PtrArraySetMap& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + ClearPtrArraySet(*(it->second)); + delete it->second; + } + + v.clear(); +} + +template struct PtrArraySetMap_Cleaner +{ + static void Clear(PtrArraySetMap& v) {ClearPtrArraySetMap(v);} +}; + +/********************************** +描述: 清除映射-影射 (清除前先清除内部映射), 适用于 map*> +参数: +v : map + +返回值: +**********************************/ +template void ClearMapMap(MapMap& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + it->second->clear(); + delete it->second; + } + + v.clear(); +} + +template struct MapMap_Cleaner +{ + static void Clear(MapMap& v) {ClearMapMap(v);} +}; + +/********************************** +描述: 清除指针映射-影射 (清除前先清除指针内部映射), 适用于 map*> +参数: + v : map + +返回值: +**********************************/ +template void ClearPtrMapMap(PtrMapMap& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + ClearPtrMap(*(it->second)); + delete it->second; + } + + v.clear(); +} + +template struct PtrMapMap_Cleaner +{ + static void Clear(PtrMapMap& v) {ClearPtrMapMap(v);} +}; + +/********************************** +描述: 清除指针映射-影射 (清除前先清除指针数组内部映射), 适用于 map*> +参数: + v : map + +返回值: +**********************************/ +template void ClearPtrArrayMapMap(PtrArrayMapMap& v) +{ + for(auto it = v.begin(), + end = v.end(); + it != end; + ++it) + { + ClearPtrArrayMap(*(it->second)); + delete it->second; + } + + v.clear(); +} + +template struct PtrArrayMapMap_Cleaner +{ + static void Clear(PtrArrayMapMap& v) {ClearPtrArrayMapMap(v);} +}; + +/************************************************************************/ +/* 指针集合容器 */ +/************************************************************************/ +template struct SetWrapper +{ + typedef typename Set::iterator iterator; + typedef typename Set::const_iterator const_iterator; + typedef typename Set::value_type value_type; + typedef typename Set::reference reference; + typedef typename Set::const_reference const_reference; + typedef typename Set::pointer pointer; + typedef typename Set::const_pointer const_pointer; + typedef typename Set::size_type size_type; + typedef typename Set::difference_type difference_type; + + SetWrapper() + { + } + + virtual ~SetWrapper() + { + Clear(); + } + + void Clear() + { + if(!IsEmpty()) + { + Cleaner::Clear(m_set); + } + } + + Set& operator * () {return m_set;} + const Set& operator * () const {return m_set;} + Set* operator -> () {return &m_set;} + const Set* operator -> () const {return &m_set;} + Set& Get () {return m_set;} + operator Set& () {return m_set;} + bool IsEmpty () const {return m_set.empty();} + size_t Size () const {return m_set.size();} + +protected: + Set m_set; + + DECLARE_NO_COPY_CLASS(SetWrapper) +}; + +template struct VectorWrapper : public SetWrapper +{ + typedef SetWrapper __super; + typedef typename __super::reference reference; + typedef typename __super::const_reference const_reference; + typedef typename __super::size_type size_type; + + VectorWrapper() + { + } + + reference operator [] (size_type i) {return __super::m_set[i];} + const_reference operator [] (size_type i) const {return __super::m_set[i];} + + DECLARE_NO_COPY_CLASS(VectorWrapper) +}; + +/************************************************************************/ +/* 指针数组集合容器 */ +/************************************************************************/ + + +/************************************************************************/ +/* 指针映射容器 */ +/************************************************************************/ +template struct MapWrapper +{ + typedef typename Map::iterator iterator; + typedef typename Map::const_iterator const_iterator; + typedef typename Map::key_type key_type; + typedef typename Map::mapped_type mapped_type; + typedef typename Map::value_type value_type; + typedef typename Map::reference reference; + typedef typename Map::const_reference const_reference; + typedef typename Map::pointer pointer; + typedef typename Map::size_type size_type; + typedef typename Map::difference_type difference_type; + + MapWrapper() + { + } + + ~MapWrapper() + { + Clear(); + } + + void Clear() + { + if(!IsEmpty()) + { + Cleaner::Clear(m_map); + } + } + + Map& operator * () {return m_map;} + const Map& operator * () const {return m_map;} + Map* operator -> () {return &m_map;} + const Map* operator -> () const {return &m_map;} + mapped_type& operator [] (const key_type& key) {return m_map[key];} + const mapped_type& operator [] (const key_type& key) const {return m_map[key];} + Map& Get () {return m_map;} + operator Map& () {return m_map;} + bool IsEmpty () const {return m_map.empty();} + size_t Size () const {return m_map.size();} + +private: + Map m_map; + + DECLARE_NO_COPY_CLASS(MapWrapper) +}; + +/************************************************************************/ +/* 比较仿函数 */ +/************************************************************************/ + +template struct char_comparator +{ + typedef T row_type; + static row_type row_type_value(const T& v) {return (row_type)v;} + static bool equal_to(const T& v1, const T& v2) {return strcmp(v1, v2) == 0;} +}; + +template struct char_nc_comparator +{ + typedef T row_type; + static row_type row_type_value(const T& v) {return (row_type)v;} + static bool equal_to(const T& v1, const T& v2) {return stricmp(v1, v2) == 0;} +}; + +template struct wchar_comparator +{ + typedef T row_type; + static row_type row_type_value(const T& v) {return (row_type)v;} + static bool equal_to(const T& v1, const T& v2) {return wcscmp(v1, v2) == 0;} +}; + +template struct wchar_nc_comparator +{ + typedef T row_type; + static row_type row_type_value(const T& v) {return (row_type)v;} + static bool equal_to(const T& v1, const T& v2) {return wcsicmp(v1, v2) == 0;} +}; + +template struct cstring_comparator +{ + typedef typename T::PCXSTR row_type; + static row_type row_type_value(const T& v) {return (row_type)v;} + static bool equal_to(const T& v1, const T& v2) {return v1.Compare(v2) == 0;} +}; + +template struct cstring_nc_comparator +{ + typedef typename T::PCXSTR row_type; + static row_type row_type_value(const T& v) {return (row_type)v;} + static bool equal_to(const T& v1, const T& v2) {return v1.CompareNoCase(v2) == 0;} +}; + +// char/wchar_t/CStringX hash function +template struct str_hash_func_t +{ + struct hash + { + size_t operator() (const T& t) const + { + return hash_value(H::row_type_value(t)); + } + }; + + struct equal_to + { + bool operator() (const T& t1, const T& t2) const + { + return H::equal_to(t1, t2); + } + }; + +}; + +// char/wchar_t/CStringX hash function (no case) +template struct str_nc_hash_func_t +{ + struct hash + { + size_t operator() (const T& t) const + { + size_t _Val = 2166136261U; + typename H::row_type lpsz = H::row_type_value(t); + B c; + + while((c = *lpsz++) != 0) + { + if(c >= 'A' && c <= 'Z') + c = (char)(c + 32); + + _Val = 16777619U * _Val ^ c; + + } + + return _Val; + } + }; + + struct equal_to + { + bool operator() (const T& t1, const T& t2) const + { + return H::equal_to(t1, t2); + } + }; + +}; + +typedef str_hash_func_t> str_hash_func; +typedef str_hash_func_t> wstr_hash_func; +typedef str_hash_func_t> cstringa_hash_func; +typedef str_hash_func_t> cstringw_hash_func; +typedef str_nc_hash_func_t, char> str_nc_hash_func; +typedef str_nc_hash_func_t, wchar_t> wstr_nc_hash_func; +typedef str_nc_hash_func_t, char> cstringa_nc_hash_func; +typedef str_nc_hash_func_t, wchar_t> cstringw_nc_hash_func; + +#ifdef _UNICODE + typedef cstringw_hash_func cstring_hash_func; + typedef cstringw_nc_hash_func cstring_nc_hash_func; +#else + typedef cstringa_hash_func cstring_hash_func; + typedef cstringa_nc_hash_func cstring_nc_hash_func; +#endif + +struct bool_comp_func +{ + bool operator() (bool v1, bool v2) const + { + if(!v1) + return false; + if(v1 == v2) + return false; + + return true; + } +}; + +template +// T -> (signed / unsigned) short / int / long / int64_t +struct integer_comp_func +{ + bool operator() (T v1, T v2) const + { + return v1 < v2; + } +}; + +typedef integer_comp_func short_comp_func; +typedef integer_comp_func int_comp_func; +typedef integer_comp_func long_comp_func; +typedef integer_comp_func int64_comp_func; +typedef integer_comp_func ushort_comp_func; +typedef integer_comp_func uint_comp_func; +typedef integer_comp_func ulong_comp_func; +typedef integer_comp_func uint64_comp_func; + +struct float_comp_func +{ + bool operator() (float v1, float v2) const + { + float disc = v1 - v2; + if(fabsf(disc) < 1E-5) + return false; + + return disc < 0; + } +}; + +struct double_comp_func +{ + bool operator() (double v1, double v2) const + { + double disc = v1 - v2; + if(fabs(disc) < 1E-8) + return false; + + return disc < 0; + } +}; + +template +// T -> (unsigned) char / wchar_t +struct character_comp_func +{ + bool operator() (T v1, T v2) const + { + if(!CASE) + { + if(v1 >= 'A' && v1 <= 'Z') v1 += 32; + if(v2 >= 'A' && v2 <= 'Z') v2 += 32; + } + + return v1 < v2; + } +}; + +typedef character_comp_func char_case_comp_func; +typedef character_comp_func uchar_case_comp_func; +typedef character_comp_func wchar_case_comp_func; +typedef character_comp_func char_ucase_comp_func; +typedef character_comp_func uchar_ucase_comp_func; +typedef character_comp_func wchar_ucase_comp_func; + +template +// T -> TCHAR* / CString +struct str_comp_func +{ + //比较函数。 + bool operator() (const T &A, const T &B) const + { + if(!CASE) + return lstricmp((LPCTSTR)A, (LPCTSTR)B) < 0; + else + return lstrcmp((LPCTSTR)A, (LPCTSTR)B) < 0; + } +}; + +typedef str_comp_func case_tchar_comp_func; +typedef str_comp_func uncase_tchar_comp_func; +typedef str_comp_func case_string_comp_func; +typedef str_comp_func uncase_string_comp_func; +typedef case_tchar_comp_func tchar_ptr_case_comp_func; +typedef uncase_tchar_comp_func tchar_ptr_ucase_comp_func; +typedef case_string_comp_func string_case_comp_func; +typedef uncase_string_comp_func string_ucase_comp_func; +/************************************************************************/ +/* 排序仿函数 */ +/************************************************************************/ +template +struct bool_sort_func +{ + bool operator() (bool v1, bool v2) const + { + if(v1 == v2) + return false; + + bool result = !v1; + return ASC ? result : !result; + } +}; + +typedef bool_sort_func bool_asc_sort_func; +typedef bool_sort_func bool_desc_sort_func; + +template +// T -> (signed / unsigned) short / int / long / int64_t +struct integer_sort_func +{ + bool operator() (T v1, T v2) const + { + if(v1 == v2) + return false; + + bool result = v1 < v2; + return ASC ? result : !result; + } +}; + +typedef integer_sort_func short_asc_sort_func; +typedef integer_sort_func ushort_asc_sort_func; +typedef integer_sort_func int_asc_sort_func; +typedef integer_sort_func uint_asc_sort_func; +typedef integer_sort_func long_asc_sort_func; +typedef integer_sort_func ulong_asc_sort_func; +typedef integer_sort_func int64_asc_sort_func; +typedef integer_sort_func uint64_asc_sort_func; +typedef integer_sort_func short_desc_sort_func; +typedef integer_sort_func ushort_desc_sort_func; +typedef integer_sort_func int_desc_sort_func; +typedef integer_sort_func uint_desc_sort_func; +typedef integer_sort_func long_desc_sort_func; +typedef integer_sort_func ulong_desc_sort_func; +typedef integer_sort_func int64_desc_sort_func; +typedef integer_sort_func uint64_desc_sort_func; + +template +struct float_sort_func +{ + bool operator() (float v1, float v2) const + { + float disc = v1 - v2; + if(fabsf(disc) < 1E-5) + return false; + + bool result = disc < 0; + return ASC ? result : !result; + } +}; + +typedef float_sort_func float_asc_sort_func; +typedef float_sort_func float_desc_sort_func; + +template +struct double_sort_func +{ + bool operator() (double v1, double v2) const + { + double disc = v1 - v2; + if(fabs(disc) < 1E-8) + return false; + + bool result = disc < 0; + return ASC ? result : !result; + } +}; + +typedef double_sort_func double_asc_sort_func; +typedef double_sort_func double_desc_sort_func; + +template +// T -> (unsigned) char / wchar_t +struct character_sort_func +{ + bool operator() (T v1, T v2) const + { + if(!CASE) + { + if(v1 >= 'A' && v1 <= 'Z') v1 += 32; + if(v2 >= 'A' && v2 <= 'Z') v2 += 32; + } + + if(v1 == v2) + return false; + + bool result = v1 < v2; + return ASC ? result : !result; + } +}; + +typedef character_sort_func char_asc_case_sort_func; +typedef character_sort_func uchar_asc_case_sort_func; +typedef character_sort_func wchar_asc_case_sort_func; +typedef character_sort_func char_asc_ucase_sort_func; +typedef character_sort_func uchar_asc_ucase_sort_func; +typedef character_sort_func wchar_asc_ucase_sort_func; +typedef character_sort_func char_desc_case_sort_func; +typedef character_sort_func uchar_desc_case_sort_func; +typedef character_sort_func wchar_desc_case_sort_func; +typedef character_sort_func char_desc_ucase_sort_func; +typedef character_sort_func uchar_desc_ucase_sort_func; +typedef character_sort_func wchar_desc_ucase_sort_func; + +template +// T -> TCHAR* / CString +struct str_sort_func +{ + bool operator() (const T& v1, const T& v2) const + { + bool result; + + if(CASE) + { + int v = lstrcmp((LPCTSTR)v1, (LPCTSTR)v2); + if(v == 0) + result = false; + else + result = v < 0; + } + else + { + int v = tstricmp((LPCTSTR)v1, (LPCTSTR)v2); + if(v == 0) + result = false; + else + result = v < 0; + } + + return ASC ? result : !result; + } +}; + +typedef str_sort_func tchar_ptr_asc_case_sort_func; +typedef str_sort_func string_asc_case_sort_func; +typedef str_sort_func tchar_ptr_asc_ucase_sort_func; +typedef str_sort_func string_asc_ucase_sort_func; +typedef str_sort_func tchar_ptr_desc_case_sort_func; +typedef str_sort_func string_desc_case_sort_func; +typedef str_sort_func tchar_ptr_desc_ucase_sort_func; +typedef str_sort_func string_desc_ucase_sort_func; + +template::value>> +class CRandomIntegralT +{ +public: + _IntType Generate() {return dist(gen);} + _IntType operator()() {return Generate();} + +public: + CRandomIntegralT(_IntType from, _IntType to) : gen(rd()), dist(from, to) + { + + } + +private: + random_device rd; + mt19937 gen; + uniform_int_distribution<_IntType> dist; +}; + +template::value>> +class CRandomRealTypeT +{ +public: + _RealType Generate() {return dist(gen);} + _RealType operator()() {return Generate();} + +public: + CRandomRealTypeT(_RealType from, _RealType to) : gen(rd()), dist(from, to) + { + + } + +private: + random_device rd; + mt19937 gen; + uniform_real_distribution<_RealType> dist; +}; + +typedef CRandomIntegralT CRandomInt; +typedef CRandomIntegralT CRandomLong; +typedef CRandomIntegralT CRandomUint; +typedef CRandomIntegralT CRandomUlong; +typedef CRandomIntegralT CRandomInt64; +typedef CRandomIntegralT CRandomUint64; + +typedef CRandomRealTypeT CRandomFloat; +typedef CRandomRealTypeT CRandomDouble; diff --git a/common/Semaphore.h b/common/Semaphore.h new file mode 100644 index 0000000..0d897f8 --- /dev/null +++ b/common/Semaphore.h @@ -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 + +using namespace std; + +class CSEM +{ +public: + void Wait() + { + CMutexLock2 lock(m_mtx); + + m_cv.wait(lock); + } + + template + void Wait(_Predicate p) + { + CMutexLock2 lock(m_mtx); + + m_cv.wait(lock, p); + } + + template + 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 + bool WaitFor(const chrono::duration<_Rep, _Period>& t, _Predicate p) + { + CMutexLock2 lock(m_mtx); + + return m_cv.wait_for(lock, t, p); + } + + template + 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; diff --git a/common/SignalHandler.h b/common/SignalHandler.h new file mode 100644 index 0000000..dd44ce2 --- /dev/null +++ b/common/SignalHandler.h @@ -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 + +#include + +using namespace std; + +template class CSignalHandler +{ +public: + using MT = CSignalHandler; + using SI = siginfo_t; + using SS = sigset_t; + using CHandlerThread = CThread; + using F = VOID (T::*)(const SI*); + using SF = VOID (*)(const SI*); + using SSPTR = unique_ptr; + 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(); + 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(); + + ::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::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_>; diff --git a/common/Singleton.h b/common/Singleton.h new file mode 100644 index 0000000..81592a3 --- /dev/null +++ b/common/Singleton.h @@ -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 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##_Single_Object_; \ No newline at end of file diff --git a/common/StringT.h b/common/StringT.h new file mode 100644 index 0000000..55096bd --- /dev/null +++ b/common/StringT.h @@ -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 +#include +#include + +using namespace std; + +template, 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 + 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 + 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 + 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 + 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 +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 +CStringT<_CharT,_Traits,_Alloc> + operator+(const _CharT* __lhs, const CStringT<_CharT,_Traits,_Alloc>& __rhs); + +template +CStringT<_CharT,_Traits,_Alloc> + operator+(_CharT __lhs, const CStringT<_CharT,_Traits,_Alloc>& __rhs); + +template +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 +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 +inline CStringT<_CharT, _Traits, _Alloc> + operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, const CStringT<_CharT, _Traits, _Alloc>& __rhs) +{return std::move(__lhs.append(__rhs));} + +template +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 +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 +inline CStringT<_CharT, _Traits, _Alloc> + operator+(const _CharT* __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs) +{return std::move(__rhs.insert(0, __lhs));} + +template +inline CStringT<_CharT, _Traits, _Alloc> + operator+(_CharT __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs) +{return std::move(__rhs.insert(0, 1, __lhs));} + +template +inline CStringT<_CharT, _Traits, _Alloc> + operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, const _CharT* __rhs) +{return std::move(__lhs.append(__rhs));} + +template +inline CStringT<_CharT, _Traits, _Alloc> + operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, _CharT __rhs) +{return std::move(__lhs.append(1, __rhs));} +#endif + +using CStringA = CStringT; +using CStringW = CStringT; + +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 +inline size_t hash_value(const _Kty& _Keyval) +{ + return ((size_t)_Keyval ^ _HASH_SEED); +} + +template +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 +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 +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))); +} diff --git a/common/SysHelper.cpp b/common/SysHelper.cpp new file mode 100644 index 0000000..e12cbad --- /dev/null +++ b/common/SysHelper.cpp @@ -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 +#include + +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; +} diff --git a/common/SysHelper.h b/common/SysHelper.h new file mode 100644 index 0000000..4a35e12 --- /dev/null +++ b/common/SysHelper.h @@ -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 +#include +#include + +#include +#include + +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 +static inline void __atomic_yield() +{ + std::this_thread::yield(); +} +#elif defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include +static inline void __atomic_yield() +{ + YieldProcessor(); +} +#elif defined(__SSE2__) +#include +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 +static inline void __atomic_yield() +{ + smt_pause(); +} +#elif defined(__wasi__) +#include +static inline void __atomic_yield() +{ + sched_yield(); +} +#else +#include +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 + +#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 diff --git a/common/Thread.cpp b/common/Thread.cpp new file mode 100644 index 0000000..059d607 --- /dev/null +++ b/common/Thread.cpp @@ -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; +} diff --git a/common/Thread.h b/common/Thread.h new file mode 100644 index 0000000..d070bc8 --- /dev/null +++ b/common/Thread.h @@ -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 +#include +#include + +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 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 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::value && !is_void::value>> + PVOID Run(T_*, R_*) + { + return (PVOID)(UINT_PTR)((m_pRunner->*m_pFunc)(m_pArg)); + } + + template::value>> + PVOID Run(T_*, PVOID) + { + (m_pRunner->*m_pFunc)(m_pArg); + + return nullptr; + } + + template::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 using CStaticThread = CThread<__CFakeRunnerClass_, P, R>; + +template class CTlsObj +{ + using TLocalMap = unordered_map; + +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 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 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 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 CTlsSimple +{ + using TLocalMap = unordered_map; + + 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; +}; diff --git a/common/http/Readme.txt b/common/http/Readme.txt new file mode 100644 index 0000000..b6dc35b --- /dev/null +++ b/common/http/Readme.txt @@ -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 \ No newline at end of file diff --git a/common/http/llhttp.h b/common/http/llhttp.h new file mode 100644 index 0000000..63ad1af --- /dev/null +++ b/common/http/llhttp.h @@ -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 + +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 + +#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_ */ diff --git a/common/http/llhttp_api.c b/common/http/llhttp_api.c new file mode 100644 index 0000000..adf35ff --- /dev/null +++ b/common/http/llhttp_api.c @@ -0,0 +1,520 @@ +#include +#include +#include + +#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 diff --git a/common/http/llhttp_internal.c b/common/http/llhttp_internal.c new file mode 100644 index 0000000..999ec50 --- /dev/null +++ b/common/http/llhttp_internal.c @@ -0,0 +1,10180 @@ +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wconversion" + #pragma GCC diagnostic ignored "-Wsign-conversion" + #pragma GCC diagnostic ignored "-Wunused-variable" + #pragma GCC diagnostic ignored "-Wunreachable-code" +#endif + +#include +#include +#include + +#ifdef __SSE4_2__ + #ifdef _MSC_VER + #include + #else /* !_MSC_VER */ + #include + #endif /* _MSC_VER */ +#endif /* __SSE4_2__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) +#endif /* _MSC_VER */ + +#include "llhttp.h" + +typedef int (*llhttp__internal__span_cb)( + llhttp__internal_t*, const char*, const char*); + +static const unsigned char llparse_blob0[] = { + 'o', 'n' +}; +static const unsigned char llparse_blob1[] = { + 'e', 'c', 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob2[] = { + 'l', 'o', 's', 'e' +}; +static const unsigned char llparse_blob3[] = { + 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' +}; +static const unsigned char llparse_blob4[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char llparse_blob5[] = { + 'c', 'h', 'u', 'n', 'k', 'e', 'd' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob6[] = { + 0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob7[] = { + '!', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', + 'Z', '^', 'z', '|', '|' +}; +#endif /* __SSE4_2__ */ +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob8[] = { + '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob9[] = { + 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' +}; +static const unsigned char llparse_blob10[] = { + 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', + 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob11[] = { + 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', + 'o', 'd', 'i', 'n', 'g' +}; +static const unsigned char llparse_blob12[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char llparse_blob13[] = { + 'T', 'T', 'P', '/' +}; +static const unsigned char llparse_blob14[] = { + 0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa +}; +static const unsigned char llparse_blob15[] = { + 'C', 'E', '/' +}; +static const unsigned char llparse_blob16[] = { + 'T', 'S', 'P', '/' +}; +static const unsigned char llparse_blob17[] = { + 'N', 'O', 'U', 'N', 'C', 'E' +}; +static const unsigned char llparse_blob18[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob19[] = { + 'E', 'C', 'K', 'O', 'U', 'T' +}; +static const unsigned char llparse_blob20[] = { + 'N', 'E', 'C', 'T' +}; +static const unsigned char llparse_blob21[] = { + 'E', 'T', 'E' +}; +static const unsigned char llparse_blob22[] = { + 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob23[] = { + 'L', 'U', 'S', 'H' +}; +static const unsigned char llparse_blob24[] = { + 'E', 'T' +}; +static const unsigned char llparse_blob25[] = { + 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' +}; +static const unsigned char llparse_blob26[] = { + 'E', 'A', 'D' +}; +static const unsigned char llparse_blob27[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob28[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob29[] = { + 'S', 'E', 'A', 'R', 'C', 'H' +}; +static const unsigned char llparse_blob30[] = { + 'R', 'G', 'E' +}; +static const unsigned char llparse_blob31[] = { + 'C', 'T', 'I', 'V', 'I', 'T', 'Y' +}; +static const unsigned char llparse_blob32[] = { + 'L', 'E', 'N', 'D', 'A', 'R' +}; +static const unsigned char llparse_blob33[] = { + 'V', 'E' +}; +static const unsigned char llparse_blob34[] = { + 'O', 'T', 'I', 'F', 'Y' +}; +static const unsigned char llparse_blob35[] = { + 'P', 'T', 'I', 'O', 'N', 'S' +}; +static const unsigned char llparse_blob36[] = { + 'C', 'H' +}; +static const unsigned char llparse_blob37[] = { + 'S', 'E' +}; +static const unsigned char llparse_blob38[] = { + 'A', 'Y' +}; +static const unsigned char llparse_blob39[] = { + 'S', 'T' +}; +static const unsigned char llparse_blob40[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob41[] = { + 'A', 'T', 'C', 'H' +}; +static const unsigned char llparse_blob42[] = { + 'G', 'E' +}; +static const unsigned char llparse_blob43[] = { + 'U', 'E', 'R', 'Y' +}; +static const unsigned char llparse_blob44[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob45[] = { + 'O', 'R', 'D' +}; +static const unsigned char llparse_blob46[] = { + 'I', 'R', 'E', 'C', 'T' +}; +static const unsigned char llparse_blob47[] = { + 'O', 'R', 'T' +}; +static const unsigned char llparse_blob48[] = { + 'R', 'C', 'H' +}; +static const unsigned char llparse_blob49[] = { + 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' +}; +static const unsigned char llparse_blob50[] = { + 'U', 'R', 'C', 'E' +}; +static const unsigned char llparse_blob51[] = { + 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob52[] = { + 'A', 'R', 'D', 'O', 'W', 'N' +}; +static const unsigned char llparse_blob53[] = { + 'A', 'C', 'E' +}; +static const unsigned char llparse_blob54[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob55[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob56[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob57[] = { + 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob58[] = { + 'H', 'T', 'T', 'P', '/' +}; +static const unsigned char llparse_blob59[] = { + 'A', 'D' +}; +static const unsigned char llparse_blob60[] = { + 'T', 'P', '/' +}; + +enum llparse_match_status_e { + kMatchComplete, + kMatchPause, + kMatchMismatch +}; +typedef enum llparse_match_status_e llparse_match_status_t; + +struct llparse_match_s { + llparse_match_status_t status; + const unsigned char* current; +}; +typedef struct llparse_match_s llparse_match_t; + +static llparse_match_t llparse__match_sequence_to_lower( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p)); + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +static llparse_match_t llparse__match_sequence_to_lower_unsafe( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = ((*p) | 0x20); + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +static llparse_match_t llparse__match_sequence_id( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = *p; + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +enum llparse_state_e { + s_error, + s_n_llhttp__internal__n_closed, + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete, + s_n_llhttp__internal__n_pause_1, + s_n_llhttp__internal__n_invoke_is_equal_upgrade, + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2, + s_n_llhttp__internal__n_chunk_data_almost_done_1, + s_n_llhttp__internal__n_chunk_data_almost_done, + s_n_llhttp__internal__n_consume_content_length, + s_n_llhttp__internal__n_span_start_llhttp__on_body, + s_n_llhttp__internal__n_invoke_is_equal_content_length, + s_n_llhttp__internal__n_chunk_size_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_9, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2, + s_n_llhttp__internal__n_invoke_test_lenient_flags_10, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1, + s_n_llhttp__internal__n_chunk_extension_quoted_value_done, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2, + s_n_llhttp__internal__n_error_30, + s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair, + s_n_llhttp__internal__n_error_31, + s_n_llhttp__internal__n_chunk_extension_quoted_value, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3, + s_n_llhttp__internal__n_error_33, + s_n_llhttp__internal__n_chunk_extension_value, + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value, + s_n_llhttp__internal__n_error_34, + s_n_llhttp__internal__n_chunk_extension_name, + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name, + s_n_llhttp__internal__n_chunk_extensions, + s_n_llhttp__internal__n_chunk_size_otherwise, + s_n_llhttp__internal__n_chunk_size, + s_n_llhttp__internal__n_chunk_size_digit, + s_n_llhttp__internal__n_invoke_update_content_length_1, + s_n_llhttp__internal__n_consume_content_length_1, + s_n_llhttp__internal__n_span_start_llhttp__on_body_1, + s_n_llhttp__internal__n_eof, + s_n_llhttp__internal__n_span_start_llhttp__on_body_2, + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete, + s_n_llhttp__internal__n_error_5, + s_n_llhttp__internal__n_headers_almost_done, + s_n_llhttp__internal__n_header_field_colon_discard_ws, + s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value, + s_n_llhttp__internal__n_header_value_discard_lws, + s_n_llhttp__internal__n_header_value_discard_ws_almost_done, + s_n_llhttp__internal__n_header_value_lws, + s_n_llhttp__internal__n_header_value_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_17, + s_n_llhttp__internal__n_header_value_lenient, + s_n_llhttp__internal__n_error_54, + s_n_llhttp__internal__n_header_value_otherwise, + s_n_llhttp__internal__n_header_value_connection_token, + s_n_llhttp__internal__n_header_value_connection_ws, + s_n_llhttp__internal__n_header_value_connection_1, + s_n_llhttp__internal__n_header_value_connection_2, + s_n_llhttp__internal__n_header_value_connection_3, + s_n_llhttp__internal__n_header_value_connection, + s_n_llhttp__internal__n_error_56, + s_n_llhttp__internal__n_error_57, + s_n_llhttp__internal__n_header_value_content_length_ws, + s_n_llhttp__internal__n_header_value_content_length, + s_n_llhttp__internal__n_error_59, + s_n_llhttp__internal__n_error_58, + s_n_llhttp__internal__n_header_value_te_token_ows, + s_n_llhttp__internal__n_header_value, + s_n_llhttp__internal__n_header_value_te_token, + s_n_llhttp__internal__n_header_value_te_chunked_last, + s_n_llhttp__internal__n_header_value_te_chunked, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1, + s_n_llhttp__internal__n_header_value_discard_ws, + s_n_llhttp__internal__n_invoke_load_header_state, + s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete, + s_n_llhttp__internal__n_header_field_general_otherwise, + s_n_llhttp__internal__n_header_field_general, + s_n_llhttp__internal__n_header_field_colon, + s_n_llhttp__internal__n_header_field_3, + s_n_llhttp__internal__n_header_field_4, + s_n_llhttp__internal__n_header_field_2, + s_n_llhttp__internal__n_header_field_1, + s_n_llhttp__internal__n_header_field_5, + s_n_llhttp__internal__n_header_field_6, + s_n_llhttp__internal__n_header_field_7, + s_n_llhttp__internal__n_header_field, + s_n_llhttp__internal__n_span_start_llhttp__on_header_field, + s_n_llhttp__internal__n_header_field_start, + s_n_llhttp__internal__n_headers_start, + s_n_llhttp__internal__n_url_to_http_09, + s_n_llhttp__internal__n_url_skip_to_http09, + s_n_llhttp__internal__n_url_skip_lf_to_http09_1, + s_n_llhttp__internal__n_url_skip_lf_to_http09, + s_n_llhttp__internal__n_req_pri_upgrade, + s_n_llhttp__internal__n_req_http_complete_crlf, + s_n_llhttp__internal__n_req_http_complete, + s_n_llhttp__internal__n_invoke_load_method_1, + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete, + s_n_llhttp__internal__n_error_66, + s_n_llhttp__internal__n_error_73, + s_n_llhttp__internal__n_req_http_minor, + s_n_llhttp__internal__n_error_74, + s_n_llhttp__internal__n_req_http_dot, + s_n_llhttp__internal__n_error_75, + s_n_llhttp__internal__n_req_http_major, + s_n_llhttp__internal__n_span_start_llhttp__on_version, + s_n_llhttp__internal__n_req_http_start_1, + s_n_llhttp__internal__n_req_http_start_2, + s_n_llhttp__internal__n_req_http_start_3, + s_n_llhttp__internal__n_req_http_start, + s_n_llhttp__internal__n_url_to_http, + s_n_llhttp__internal__n_url_skip_to_http, + s_n_llhttp__internal__n_url_fragment, + s_n_llhttp__internal__n_span_end_stub_query_3, + s_n_llhttp__internal__n_url_query, + s_n_llhttp__internal__n_url_query_or_fragment, + s_n_llhttp__internal__n_url_path, + s_n_llhttp__internal__n_span_start_stub_path_2, + s_n_llhttp__internal__n_span_start_stub_path, + s_n_llhttp__internal__n_span_start_stub_path_1, + s_n_llhttp__internal__n_url_server_with_at, + s_n_llhttp__internal__n_url_server, + s_n_llhttp__internal__n_url_schema_delim_1, + s_n_llhttp__internal__n_url_schema_delim, + s_n_llhttp__internal__n_span_end_stub_schema, + s_n_llhttp__internal__n_url_schema, + s_n_llhttp__internal__n_url_start, + s_n_llhttp__internal__n_span_start_llhttp__on_url_1, + s_n_llhttp__internal__n_url_entry_normal, + s_n_llhttp__internal__n_span_start_llhttp__on_url, + s_n_llhttp__internal__n_url_entry_connect, + s_n_llhttp__internal__n_req_spaces_before_url, + s_n_llhttp__internal__n_req_first_space_before_url, + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1, + s_n_llhttp__internal__n_after_start_req_2, + s_n_llhttp__internal__n_after_start_req_3, + s_n_llhttp__internal__n_after_start_req_1, + s_n_llhttp__internal__n_after_start_req_4, + s_n_llhttp__internal__n_after_start_req_6, + s_n_llhttp__internal__n_after_start_req_8, + s_n_llhttp__internal__n_after_start_req_9, + s_n_llhttp__internal__n_after_start_req_7, + s_n_llhttp__internal__n_after_start_req_5, + s_n_llhttp__internal__n_after_start_req_12, + s_n_llhttp__internal__n_after_start_req_13, + s_n_llhttp__internal__n_after_start_req_11, + s_n_llhttp__internal__n_after_start_req_10, + s_n_llhttp__internal__n_after_start_req_14, + s_n_llhttp__internal__n_after_start_req_17, + s_n_llhttp__internal__n_after_start_req_16, + s_n_llhttp__internal__n_after_start_req_15, + s_n_llhttp__internal__n_after_start_req_18, + s_n_llhttp__internal__n_after_start_req_20, + s_n_llhttp__internal__n_after_start_req_21, + s_n_llhttp__internal__n_after_start_req_19, + s_n_llhttp__internal__n_after_start_req_23, + s_n_llhttp__internal__n_after_start_req_24, + s_n_llhttp__internal__n_after_start_req_26, + s_n_llhttp__internal__n_after_start_req_28, + s_n_llhttp__internal__n_after_start_req_29, + s_n_llhttp__internal__n_after_start_req_27, + s_n_llhttp__internal__n_after_start_req_25, + s_n_llhttp__internal__n_after_start_req_30, + s_n_llhttp__internal__n_after_start_req_22, + s_n_llhttp__internal__n_after_start_req_31, + s_n_llhttp__internal__n_after_start_req_32, + s_n_llhttp__internal__n_after_start_req_35, + s_n_llhttp__internal__n_after_start_req_36, + s_n_llhttp__internal__n_after_start_req_34, + s_n_llhttp__internal__n_after_start_req_37, + s_n_llhttp__internal__n_after_start_req_38, + s_n_llhttp__internal__n_after_start_req_42, + s_n_llhttp__internal__n_after_start_req_43, + s_n_llhttp__internal__n_after_start_req_41, + s_n_llhttp__internal__n_after_start_req_40, + s_n_llhttp__internal__n_after_start_req_39, + s_n_llhttp__internal__n_after_start_req_45, + s_n_llhttp__internal__n_after_start_req_44, + s_n_llhttp__internal__n_after_start_req_33, + s_n_llhttp__internal__n_after_start_req_46, + s_n_llhttp__internal__n_after_start_req_49, + s_n_llhttp__internal__n_after_start_req_50, + s_n_llhttp__internal__n_after_start_req_51, + s_n_llhttp__internal__n_after_start_req_52, + s_n_llhttp__internal__n_after_start_req_48, + s_n_llhttp__internal__n_after_start_req_47, + s_n_llhttp__internal__n_after_start_req_55, + s_n_llhttp__internal__n_after_start_req_57, + s_n_llhttp__internal__n_after_start_req_58, + s_n_llhttp__internal__n_after_start_req_56, + s_n_llhttp__internal__n_after_start_req_54, + s_n_llhttp__internal__n_after_start_req_59, + s_n_llhttp__internal__n_after_start_req_60, + s_n_llhttp__internal__n_after_start_req_53, + s_n_llhttp__internal__n_after_start_req_62, + s_n_llhttp__internal__n_after_start_req_63, + s_n_llhttp__internal__n_after_start_req_61, + s_n_llhttp__internal__n_after_start_req_66, + s_n_llhttp__internal__n_after_start_req_68, + s_n_llhttp__internal__n_after_start_req_69, + s_n_llhttp__internal__n_after_start_req_67, + s_n_llhttp__internal__n_after_start_req_70, + s_n_llhttp__internal__n_after_start_req_65, + s_n_llhttp__internal__n_after_start_req_64, + s_n_llhttp__internal__n_after_start_req, + s_n_llhttp__internal__n_span_start_llhttp__on_method_1, + s_n_llhttp__internal__n_res_line_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_30, + s_n_llhttp__internal__n_res_status, + s_n_llhttp__internal__n_span_start_llhttp__on_status, + s_n_llhttp__internal__n_res_status_code_otherwise, + s_n_llhttp__internal__n_res_status_code_digit_3, + s_n_llhttp__internal__n_res_status_code_digit_2, + s_n_llhttp__internal__n_res_status_code_digit_1, + s_n_llhttp__internal__n_res_after_version, + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1, + s_n_llhttp__internal__n_error_89, + s_n_llhttp__internal__n_error_103, + s_n_llhttp__internal__n_res_http_minor, + s_n_llhttp__internal__n_error_104, + s_n_llhttp__internal__n_res_http_dot, + s_n_llhttp__internal__n_error_105, + s_n_llhttp__internal__n_res_http_major, + s_n_llhttp__internal__n_span_start_llhttp__on_version_1, + s_n_llhttp__internal__n_start_res, + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete, + s_n_llhttp__internal__n_req_or_res_method_2, + s_n_llhttp__internal__n_invoke_update_type_1, + s_n_llhttp__internal__n_req_or_res_method_3, + s_n_llhttp__internal__n_req_or_res_method_1, + s_n_llhttp__internal__n_req_or_res_method, + s_n_llhttp__internal__n_span_start_llhttp__on_method, + s_n_llhttp__internal__n_start_req_or_res, + s_n_llhttp__internal__n_invoke_load_type, + s_n_llhttp__internal__n_invoke_update_finish, + s_n_llhttp__internal__n_start, +}; +typedef enum llparse_state_e llparse_state_t; + +int llhttp__on_method( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_url( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_version( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_field( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_value( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_body( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_name( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_value( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_status( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_initial_message_completed( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->initial_message_completed; +} + +int llhttp__on_reset( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 2; + return 0; +} + +int llhttp__on_message_begin( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->type; +} + +int llhttp__internal__c_store_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->method = match; + return 0; +} + +int llhttp__on_method_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method == 5; +} + +int llhttp__internal__c_update_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_major = 0; + return 0; +} + +int llhttp__internal__c_update_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_minor = 9; + return 0; +} + +int llhttp__on_url_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_test_lenient_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 1) == 1; +} + +int llhttp__internal__c_test_lenient_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 256) == 256; +} + +int llhttp__internal__c_test_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 128) == 128; +} + +int llhttp__on_chunk_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->upgrade == 1; +} + +int llhttp__after_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->content_length = 0; + return 0; +} + +int llhttp__internal__c_update_initial_message_completed( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->initial_message_completed = 1; + return 0; +} + +int llhttp__internal__c_update_finish_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 0; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 4) == 4; +} + +int llhttp__internal__c_test_lenient_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 32) == 32; +} + +int llhttp__before_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__after_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_mul_add_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 16) { + return 1; + } + + state->content_length *= 16; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 512) == 512; +} + +int llhttp__on_chunk_header( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->content_length == 0; +} + +int llhttp__internal__c_test_lenient_flags_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 128) == 128; +} + +int llhttp__internal__c_or_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 128; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 64) == 64; +} + +int llhttp__on_chunk_extension_name_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_value_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 1; + return 0; +} + +int llhttp__internal__c_or_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 64; + return 0; +} + +int llhttp__internal__c_update_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->upgrade = 1; + return 0; +} + +int llhttp__internal__c_store_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->header_state = match; + return 0; +} + +int llhttp__on_header_field_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->header_state; +} + +int llhttp__internal__c_test_flags_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 512) == 512; +} + +int llhttp__internal__c_test_lenient_flags_22( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 2) == 2; +} + +int llhttp__internal__c_or_flags_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 1; + return 0; +} + +int llhttp__internal__c_update_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 1; + return 0; +} + +int llhttp__on_header_value_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_or_flags_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 2; + return 0; +} + +int llhttp__internal__c_or_flags_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 4; + return 0; +} + +int llhttp__internal__c_or_flags_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 8; + return 0; +} + +int llhttp__internal__c_update_header_state_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 6; + return 0; +} + +int llhttp__internal__c_update_header_state_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 0; + return 0; +} + +int llhttp__internal__c_update_header_state_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 5; + return 0; +} + +int llhttp__internal__c_update_header_state_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 7; + return 0; +} + +int llhttp__internal__c_test_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 32) == 32; +} + +int llhttp__internal__c_mul_add_content_length_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 10) { + return 1; + } + + state->content_length *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__internal__c_or_flags_17( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 32; + return 0; +} + +int llhttp__internal__c_test_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 8) == 8; +} + +int llhttp__internal__c_test_lenient_flags_20( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 8) == 8; +} + +int llhttp__internal__c_or_flags_18( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 512; + return 0; +} + +int llhttp__internal__c_and_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags &= -9; + return 0; +} + +int llhttp__internal__c_update_header_state_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 8; + return 0; +} + +int llhttp__internal__c_or_flags_20( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 16; + return 0; +} + +int llhttp__internal__c_load_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method; +} + +int llhttp__internal__c_store_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_major = match; + return 0; +} + +int llhttp__internal__c_store_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_minor = match; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_24( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 16) == 16; +} + +int llhttp__on_version_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->http_major; +} + +int llhttp__internal__c_load_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->http_minor; +} + +int llhttp__internal__c_update_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->status_code = 0; + return 0; +} + +int llhttp__internal__c_mul_add_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->status_code > 0xffff / 10) { + return 1; + } + + state->status_code *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->status_code > 0xffff - match) { + return 1; + } + } else { + if (state->status_code < 0 - match) { + return 1; + } + } + state->status_code += match; + return 0; +} + +int llhttp__on_status_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 1; + return 0; +} + +int llhttp__internal__c_update_type_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 2; + return 0; +} + +int llhttp__internal_init(llhttp__internal_t* state) { + memset(state, 0, sizeof(*state)); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start; + return 0; +} + +static llparse_state_t llhttp__internal__run( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + int match; + switch ((llparse_state_t) (intptr_t) state->_current) { + case s_n_llhttp__internal__n_closed: + s_n_llhttp__internal__n_closed: { + if (p == endp) { + return s_n_llhttp__internal__n_closed; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_closed; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_closed; + } + default: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: { + switch (llhttp__after_message_complete(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_update_content_length; + default: + goto s_n_llhttp__internal__n_invoke_update_finish_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_pause_1: + s_n_llhttp__internal__n_pause_1: { + state->error = 0x16; + state->reason = "Pause on CONNECT/Upgrade"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_is_equal_upgrade: + s_n_llhttp__internal__n_invoke_is_equal_upgrade: { + switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + default: + goto s_n_llhttp__internal__n_pause_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_13; + default: + goto s_n_llhttp__internal__n_error_38; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_data_almost_done_1: + s_n_llhttp__internal__n_chunk_data_almost_done_1: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done_1; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_data_almost_done: + s_n_llhttp__internal__n_chunk_data_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_6; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_data_almost_done_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_consume_content_length: + s_n_llhttp__internal__n_consume_content_length: { + size_t avail; + uint64_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body: + s_n_llhttp__internal__n_span_start_llhttp__on_body: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_is_equal_content_length: + s_n_llhttp__internal__n_invoke_is_equal_content_length: { + switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body; + default: + goto s_n_llhttp__internal__n_invoke_or_flags; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_almost_done: + s_n_llhttp__internal__n_chunk_size_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_8; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_9: + s_n_llhttp__internal__n_invoke_test_lenient_flags_9: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_20; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_9; + case 21: + goto s_n_llhttp__internal__n_pause_5; + default: + goto s_n_llhttp__internal__n_error_19; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + case 21: + goto s_n_llhttp__internal__n_pause_6; + default: + goto s_n_llhttp__internal__n_error_21; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extensions; + case 21: + goto s_n_llhttp__internal__n_pause_7; + default: + goto s_n_llhttp__internal__n_error_22; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_10: + s_n_llhttp__internal__n_invoke_test_lenient_flags_10: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_25; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_10; + case 21: + goto s_n_llhttp__internal__n_pause_8; + default: + goto s_n_llhttp__internal__n_error_24; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + case 21: + goto s_n_llhttp__internal__n_pause_9; + default: + goto s_n_llhttp__internal__n_error_26; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value_done: + s_n_llhttp__internal__n_chunk_extension_quoted_value_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_11; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + case ';': { + p++; + goto s_n_llhttp__internal__n_chunk_extensions; + } + default: { + goto s_n_llhttp__internal__n_error_29; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + case 21: + goto s_n_llhttp__internal__n_pause_10; + default: + goto s_n_llhttp__internal__n_error_27; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_30: + s_n_llhttp__internal__n_error_30: { + state->error = 0x2; + state->reason = "Invalid quoted-pair in chunk extensions quoted value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: + s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_31: + s_n_llhttp__internal__n_error_31: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions quoted value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value: + s_n_llhttp__internal__n_chunk_extension_quoted_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extensions; + case 21: + goto s_n_llhttp__internal__n_pause_11; + default: + goto s_n_llhttp__internal__n_error_32; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_33: + s_n_llhttp__internal__n_error_33: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_value: + s_n_llhttp__internal__n_chunk_extension_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 4, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 5, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_value; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_value; + } + case 4: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + case 5: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_chunk_extension_value; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_34: + s_n_llhttp__internal__n_error_34: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions name"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_name: + s_n_llhttp__internal__n_chunk_extension_name: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 0, 5, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_name; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_name; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2; + } + case 5: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_chunk_extension_name; + goto s_n_llhttp__internal__n_chunk_extension_name; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extensions: + s_n_llhttp__internal__n_chunk_extensions: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extensions; + } + switch (*p) { + case 13: { + p++; + goto s_n_llhttp__internal__n_error_17; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_error_18; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_otherwise: + s_n_llhttp__internal__n_chunk_size_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_otherwise; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_5; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4; + } + case ';': { + p++; + goto s_n_llhttp__internal__n_chunk_extensions; + } + default: { + goto s_n_llhttp__internal__n_error_35; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size: + s_n_llhttp__internal__n_chunk_size: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_chunk_size_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_digit: + s_n_llhttp__internal__n_chunk_size_digit: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_digit; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_error_37; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_update_content_length_1: + s_n_llhttp__internal__n_invoke_update_content_length_1: { + switch (llhttp__internal__c_update_content_length(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_chunk_size_digit; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_consume_content_length_1: + s_n_llhttp__internal__n_consume_content_length_1: { + size_t avail; + uint64_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length_1; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_1: + s_n_llhttp__internal__n_span_start_llhttp__on_body_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length_1; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_eof: + s_n_llhttp__internal__n_eof: { + if (p == endp) { + return s_n_llhttp__internal__n_eof; + } + p++; + goto s_n_llhttp__internal__n_eof; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_2: + s_n_llhttp__internal__n_span_start_llhttp__on_body_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_eof; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: { + switch (llhttp__after_headers_complete(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_content_length_1; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + case 4: + goto s_n_llhttp__internal__n_invoke_update_finish_3; + case 5: + goto s_n_llhttp__internal__n_error_39; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_5: + s_n_llhttp__internal__n_error_5: { + state->error = 0xa; + state->reason = "Invalid header field char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_headers_almost_done: + s_n_llhttp__internal__n_headers_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_headers_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_flags_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_12; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_colon_discard_ws: + s_n_llhttp__internal__n_header_field_colon_discard_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_colon_discard_ws; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_field_colon_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_header_field_colon; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: { + switch (llhttp__on_header_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_header_field_start; + case 21: + goto s_n_llhttp__internal__n_pause_18; + default: + goto s_n_llhttp__internal__n_error_48; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_lws: + s_n_llhttp__internal__n_header_value_discard_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_lws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_ws_almost_done: + s_n_llhttp__internal__n_header_value_discard_ws_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_lws; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_16; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_lws: + s_n_llhttp__internal__n_header_value_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lws; + } + switch (*p) { + case 9: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18; + } + case ' ': { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state_5; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_almost_done: + s_n_llhttp__internal__n_header_value_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_lws; + } + default: { + goto s_n_llhttp__internal__n_error_53; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_17: + s_n_llhttp__internal__n_invoke_test_lenient_flags_17: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_almost_done; + default: + goto s_n_llhttp__internal__n_error_51; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_lenient: + s_n_llhttp__internal__n_header_value_lenient: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lenient; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; + } + default: { + p++; + goto s_n_llhttp__internal__n_header_value_lenient; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_54: + s_n_llhttp__internal__n_error_54: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_otherwise: + s_n_llhttp__internal__n_header_value_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_otherwise; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_19; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_token: + s_n_llhttp__internal__n_header_value_connection_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_connection_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_ws: + s_n_llhttp__internal__n_header_value_connection_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case 13: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + case ',': { + p++; + goto s_n_llhttp__internal__n_invoke_load_header_state_6; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_5; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_1: + s_n_llhttp__internal__n_header_value_connection_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_1; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_2: + s_n_llhttp__internal__n_header_value_connection_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_2; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_6; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_3: + s_n_llhttp__internal__n_header_value_connection_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_3; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection: + s_n_llhttp__internal__n_header_value_connection: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_1; + } + case 'k': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_2; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_3; + } + default: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_56: + s_n_llhttp__internal__n_error_56: { + state->error = 0xb; + state->reason = "Content-Length overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_57: + s_n_llhttp__internal__n_error_57: { + state->error = 0xb; + state->reason = "Invalid character in Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_content_length_ws: + s_n_llhttp__internal__n_header_value_content_length_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_or_flags_17; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_or_flags_17; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_content_length: + s_n_llhttp__internal__n_header_value_content_length: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + default: { + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_59: + s_n_llhttp__internal__n_error_59: { + state->error = 0xf; + state->reason = "Invalid `Transfer-Encoding` header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_58: + s_n_llhttp__internal__n_error_58: { + state->error = 0xf; + state->reason = "Invalid `Transfer-Encoding` header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_token_ows: + s_n_llhttp__internal__n_header_value_te_token_ows: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token_ows; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value: + s_n_llhttp__internal__n_header_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob6); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 6, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_value; + } + goto s_n_llhttp__internal__n_header_value_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_token: + s_n_llhttp__internal__n_header_value_te_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_9; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_chunked_last: + s_n_llhttp__internal__n_header_value_te_chunked_last: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked_last; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + case ',': { + goto s_n_llhttp__internal__n_invoke_load_type_1; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_chunked: + s_n_llhttp__internal__n_header_value_te_chunked: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_te_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_invoke_load_header_state_3; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_ws: + s_n_llhttp__internal__n_header_value_discard_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_14; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_load_header_state: + s_n_llhttp__internal__n_invoke_load_header_state: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 2: + goto s_n_llhttp__internal__n_invoke_test_flags_4; + case 3: + goto s_n_llhttp__internal__n_invoke_test_flags_5; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: { + switch (llhttp__on_header_field_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_header_state; + case 21: + goto s_n_llhttp__internal__n_pause_19; + default: + goto s_n_llhttp__internal__n_error_45; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_general_otherwise: + s_n_llhttp__internal__n_header_field_general_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general_otherwise; + } + switch (*p) { + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2; + } + default: { + goto s_n_llhttp__internal__n_error_62; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_general: + s_n_llhttp__internal__n_header_field_general: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 16, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + ranges = _mm_loadu_si128((__m128i const*) llparse_blob8); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 2, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_field_general; + } + default: { + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_colon: + s_n_llhttp__internal__n_header_field_colon: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_colon; + } + switch (*p) { + case ' ': { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_13; + } + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_3: + s_n_llhttp__internal__n_header_field_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_3; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_4: + s_n_llhttp__internal__n_header_field_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_4; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob9, 10); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_2: + s_n_llhttp__internal__n_header_field_2: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_2; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 'n': { + p++; + goto s_n_llhttp__internal__n_header_field_3; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_4; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_1: + s_n_llhttp__internal__n_header_field_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_1; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob0, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_field_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_5: + s_n_llhttp__internal__n_header_field_5: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_5; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 15); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_5; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_6: + s_n_llhttp__internal__n_header_field_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_6; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 16); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_7: + s_n_llhttp__internal__n_header_field_7: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_7; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_7; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field: + s_n_llhttp__internal__n_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_field_1; + } + case 'p': { + p++; + goto s_n_llhttp__internal__n_header_field_5; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_6; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_field_7; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_field: + s_n_llhttp__internal__n_span_start_llhttp__on_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_field; + goto s_n_llhttp__internal__n_header_field; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_start: + s_n_llhttp__internal__n_header_field_start: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_headers_almost_done; + } + case ':': { + goto s_n_llhttp__internal__n_error_44; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_headers_start: + s_n_llhttp__internal__n_headers_start: { + if (p == endp) { + return s_n_llhttp__internal__n_headers_start; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags; + } + default: { + goto s_n_llhttp__internal__n_header_field_start; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_to_http_09: + s_n_llhttp__internal__n_url_to_http_09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_to_http_09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_http_major; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_to_http09: + s_n_llhttp__internal__n_url_skip_to_http09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + p++; + goto s_n_llhttp__internal__n_url_to_http_09; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_lf_to_http09_1: + s_n_llhttp__internal__n_url_skip_lf_to_http09_1: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_lf_to_http09_1; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_url_to_http_09; + } + default: { + goto s_n_llhttp__internal__n_error_63; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_lf_to_http09: + s_n_llhttp__internal__n_url_skip_lf_to_http09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_lf_to_http09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1; + } + default: { + goto s_n_llhttp__internal__n_error_63; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_pri_upgrade: + s_n_llhttp__internal__n_req_pri_upgrade: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_pri_upgrade; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 10); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_error_71; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_pri_upgrade; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_72; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_complete_crlf: + s_n_llhttp__internal__n_req_http_complete_crlf: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_complete_crlf; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_headers_start; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_26; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_complete: + s_n_llhttp__internal__n_req_http_complete: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_complete; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_25; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_req_http_complete_crlf; + } + default: { + goto s_n_llhttp__internal__n_error_70; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_load_method_1: + s_n_llhttp__internal__n_invoke_load_method_1: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 34: + goto s_n_llhttp__internal__n_req_pri_upgrade; + default: + goto s_n_llhttp__internal__n_req_http_complete; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: { + switch (llhttp__on_version_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method_1; + case 21: + goto s_n_llhttp__internal__n_pause_21; + default: + goto s_n_llhttp__internal__n_error_67; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_66: + s_n_llhttp__internal__n_error_66: { + state->error = 0x9; + state->reason = "Invalid HTTP version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_73: + s_n_llhttp__internal__n_error_73: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_minor: + s_n_llhttp__internal__n_req_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_2; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_74: + s_n_llhttp__internal__n_error_74: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_dot: + s_n_llhttp__internal__n_req_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_req_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_3; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_75: + s_n_llhttp__internal__n_error_75: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_major: + s_n_llhttp__internal__n_req_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_version: + s_n_llhttp__internal__n_span_start_llhttp__on_version: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_version; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_version; + goto s_n_llhttp__internal__n_req_http_major; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_1: + s_n_llhttp__internal__n_req_http_start_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_load_method; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_78; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_2: + s_n_llhttp__internal__n_req_http_start_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_load_method_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_78; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_3: + s_n_llhttp__internal__n_req_http_start_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_load_method_3; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_78; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start: + s_n_llhttp__internal__n_req_http_start: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_http_start; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_http_start_1; + } + case 'I': { + p++; + goto s_n_llhttp__internal__n_req_http_start_2; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_req_http_start_3; + } + default: { + goto s_n_llhttp__internal__n_error_78; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_to_http: + s_n_llhttp__internal__n_url_to_http: { + if (p == endp) { + return s_n_llhttp__internal__n_url_to_http; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_to_http: + s_n_llhttp__internal__n_url_skip_to_http: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + p++; + goto s_n_llhttp__internal__n_url_to_http; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_fragment: + s_n_llhttp__internal__n_url_fragment: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_fragment; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + default: { + goto s_n_llhttp__internal__n_error_79; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_end_stub_query_3: + s_n_llhttp__internal__n_span_end_stub_query_3: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_query_3; + } + p++; + goto s_n_llhttp__internal__n_url_fragment; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_query: + s_n_llhttp__internal__n_url_query: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_query; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 6: { + goto s_n_llhttp__internal__n_span_end_stub_query_3; + } + default: { + goto s_n_llhttp__internal__n_error_80; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_query_or_fragment: + s_n_llhttp__internal__n_url_query_or_fragment: { + if (p == endp) { + return s_n_llhttp__internal__n_url_query_or_fragment; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4; + } + case ' ': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5; + } + case '#': { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + case '?': { + p++; + goto s_n_llhttp__internal__n_url_query; + } + default: { + goto s_n_llhttp__internal__n_error_81; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_path: + s_n_llhttp__internal__n_url_path: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_path; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_url_path; + } + default: { + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path_2: + s_n_llhttp__internal__n_span_start_stub_path_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_2; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path: + s_n_llhttp__internal__n_span_start_stub_path: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path_1: + s_n_llhttp__internal__n_span_start_stub_path_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_1; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_server_with_at: + s_n_llhttp__internal__n_url_server_with_at: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, + 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server_with_at; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 6: { + goto s_n_llhttp__internal__n_span_start_stub_path_1; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 8: { + p++; + goto s_n_llhttp__internal__n_error_82; + } + default: { + goto s_n_llhttp__internal__n_error_83; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_server: + s_n_llhttp__internal__n_url_server: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, + 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 6: { + goto s_n_llhttp__internal__n_span_start_stub_path; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 8: { + p++; + goto s_n_llhttp__internal__n_url_server_with_at; + } + default: { + goto s_n_llhttp__internal__n_error_84; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema_delim_1: + s_n_llhttp__internal__n_url_schema_delim_1: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim_1; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_url_server; + } + default: { + goto s_n_llhttp__internal__n_error_85; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema_delim: + s_n_llhttp__internal__n_url_schema_delim: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case '/': { + p++; + goto s_n_llhttp__internal__n_url_schema_delim_1; + } + default: { + goto s_n_llhttp__internal__n_error_85; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_end_stub_schema: + s_n_llhttp__internal__n_span_end_stub_schema: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_schema; + } + p++; + goto s_n_llhttp__internal__n_url_schema_delim; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema: + s_n_llhttp__internal__n_url_schema: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_schema; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_stub_schema; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_86; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_start: + s_n_llhttp__internal__n_url_start: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_start; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_start_stub_path_2; + } + case 3: { + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_87; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url_1: + s_n_llhttp__internal__n_span_start_llhttp__on_url_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_start; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_entry_normal: + s_n_llhttp__internal__n_url_entry_normal: { + if (p == endp) { + return s_n_llhttp__internal__n_url_entry_normal; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url: + s_n_llhttp__internal__n_span_start_llhttp__on_url: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_server; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_entry_connect: + s_n_llhttp__internal__n_url_entry_connect: { + if (p == endp) { + return s_n_llhttp__internal__n_url_entry_connect; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_spaces_before_url: + s_n_llhttp__internal__n_req_spaces_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_spaces_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_invoke_is_equal_method; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_first_space_before_url: + s_n_llhttp__internal__n_req_first_space_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_first_space_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_error_88; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: { + switch (llhttp__on_method_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_first_space_before_url; + case 21: + goto s_n_llhttp__internal__n_pause_26; + default: + goto s_n_llhttp__internal__n_error_107; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_2: + s_n_llhttp__internal__n_after_start_req_2: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_2; + } + switch (*p) { + case 'L': { + p++; + match = 19; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_3: + s_n_llhttp__internal__n_after_start_req_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 36; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_1: + s_n_llhttp__internal__n_after_start_req_1: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_1; + } + switch (*p) { + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_2; + } + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_3; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_4: + s_n_llhttp__internal__n_after_start_req_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_4; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 16; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_6: + s_n_llhttp__internal__n_after_start_req_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_6; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 22; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_8: + s_n_llhttp__internal__n_after_start_req_8: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_8; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_8; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_9: + s_n_llhttp__internal__n_after_start_req_9: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_9; + } + switch (*p) { + case 'Y': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_7: + s_n_llhttp__internal__n_after_start_req_7: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_7; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_8; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_9; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_5: + s_n_llhttp__internal__n_after_start_req_5: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_5; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_after_start_req_6; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_7; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_12: + s_n_llhttp__internal__n_after_start_req_12: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_12; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_12; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_13: + s_n_llhttp__internal__n_after_start_req_13: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_13; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 35; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_13; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_11: + s_n_llhttp__internal__n_after_start_req_11: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_11; + } + switch (*p) { + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_12; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_13; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_10: + s_n_llhttp__internal__n_after_start_req_10: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_10; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_11; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_14: + s_n_llhttp__internal__n_after_start_req_14: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_14; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 45; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_14; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_17: + s_n_llhttp__internal__n_after_start_req_17: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_17; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 41; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_17; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_16: + s_n_llhttp__internal__n_after_start_req_16: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_16; + } + switch (*p) { + case '_': { + p++; + goto s_n_llhttp__internal__n_after_start_req_17; + } + default: { + match = 1; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_15: + s_n_llhttp__internal__n_after_start_req_15: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_15; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_after_start_req_16; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_15; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_18: + s_n_llhttp__internal__n_after_start_req_18: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_18; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_18; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_20: + s_n_llhttp__internal__n_after_start_req_20: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_20; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 31; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_20; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_21: + s_n_llhttp__internal__n_after_start_req_21: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_21; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_21; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_19: + s_n_llhttp__internal__n_after_start_req_19: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_19; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_after_start_req_20; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_21; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_23: + s_n_llhttp__internal__n_after_start_req_23: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_23; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 24; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_23; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_24: + s_n_llhttp__internal__n_after_start_req_24: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_24; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 23; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_24; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_26: + s_n_llhttp__internal__n_after_start_req_26: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_26; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 21; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_26; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_28: + s_n_llhttp__internal__n_after_start_req_28: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_28; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 30; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_28; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_29: + s_n_llhttp__internal__n_after_start_req_29: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_29; + } + switch (*p) { + case 'L': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_27: + s_n_llhttp__internal__n_after_start_req_27: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_27; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_28; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_29; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_25: + s_n_llhttp__internal__n_after_start_req_25: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_25; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_26; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_27; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_30: + s_n_llhttp__internal__n_after_start_req_30: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_30; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_30; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_22: + s_n_llhttp__internal__n_after_start_req_22: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_22; + } + switch (*p) { + case '-': { + p++; + goto s_n_llhttp__internal__n_after_start_req_23; + } + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_24; + } + case 'K': { + p++; + goto s_n_llhttp__internal__n_after_start_req_25; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_30; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_31: + s_n_llhttp__internal__n_after_start_req_31: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_31; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 25; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_31; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_32: + s_n_llhttp__internal__n_after_start_req_32: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_32; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_32; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_35: + s_n_llhttp__internal__n_after_start_req_35: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_35; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 28; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_35; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_36: + s_n_llhttp__internal__n_after_start_req_36: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_36; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 39; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_36; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_34: + s_n_llhttp__internal__n_after_start_req_34: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_34; + } + switch (*p) { + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_35; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_36; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_37: + s_n_llhttp__internal__n_after_start_req_37: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_37; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 38; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_37; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_38: + s_n_llhttp__internal__n_after_start_req_38: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_38; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_38; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_42: + s_n_llhttp__internal__n_after_start_req_42: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_42; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_42; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_43: + s_n_llhttp__internal__n_after_start_req_43: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_43; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_43; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_41: + s_n_llhttp__internal__n_after_start_req_41: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_41; + } + switch (*p) { + case 'F': { + p++; + goto s_n_llhttp__internal__n_after_start_req_42; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_43; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_40: + s_n_llhttp__internal__n_after_start_req_40: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_40; + } + switch (*p) { + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_41; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_39: + s_n_llhttp__internal__n_after_start_req_39: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_39; + } + switch (*p) { + case 'I': { + p++; + match = 34; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_40; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_45: + s_n_llhttp__internal__n_after_start_req_45: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_45; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 29; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_45; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_44: + s_n_llhttp__internal__n_after_start_req_44: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_44; + } + switch (*p) { + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_45; + } + case 'T': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_33: + s_n_llhttp__internal__n_after_start_req_33: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_33; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_34; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_37; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_38; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_39; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_44; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_46: + s_n_llhttp__internal__n_after_start_req_46: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_46; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 46; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_46; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_49: + s_n_llhttp__internal__n_after_start_req_49: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_49; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 17; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_49; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_50: + s_n_llhttp__internal__n_after_start_req_50: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_50; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 44; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_50; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_51: + s_n_llhttp__internal__n_after_start_req_51: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_51; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 43; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_51; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_52: + s_n_llhttp__internal__n_after_start_req_52: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_52; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 20; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_52; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_48: + s_n_llhttp__internal__n_after_start_req_48: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_48; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_49; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_50; + } + case 'D': { + p++; + goto s_n_llhttp__internal__n_after_start_req_51; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_52; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_47: + s_n_llhttp__internal__n_after_start_req_47: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_47; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_48; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_55: + s_n_llhttp__internal__n_after_start_req_55: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_55; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_55; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_57: + s_n_llhttp__internal__n_after_start_req_57: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_57; + } + switch (*p) { + case 'P': { + p++; + match = 37; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_58: + s_n_llhttp__internal__n_after_start_req_58: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_58; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 42; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_58; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_56: + s_n_llhttp__internal__n_after_start_req_56: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_56; + } + switch (*p) { + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_57; + } + case '_': { + p++; + goto s_n_llhttp__internal__n_after_start_req_58; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_54: + s_n_llhttp__internal__n_after_start_req_54: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_54; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_55; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_56; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_59: + s_n_llhttp__internal__n_after_start_req_59: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_59; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 33; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_59; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_60: + s_n_llhttp__internal__n_after_start_req_60: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_60; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 26; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_60; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_53: + s_n_llhttp__internal__n_after_start_req_53: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_53; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_54; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_59; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_60; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_62: + s_n_llhttp__internal__n_after_start_req_62: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_62; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 40; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_62; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_63: + s_n_llhttp__internal__n_after_start_req_63: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_63; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_63; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_61: + s_n_llhttp__internal__n_after_start_req_61: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_61; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_62; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_63; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_66: + s_n_llhttp__internal__n_after_start_req_66: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_66; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 18; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_66; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_68: + s_n_llhttp__internal__n_after_start_req_68: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_68; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 32; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_68; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_69: + s_n_llhttp__internal__n_after_start_req_69: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_69; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_69; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_67: + s_n_llhttp__internal__n_after_start_req_67: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_67; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_after_start_req_68; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_69; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_70: + s_n_llhttp__internal__n_after_start_req_70: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_70; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 27; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_70; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_65: + s_n_llhttp__internal__n_after_start_req_65: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_65; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_66; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_67; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_70; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_64: + s_n_llhttp__internal__n_after_start_req_64: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_64; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_65; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req: + s_n_llhttp__internal__n_after_start_req: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_1; + } + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_4; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_5; + } + case 'D': { + p++; + goto s_n_llhttp__internal__n_after_start_req_10; + } + case 'F': { + p++; + goto s_n_llhttp__internal__n_after_start_req_14; + } + case 'G': { + p++; + goto s_n_llhttp__internal__n_after_start_req_15; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_after_start_req_18; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_19; + } + case 'M': { + p++; + goto s_n_llhttp__internal__n_after_start_req_22; + } + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_31; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_32; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_33; + } + case 'Q': { + p++; + goto s_n_llhttp__internal__n_after_start_req_46; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_47; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_53; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_61; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_64; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_method_1: + s_n_llhttp__internal__n_span_start_llhttp__on_method_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_method; + goto s_n_llhttp__internal__n_after_start_req; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_line_almost_done: + s_n_llhttp__internal__n_res_line_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_res_line_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_29; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_30: + s_n_llhttp__internal__n_invoke_test_lenient_flags_30: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_94; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status: + s_n_llhttp__internal__n_res_status: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1; + } + default: { + p++; + goto s_n_llhttp__internal__n_res_status; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_status: + s_n_llhttp__internal__n_span_start_llhttp__on_status: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_status; + goto s_n_llhttp__internal__n_res_status; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_otherwise: + s_n_llhttp__internal__n_res_status_code_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_otherwise; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_28; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + default: { + goto s_n_llhttp__internal__n_error_95; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_digit_3: + s_n_llhttp__internal__n_res_status_code_digit_3: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_3; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + default: { + goto s_n_llhttp__internal__n_error_97; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_digit_2: + s_n_llhttp__internal__n_res_status_code_digit_2: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_2; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + default: { + goto s_n_llhttp__internal__n_error_99; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_digit_1: + s_n_llhttp__internal__n_res_status_code_digit_1: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_1; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + default: { + goto s_n_llhttp__internal__n_error_101; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_after_version: + s_n_llhttp__internal__n_res_after_version: { + if (p == endp) { + return s_n_llhttp__internal__n_res_after_version; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_update_status_code; + } + default: { + goto s_n_llhttp__internal__n_error_102; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: { + switch (llhttp__on_version_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_res_after_version; + case 21: + goto s_n_llhttp__internal__n_pause_25; + default: + goto s_n_llhttp__internal__n_error_90; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_89: + s_n_llhttp__internal__n_error_89: { + state->error = 0x9; + state->reason = "Invalid HTTP version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_103: + s_n_llhttp__internal__n_error_103: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_minor: + s_n_llhttp__internal__n_res_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_104: + s_n_llhttp__internal__n_error_104: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_dot: + s_n_llhttp__internal__n_res_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_res_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_8; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_105: + s_n_llhttp__internal__n_error_105: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_major: + s_n_llhttp__internal__n_res_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_9; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_version_1: + s_n_llhttp__internal__n_span_start_llhttp__on_version_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_version; + goto s_n_llhttp__internal__n_res_http_major; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_res: + s_n_llhttp__internal__n_start_res: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_res; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_res; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_109; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: { + switch (llhttp__on_method_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_first_space_before_url; + case 21: + goto s_n_llhttp__internal__n_pause_23; + default: + goto s_n_llhttp__internal__n_error_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_2: + s_n_llhttp__internal__n_req_or_res_method_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_106; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_update_type_1: + s_n_llhttp__internal__n_invoke_update_type_1: { + switch (llhttp__internal__c_update_type_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_3: + s_n_llhttp__internal__n_req_or_res_method_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_106; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_1: + s_n_llhttp__internal__n_req_or_res_method_1: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_1; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_2; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_3; + } + default: { + goto s_n_llhttp__internal__n_error_106; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method: + s_n_llhttp__internal__n_req_or_res_method: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_106; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_method: + s_n_llhttp__internal__n_span_start_llhttp__on_method: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_method; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_method; + goto s_n_llhttp__internal__n_req_or_res_method; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_or_res: + s_n_llhttp__internal__n_start_req_or_res: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_or_res; + } + switch (*p) { + case 'H': { + goto s_n_llhttp__internal__n_span_start_llhttp__on_method; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_type_2; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_load_type: + s_n_llhttp__internal__n_invoke_load_type: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + case 2: + goto s_n_llhttp__internal__n_start_res; + default: + goto s_n_llhttp__internal__n_start_req_or_res; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_update_finish: + s_n_llhttp__internal__n_invoke_update_finish: { + switch (llhttp__internal__c_update_finish(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start: + s_n_llhttp__internal__n_start: { + if (p == endp) { + return s_n_llhttp__internal__n_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_start; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_initial_message_completed; + } + } + /* UNREACHABLE */; + abort(); + } + default: + /* UNREACHABLE */ + abort(); + } + s_n_llhttp__internal__n_error_2: { + state->error = 0x7; + state->reason = "Invalid characters in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_2: { + switch (llhttp__internal__c_update_finish_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_initial_message_completed: { + switch (llhttp__internal__c_update_initial_message_completed(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_finish_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_content_length: { + switch (llhttp__internal__c_update_content_length(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_initial_message_completed; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_8: { + state->error = 0x5; + state->reason = "Data after `Connection: close`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_3: { + switch (llhttp__internal__c_test_lenient_flags_3(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_closed; + default: + goto s_n_llhttp__internal__n_error_8; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_2: { + switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_update_initial_message_completed; + default: + goto s_n_llhttp__internal__n_closed; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_1: { + switch (llhttp__internal__c_update_finish_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_13: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_38: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_15: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_40: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + case 21: + goto s_n_llhttp__internal__n_pause_15; + default: + goto s_n_llhttp__internal__n_error_40; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_2: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_9: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_pause_1; + case 21: + goto s_n_llhttp__internal__n_pause_2; + default: + goto s_n_llhttp__internal__n_error_9; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_36: { + state->error = 0xc; + state->reason = "Chunk size overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_10: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_4: { + switch (llhttp__internal__c_test_lenient_flags_4(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_otherwise; + default: + goto s_n_llhttp__internal__n_error_10; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_3: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length_1; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_14: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_update_content_length_1; + case 21: + goto s_n_llhttp__internal__n_pause_3; + default: + goto s_n_llhttp__internal__n_error_14; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_13: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk data"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_6: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + default: + goto s_n_llhttp__internal__n_error_13; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_15: { + state->error = 0x2; + state->reason = "Expected LF after chunk data"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_7: { + switch (llhttp__internal__c_test_lenient_flags_7(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + default: + goto s_n_llhttp__internal__n_error_15; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_body: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_chunk_data_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags: { + switch (llhttp__internal__c_or_flags(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_4: { + state->error = 0x15; + state->reason = "on_chunk_header pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_12: { + state->error = 0x13; + state->reason = "`on_chunk_header` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: { + switch (llhttp__on_chunk_header(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_content_length; + case 21: + goto s_n_llhttp__internal__n_pause_4; + default: + goto s_n_llhttp__internal__n_error_12; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_16: { + state->error = 0x2; + state->reason = "Expected LF after chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_8: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; + default: + goto s_n_llhttp__internal__n_error_16; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_11: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_5: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_11; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_17: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_18: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_20: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension name"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_5: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_9; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_19: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_6: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_21: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_7: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_22: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_25: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_8: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_10; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_24: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_9: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_26: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_28: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_11: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_28; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_29: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions quote value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_10: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_27: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_30; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_30; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_31; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_31; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_11: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_32: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_33; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_33; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_12: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_value; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_23: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extension_value; + case 21: + goto s_n_llhttp__internal__n_pause_12; + default: + goto s_n_llhttp__internal__n_error_23; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_34; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_34; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_35: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_content_length: { + switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_36; + default: + goto s_n_llhttp__internal__n_chunk_size; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_37: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_body_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_3: { + switch (llhttp__internal__c_update_finish_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_39: { + state->error = 0xf; + state->reason = "Request has invalid `Transfer-Encoding`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_7: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + case 21: + goto s_n_llhttp__internal__n_pause; + default: + goto s_n_llhttp__internal__n_error_7; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_1: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_2: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_upgrade: { + switch (llhttp__internal__c_update_upgrade(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_or_flags_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_14: { + state->error = 0x15; + state->reason = "Paused by on_headers_complete"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_6: { + state->error = 0x11; + state->reason = "User callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: { + switch (llhttp__on_headers_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + case 1: + goto s_n_llhttp__internal__n_invoke_or_flags_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_14; + default: + goto s_n_llhttp__internal__n_error_6; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: { + switch (llhttp__before_headers_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags: { + switch (llhttp__internal__c_test_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_1: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags; + default: + goto s_n_llhttp__internal__n_error_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_17: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_42: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + case 21: + goto s_n_llhttp__internal__n_pause_17; + default: + goto s_n_llhttp__internal__n_error_42; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_3: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_4: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_upgrade_1: { + switch (llhttp__internal__c_update_upgrade(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_or_flags_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_16: { + state->error = 0x15; + state->reason = "Paused by on_headers_complete"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_41: { + state->error = 0x11; + state->reason = "User callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1: { + switch (llhttp__on_headers_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + case 1: + goto s_n_llhttp__internal__n_invoke_or_flags_3; + case 2: + goto s_n_llhttp__internal__n_invoke_update_upgrade_1; + case 21: + goto s_n_llhttp__internal__n_pause_16; + default: + goto s_n_llhttp__internal__n_error_41; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1: { + switch (llhttp__before_headers_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_1: { + switch (llhttp__internal__c_test_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_43: { + state->error = 0x2; + state->reason = "Expected LF after headers"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_12: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags_1; + default: + goto s_n_llhttp__internal__n_error_43; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_44: { + state->error = 0xa; + state->reason = "Invalid header token"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_5; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_5; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_13: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_field_colon_discard_ws; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_60: { + state->error = 0xb; + state->reason = "Content-Length can't be present with Transfer-Encoding"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_47: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_15: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_ws; + default: + goto s_n_llhttp__internal__n_error_47; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_49: { + state->error = 0xb; + state->reason = "Empty Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_18: { + state->error = 0x15; + state->reason = "on_header_value_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_48: { + state->error = 0x1d; + state->reason = "`on_header_value_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_5: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_6: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_7: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_8: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_2: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_5; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_6; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_7; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_8; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_1: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 2: + goto s_n_llhttp__internal__n_error_49; + default: + goto s_n_llhttp__internal__n_invoke_load_header_state_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_46: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_14: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_lws; + default: + goto s_n_llhttp__internal__n_error_46; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_50: { + state->error = 0x2; + state->reason = "Expected LF after CR"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_16: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_lws; + default: + goto s_n_llhttp__internal__n_error_50; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_1: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_4: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 8: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_52: { + state->error = 0xa; + state->reason = "Unexpected whitespace after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_18: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_load_header_state_4; + default: + goto s_n_llhttp__internal__n_error_52; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_2: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_9: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_10: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_11: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_12: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_5: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_9; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_10; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_11; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_12; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_53: { + state->error = 0x3; + state->reason = "Missing expected LF after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_51: { + state->error = 0x19; + state->reason = "Missing expected CR after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_17; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_17; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54; + return s_error; + } + goto s_n_llhttp__internal__n_error_54; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_19: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_lenient; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_4: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_13: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_14: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_15: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_16: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_6: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_13; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_14; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_15; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_16; + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_5: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_token; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_3: { + switch (llhttp__internal__c_update_header_state_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_6: { + switch (llhttp__internal__c_update_header_state_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_7: { + switch (llhttp__internal__c_update_header_state_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56; + return s_error; + } + goto s_n_llhttp__internal__n_error_56; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { + switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6; + default: + goto s_n_llhttp__internal__n_header_value_content_length; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_17: { + switch (llhttp__internal__c_or_flags_17(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_otherwise; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57; + return s_error; + } + goto s_n_llhttp__internal__n_error_57; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_55: { + state->error = 0x4; + state->reason = "Duplicate Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_2: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_header_value_content_length; + default: + goto s_n_llhttp__internal__n_error_55; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_59; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_8: { + switch (llhttp__internal__c_update_header_state_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_otherwise; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_58; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_20: { + switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8; + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_type_1: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_20; + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_9: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_and_flags: { + switch (llhttp__internal__c_and_flags(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_19: { + switch (llhttp__internal__c_or_flags_18(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_and_flags; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_21: { + switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_19; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_type_2: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_21; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_19; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_18: { + switch (llhttp__internal__c_or_flags_18(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_and_flags; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_3: { + switch (llhttp__internal__c_test_flags_3(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_load_type_2; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_18; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_20: { + switch (llhttp__internal__c_or_flags_20(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_9; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_3: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_connection; + case 2: + goto s_n_llhttp__internal__n_invoke_test_flags_2; + case 3: + goto s_n_llhttp__internal__n_invoke_test_flags_3; + case 4: + goto s_n_llhttp__internal__n_invoke_or_flags_20; + default: + goto s_n_llhttp__internal__n_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_22: { + switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_60; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_4: { + switch (llhttp__internal__c_test_flags_4(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_22; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_61: { + state->error = 0xf; + state->reason = "Transfer-Encoding can't be present with Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_23: { + switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_61; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_5: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_23; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_19: { + state->error = 0x15; + state->reason = "on_header_field_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_header_state; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_45: { + state->error = 0x1c; + state->reason = "`on_header_field_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_62: { + state->error = 0xa; + state->reason = "Invalid header token"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_10: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_header_state: { + switch (llhttp__internal__c_store_header_state(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_header_field_colon; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_11: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_4: { + state->error = 0x1e; + state->reason = "Unexpected space after start line"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_field_start; + default: + goto s_n_llhttp__internal__n_error_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_20: { + state->error = 0x15; + state->reason = "on_url_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_3: { + state->error = 0x1a; + state->reason = "`on_url_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: { + switch (llhttp__on_url_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_headers_start; + case 21: + goto s_n_llhttp__internal__n_pause_20; + default: + goto s_n_llhttp__internal__n_error_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_http_minor: { + switch (llhttp__internal__c_update_http_minor(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_http_major: { + switch (llhttp__internal__c_update_http_major(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_http_minor; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_63: { + state->error = 0x7; + state->reason = "Expected CRLF"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_71: { + state->error = 0x17; + state->reason = "Pause on PRI/Upgrade"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_72: { + state->error = 0x9; + state->reason = "Expected HTTP/2 Connection Preface"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_69: { + state->error = 0x2; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_26: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_headers_start; + default: + goto s_n_llhttp__internal__n_error_69; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_68: { + state->error = 0x9; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_25: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_req_http_complete_crlf; + default: + goto s_n_llhttp__internal__n_error_68; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_70: { + state->error = 0x9; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_21: { + state->error = 0x15; + state->reason = "on_version_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_1; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_67: { + state->error = 0x21; + state->reason = "`on_version_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_66; + return s_error; + } + goto s_n_llhttp__internal__n_error_66; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 9: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_1: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_2: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_major: { + switch (llhttp__internal__c_load_http_major(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_http_minor; + case 1: + goto s_n_llhttp__internal__n_invoke_load_http_minor_1; + case 2: + goto s_n_llhttp__internal__n_invoke_load_http_minor_2; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_24: { + switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_invoke_load_http_major; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_minor: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_24; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_73; + return s_error; + } + goto s_n_llhttp__internal__n_error_73; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74; + return s_error; + } + goto s_n_llhttp__internal__n_error_74; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_major: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_http_dot; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75; + return s_error; + } + goto s_n_llhttp__internal__n_error_75; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_65: { + state->error = 0x8; + state->reason = "Invalid method for HTTP/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_method: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 1: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 2: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 4: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 5: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 6: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 7: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 8: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 9: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 10: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 11: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 12: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 13: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 14: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 15: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 16: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 17: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 18: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 19: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 20: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 21: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 22: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 23: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 24: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 25: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 26: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 27: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 28: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 29: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 30: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 31: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 32: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 33: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 34: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 46: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + default: + goto s_n_llhttp__internal__n_error_65; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_78: { + state->error = 0x8; + state->reason = "Expected HTTP/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_76: { + state->error = 0x8; + state->reason = "Expected SOURCE method for ICE/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_method_2: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 33: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + default: + goto s_n_llhttp__internal__n_error_76; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_77: { + state->error = 0x8; + state->reason = "Invalid method for RTSP/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_method_3: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 6: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 35: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 36: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 37: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 38: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 39: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 40: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 41: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 42: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 43: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 44: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 45: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + default: + goto s_n_llhttp__internal__n_error_77; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_22: { + state->error = 0x15; + state->reason = "on_url_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_http_start; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_64: { + state->error = 0x1a; + state->reason = "`on_url_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: { + switch (llhttp__on_url_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_http_start; + case 21: + goto s_n_llhttp__internal__n_pause_22; + default: + goto s_n_llhttp__internal__n_error_64; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_79: { + state->error = 0x7; + state->reason = "Invalid char in url fragment start"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_10: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_11: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_80: { + state->error = 0x7; + state->reason = "Invalid char in url query"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_81: { + state->error = 0x7; + state->reason = "Invalid char in url path"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_12: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_13: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_14: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_82: { + state->error = 0x7; + state->reason = "Double @ in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_83: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_84: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_85: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_86: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_87: { + state->error = 0x7; + state->reason = "Unexpected start char in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_is_equal_method: { + switch (llhttp__internal__c_is_equal_method(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_url_entry_normal; + default: + goto s_n_llhttp__internal__n_url_entry_connect; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_88: { + state->error = 0x6; + state->reason = "Expected space after method"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_26: { + state->error = 0x15; + state->reason = "on_method_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_107: { + state->error = 0x20; + state->reason = "`on_method_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_method_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_method_1: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_method_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_108: { + state->error = 0x6; + state->reason = "Invalid method encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_100: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_98: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_96: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_24: { + state->error = 0x15; + state->reason = "on_status_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_92: { + state->error = 0x1b; + state->reason = "`on_status_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: { + switch (llhttp__on_status_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_headers_start; + case 21: + goto s_n_llhttp__internal__n_pause_24; + default: + goto s_n_llhttp__internal__n_error_92; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_91: { + state->error = 0xd; + state->reason = "Invalid response status"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_28: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_91; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_93: { + state->error = 0x2; + state->reason = "Expected LF after CR"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_29: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_93; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_94: { + state->error = 0x19; + state->reason = "Missing expected CR after response line"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_status: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_30; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_30; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_status_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_95: { + state->error = 0xd; + state->reason = "Invalid response status"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_status_code_2: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_96; + default: + goto s_n_llhttp__internal__n_res_status_code_otherwise; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_97: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_status_code_1: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_98; + default: + goto s_n_llhttp__internal__n_res_status_code_digit_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_99: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_status_code: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_100; + default: + goto s_n_llhttp__internal__n_res_status_code_digit_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_101: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_status_code: { + switch (llhttp__internal__c_update_status_code(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_res_status_code_digit_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_102: { + state->error = 0x9; + state->reason = "Expected space after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_25: { + state->error = 0x15; + state->reason = "on_version_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_version; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_90: { + state->error = 0x21; + state->reason = "`on_version_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_89; + return s_error; + } + goto s_n_llhttp__internal__n_error_89; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_3: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 9: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_4: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_5: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_major_1: { + switch (llhttp__internal__c_load_http_major(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_http_minor_3; + case 1: + goto s_n_llhttp__internal__n_invoke_load_http_minor_4; + case 2: + goto s_n_llhttp__internal__n_invoke_load_http_minor_5; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_27: { + switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_invoke_load_http_major_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_minor_1: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_27; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_103; + return s_error; + } + goto s_n_llhttp__internal__n_error_103; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_104; + return s_error; + } + goto s_n_llhttp__internal__n_error_104; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_major_1: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_res_http_dot; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_105; + return s_error; + } + goto s_n_llhttp__internal__n_error_105; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_109: { + state->error = 0x8; + state->reason = "Expected HTTP/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_23: { + state->error = 0x15; + state->reason = "on_method_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_1: { + state->error = 0x20; + state->reason = "`on_method_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_method: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_method; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_method: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_update_type; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_106: { + state->error = 0x8; + state->reason = "Invalid word encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_method_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_type_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_update_type_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type_2: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_27: { + state->error = 0x15; + state->reason = "on_message_begin pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error: { + state->error = 0x10; + state->reason = "`on_message_begin` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: { + switch (llhttp__on_message_begin(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_type; + case 21: + goto s_n_llhttp__internal__n_pause_27; + default: + goto s_n_llhttp__internal__n_error; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_28: { + state->error = 0x15; + state->reason = "on_reset pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_finish; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_110: { + state->error = 0x1f; + state->reason = "`on_reset` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_reset: { + switch (llhttp__on_reset(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_update_finish; + case 21: + goto s_n_llhttp__internal__n_pause_28; + default: + goto s_n_llhttp__internal__n_error_110; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_initial_message_completed: { + switch (llhttp__internal__c_load_initial_message_completed(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_reset; + default: + goto s_n_llhttp__internal__n_invoke_update_finish; + } + /* UNREACHABLE */; + abort(); + } +} + +int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) { + llparse_state_t next; + + /* check lingering errors */ + if (state->error != 0) { + return state->error; + } + + /* restart spans */ + if (state->_span_pos0 != NULL) { + state->_span_pos0 = (void*) p; + } + + next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); + if (next == s_error) { + return state->error; + } + state->_current = (void*) (intptr_t) next; + + /* execute spans */ + if (state->_span_pos0 != NULL) { + int error; + + error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); + if (error != 0) { + state->error = error; + state->error_pos = endp; + return error; + } + } + + return 0; +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/common/http/llhttp_support.c b/common/http/llhttp_support.c new file mode 100644 index 0000000..1ab91a5 --- /dev/null +++ b/common/http/llhttp_support.c @@ -0,0 +1,170 @@ +#include +#ifndef LLHTTP__TEST +# include "llhttp.h" +#else +# define llhttp_t llparse_t +#endif /* */ + +int llhttp_message_needs_eof(const llhttp_t* parser); +int llhttp_should_keep_alive(const llhttp_t* parser); + +int llhttp__before_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + /* Set this here so that on_headers_complete() callbacks can see it */ + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } + return 0; +} + + +/* Return values: + * 0 - No body, `restart`, message_complete + * 1 - CONNECT request, `restart`, message_complete, and pause + * 2 - chunk_size_start + * 3 - body_identity + * 4 - body_identity_eof + * 5 - invalid transfer-encoding for request + */ +int llhttp__after_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + int hasBody; + + hasBody = parser->flags & F_CHUNKED || parser->content_length > 0; + if ( + (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) || + /* See RFC 2616 section 4.4 - 1xx e.g. Continue */ + (parser->type == HTTP_RESPONSE && parser->status_code == 101) + ) { + /* Exit, the rest of the message is in a different protocol. */ + return 1; + } + + if (parser->type == HTTP_RESPONSE && parser->status_code == 100) { + /* No body, restart as the message is complete */ + return 0; + } + + /* See RFC 2616 section 4.4 */ + if ( + parser->flags & F_SKIPBODY || /* response to a HEAD request */ + ( + parser->type == HTTP_RESPONSE && ( + parser->status_code == 102 || /* Processing */ + parser->status_code == 103 || /* Early Hints */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 /* Not Modified */ + ) + ) + ) { + return 0; + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header, prepare for a chunk */ + return 2; + } else if (parser->flags & F_TRANSFER_ENCODING) { + if (parser->type == HTTP_REQUEST && + (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 && + (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field + * is present in a request and the chunked transfer coding is not + * the final encoding, the message body length cannot be determined + * reliably; the server MUST respond with the 400 (Bad Request) + * status code and then close the connection. + */ + return 5; + } else { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field is present in a response and + * the chunked transfer coding is not the final encoding, the + * message body length is determined by reading the connection until + * it is closed by the server. + */ + return 4; + } + } else { + if (!(parser->flags & F_CONTENT_LENGTH)) { + if (!llhttp_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + return 0; + } else { + /* Read body until EOF */ + return 4; + } + } else if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + return 0; + } else { + /* Content-Length header given and non-zero */ + return 3; + } + } +} + + +int llhttp__after_message_complete(llhttp_t* parser, const char* p, + const char* endp) { + int should_keep_alive; + + should_keep_alive = llhttp_should_keep_alive(parser); + parser->finish = HTTP_FINISH_SAFE; + parser->flags = 0; + + /* NOTE: this is ignored in loose parsing mode */ + return should_keep_alive; +} + + +int llhttp_message_needs_eof(const llhttp_t* parser) { + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */ + return 0; + } + + /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */ + if ((parser->flags & F_TRANSFER_ENCODING) && + (parser->flags & F_CHUNKED) == 0) { + return 1; + } + + if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { + return 0; + } + + return 1; +} + + +int llhttp_should_keep_alive(const llhttp_t* parser) { + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !llhttp_message_needs_eof(parser); +} diff --git a/common/http/llhttp_url.c b/common/http/llhttp_url.c new file mode 100644 index 0000000..c0ac411 --- /dev/null +++ b/common/http/llhttp_url.c @@ -0,0 +1,640 @@ +#include +#include +#include "llhttp_url.h" + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wconversion" + #pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +#ifndef BIT_AT +# define BIT_AT(a, i) \ + (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ + (1 << ((unsigned int) (i) & 7)))) +#endif + +#if LLHTTP_STRICT_MODE +# define T(v) 0 +#else +# define T(v) v +#endif + + +static const uint8_t normal_url_char[32] = { + /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, + /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, + /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, + /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; + +#undef T + +enum state + { s_dead = 1 /* important that this is > 0 */ + + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_http_major + , s_res_http_dot + , s_res_http_minor + , s_res_http_end + , s_res_first_status_code + , s_res_status_code + , s_res_status_start + , s_res_status + , s_res_line_almost_done + + , s_start_req + + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_server_start + , s_req_server + , s_req_server_with_at + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_http_I + , s_req_http_IC + , s_req_http_major + , s_req_http_dot + , s_req_http_minor + , s_req_http_end + , s_req_line_almost_done + + , s_header_field_start + , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws + , s_header_value_start + , s_header_value + , s_header_value_lws + + , s_header_almost_done + + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + + , s_headers_almost_done + , s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done + + , s_body_identity + , s_body_identity_eof + + , s_message_done + }; + +enum http_host_state +{ + s_http_host_dead = 1 + , s_http_userinfo_start + , s_http_userinfo + , s_http_host_start + , s_http_host_v6_start + , s_http_host + , s_http_host_v6 + , s_http_host_v6_end + , s_http_host_v6_zone_start + , s_http_host_v6_zone + , s_http_host_port_start + , s_http_host_port +}; + +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) +#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ + (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ + (c) == ')') +#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ + (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ + (c) == '$' || (c) == ',') + +#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c]) + +#if LLHTTP_STRICT_MODE +#define TOKEN(c) STRICT_TOKEN(c) +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define TOKEN(c) tokens[(unsigned char)c] +#define IS_URL_CHAR(c) \ + (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + +/* Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ +static enum state +parse_url_char(enum state s, const char ch) +{ + if (ch == ' ' || ch == '\r' || ch == '\n') { + return s_dead; + } + +#if LLHTTP_STRICT_MODE + if (ch == '\t' || ch == '\f') { + return s_dead; + } +#endif + + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + + if (ch == '/' || ch == '*') { + return s_req_path; + } + + if (IS_ALPHA(ch)) { + return s_req_schema; + } + + break; + + case s_req_schema: + if (IS_ALPHA(ch)) { + return s; + } + + if (ch == ':') { + return s_req_schema_slash; + } + + break; + + case s_req_schema_slash: + if (ch == '/') { + return s_req_schema_slash_slash; + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') { + return s_req_server_start; + } + + break; + + case s_req_server_with_at: + if (ch == '@') { + return s_dead; + } + + /* fall through */ + case s_req_server_start: + case s_req_server: + if (ch == '/') { + return s_req_path; + } + + if (ch == '?') { + return s_req_query_string_start; + } + + if (ch == '@') { + return s_req_server_with_at; + } + + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return s_req_server; + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + return s_req_query_string_start; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) { + return s_req_query_string; + } + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) { + return s_req_fragment; + } + + switch (ch) { + case '?': + return s_req_fragment; + + case '#': + return s; + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + case '#': + return s; + } + + break; + + default: + break; + } + + /* We should never fall out of the switch above unless there's an error */ + return s_dead; +} + +static enum http_host_state +http_parse_host_char(enum http_host_state s, const char ch) { + switch(s) { + case s_http_userinfo: + case s_http_userinfo_start: + if (ch == '@') { + return s_http_host_start; + } + + if (IS_USERINFO_CHAR(ch)) { + return s_http_userinfo; + } + break; + + case s_http_host_start: + if (ch == '[') { + return s_http_host_v6_start; + } + + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + break; + + case s_http_host: + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + /* fall through */ + case s_http_host_v6_end: + if (ch == ':') { + return s_http_host_port_start; + } + + break; + + case s_http_host_v6: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* fall through */ + case s_http_host_v6_start: + if (IS_HEX(ch) || ch == ':' || ch == '.') { + return s_http_host_v6; + } + + if (s == s_http_host_v6 && ch == '%') { + return s_http_host_v6_zone_start; + } + break; + + case s_http_host_v6_zone: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* fall through */ + case s_http_host_v6_zone_start: + /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ + if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || + ch == '~') { + return s_http_host_v6_zone; + } + break; + + case s_http_host_port: + case s_http_host_port_start: + if (IS_NUM(ch)) { + return s_http_host_port; + } + + break; + + default: + break; + } + return s_http_host_dead; +} + +static int +http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { + enum http_host_state s; + + const char *p; + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + + assert(u->field_set & (1 << UF_HOST)); + + u->field_data[UF_HOST].len = 0; + + s = found_at ? s_http_userinfo_start : s_http_host_start; + + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { + enum http_host_state new_s = http_parse_host_char(s, *p); + + if (new_s == s_http_host_dead) { + return 1; + } + + switch(new_s) { + case s_http_host: + if (s != s_http_host) { + u->field_data[UF_HOST].off = (uint16_t)(p - buf); + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6: + if (s != s_http_host_v6) { + u->field_data[UF_HOST].off = (uint16_t)(p - buf); + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + u->field_data[UF_HOST].len++; + break; + + case s_http_host_port: + if (s != s_http_host_port) { + u->field_data[UF_PORT].off = (uint16_t)(p - buf); + u->field_data[UF_PORT].len = 0; + u->field_set |= (1 << UF_PORT); + } + u->field_data[UF_PORT].len++; + break; + + case s_http_userinfo: + if (s != s_http_userinfo) { + u->field_data[UF_USERINFO].off = (uint16_t)(p - buf); + u->field_data[UF_USERINFO].len = 0; + u->field_set |= (1 << UF_USERINFO); + } + u->field_data[UF_USERINFO].len++; + break; + + default: + break; + } + s = new_s; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) { + case s_http_host_start: + case s_http_host_v6_start: + case s_http_host_v6: + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + case s_http_host_port_start: + case s_http_userinfo: + case s_http_userinfo_start: + return 1; + default: + break; + } + + return 0; +} + +void +http_parser_url_init(struct http_parser_url *u) { + memset(u, 0, sizeof(*u)); +} + +int +http_parser_parse_url(const char *buf, size_t buflen, int is_connect, +struct http_parser_url *u) +{ + enum state s; + const char *p; + enum http_parser_url_fields uf, old_uf; + int found_at = 0; + + if (buflen == 0) { + return 1; + } + + u->port = u->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { +case s_dead: + return 1; + + /* Skip delimeters */ +case s_req_schema_slash: +case s_req_schema_slash_slash: +case s_req_server_start: +case s_req_query_string_start: +case s_req_fragment_start: + continue; + +case s_req_schema: + uf = UF_SCHEMA; + break; + +case s_req_server_with_at: + found_at = 1; + + /* fall through */ +case s_req_server: + uf = UF_HOST; + break; + +case s_req_path: + uf = UF_PATH; + break; + +case s_req_query_string: + uf = UF_QUERY; + break; + +case s_req_fragment: + uf = UF_FRAGMENT; + break; + +default: + assert(!"Unexpected state"); + return 1; + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + u->field_data[uf].len++; + continue; + } + + u->field_data[uf].off = (uint16_t)(p - buf); + u->field_data[uf].len = 1; + + u->field_set |= (1 << uf); + old_uf = uf; + } + + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((u->field_set & (1 << UF_SCHEMA)) && + (u->field_set & (1 << UF_HOST)) == 0) { + return 1; + } + + if (u->field_set & (1 << UF_HOST)) { + if (http_parse_host(buf, u, found_at) != 0) { + return 1; + } + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { + return 1; + } + + if (u->field_set & (1 << UF_PORT)) { + uint16_t off; + uint16_t len; + const char* p; + const char* end; + unsigned long v; + + off = u->field_data[UF_PORT].off; + len = u->field_data[UF_PORT].len; + end = buf + off + len; + + /* NOTE: The characters are already validated and are in the [0-9] range */ + assert((size_t)(off + len) <= buflen && "Port number overflow"); + v = 0; + for (p = buf + off; p < end; p++) { + v *= 10; + v += *p - '0'; + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } + } + + u->port = (uint16_t) v; + } + + return 0; +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/common/http/llhttp_url.h b/common/http/llhttp_url.h new file mode 100644 index 0000000..146633f --- /dev/null +++ b/common/http/llhttp_url.h @@ -0,0 +1,60 @@ +#ifndef INCLUDE_LLHTTP_URL_H_ +#define INCLUDE_LLHTTP_URL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// copy code from http_parser + +/* Compile with -DLLHTTP_STRICT_MODE=0 to make less checks, but run + * faster + */ +#ifndef LLHTTP_STRICT_MODE +# define LLHTTP_STRICT_MODE 1 +#endif + +enum http_parser_url_fields + { UF_SCHEMA = 0 + , UF_HOST = 1 + , UF_PORT = 2 + , UF_PATH = 3 + , UF_QUERY = 4 + , UF_FRAGMENT = 5 + , UF_USERINFO = 6 + , UF_MAX = 7 + }; + + +/* Result structure for http_parser_parse_url(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +struct http_parser_url { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; +}; + +/* Initialize all http_parser_url members to 0 */ +void http_parser_url_init(struct http_parser_url *u); + +/* Parse a URL; return nonzero on failure */ +int http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, struct http_parser_url *u); + +#ifdef __cplusplus +} +#endif + +#endif // INCLUDE_LLHTTP_URL_H_ \ No newline at end of file diff --git a/common/kcp/Readme.txt b/common/kcp/Readme.txt new file mode 100644 index 0000000..01d8c7c --- /dev/null +++ b/common/kcp/Readme.txt @@ -0,0 +1,4 @@ +Modifications +-------------------- +1. kcp.c ignore warning: "-Wconversion", "-Wsign-conversion" +2. kcp.c ikcp_input(): "if (conv != kcp->conv) return -1;" -> "conv = kcp->conv;" \ No newline at end of file diff --git a/common/kcp/ikcp.c b/common/kcp/ikcp.c new file mode 100644 index 0000000..7d54f0e --- /dev/null +++ b/common/kcp/ikcp.c @@ -0,0 +1,1307 @@ +//===================================================================== +// +// KCP - A Better ARQ Protocol Implementation +// skywind3000 (at) gmail.com, 2010-2011 +// +// Features: +// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. +// + Maximum RTT reduce three times vs tcp. +// + Lightweight, distributed as a single source file. +// +//===================================================================== +#include "ikcp.h" + +#include +#include +#include +#include +#include + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wconversion" + #pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +//===================================================================== +// KCP BASIC +//===================================================================== +const IUINT32 IKCP_RTO_NDL = 30; // no delay min rto +const IUINT32 IKCP_RTO_MIN = 100; // normal min rto +const IUINT32 IKCP_RTO_DEF = 200; +const IUINT32 IKCP_RTO_MAX = 60000; +const IUINT32 IKCP_CMD_PUSH = 81; // cmd: push data +const IUINT32 IKCP_CMD_ACK = 82; // cmd: ack +const IUINT32 IKCP_CMD_WASK = 83; // cmd: window probe (ask) +const IUINT32 IKCP_CMD_WINS = 84; // cmd: window size (tell) +const IUINT32 IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK +const IUINT32 IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS +const IUINT32 IKCP_WND_SND = 32; +const IUINT32 IKCP_WND_RCV = 128; // must >= max fragment size +const IUINT32 IKCP_MTU_DEF = 1400; +const IUINT32 IKCP_ACK_FAST = 3; +const IUINT32 IKCP_INTERVAL = 100; +const IUINT32 IKCP_OVERHEAD = 24; +const IUINT32 IKCP_DEADLINK = 20; +const IUINT32 IKCP_THRESH_INIT = 2; +const IUINT32 IKCP_THRESH_MIN = 2; +const IUINT32 IKCP_PROBE_INIT = 7000; // 7 secs to probe window size +const IUINT32 IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window +const IUINT32 IKCP_FASTACK_LIMIT = 5; // max times to trigger fastack + + +//--------------------------------------------------------------------- +// encode / decode +//--------------------------------------------------------------------- + +/* encode 8 bits unsigned int */ +static inline char *ikcp_encode8u(char *p, unsigned char c) +{ + *(unsigned char*)p++ = c; + return p; +} + +/* decode 8 bits unsigned int */ +static inline const char *ikcp_decode8u(const char *p, unsigned char *c) +{ + *c = *(unsigned char*)p++; + return p; +} + +/* encode 16 bits unsigned int (lsb) */ +static inline char *ikcp_encode16u(char *p, unsigned short w) +{ +#if IWORDS_BIG_ENDIAN || IWORDS_MUST_ALIGN + *(unsigned char*)(p + 0) = (w & 255); + *(unsigned char*)(p + 1) = (w >> 8); +#else + *(unsigned short*)(p) = w; +#endif + p += 2; + return p; +} + +/* decode 16 bits unsigned int (lsb) */ +static inline const char *ikcp_decode16u(const char *p, unsigned short *w) +{ +#if IWORDS_BIG_ENDIAN || IWORDS_MUST_ALIGN + *w = *(const unsigned char*)(p + 1); + *w = *(const unsigned char*)(p + 0) + (*w << 8); +#else + *w = *(const unsigned short*)p; +#endif + p += 2; + return p; +} + +/* encode 32 bits unsigned int (lsb) */ +static inline char *ikcp_encode32u(char *p, IUINT32 l) +{ +#if IWORDS_BIG_ENDIAN || IWORDS_MUST_ALIGN + *(unsigned char*)(p + 0) = (unsigned char)((l >> 0) & 0xff); + *(unsigned char*)(p + 1) = (unsigned char)((l >> 8) & 0xff); + *(unsigned char*)(p + 2) = (unsigned char)((l >> 16) & 0xff); + *(unsigned char*)(p + 3) = (unsigned char)((l >> 24) & 0xff); +#else + *(IUINT32*)p = l; +#endif + p += 4; + return p; +} + +/* decode 32 bits unsigned int (lsb) */ +static inline const char *ikcp_decode32u(const char *p, IUINT32 *l) +{ +#if IWORDS_BIG_ENDIAN || IWORDS_MUST_ALIGN + *l = *(const unsigned char*)(p + 3); + *l = *(const unsigned char*)(p + 2) + (*l << 8); + *l = *(const unsigned char*)(p + 1) + (*l << 8); + *l = *(const unsigned char*)(p + 0) + (*l << 8); +#else + *l = *(const IUINT32*)p; +#endif + p += 4; + return p; +} + +static inline IUINT32 _imin_(IUINT32 a, IUINT32 b) { + return a <= b ? a : b; +} + +static inline IUINT32 _imax_(IUINT32 a, IUINT32 b) { + return a >= b ? a : b; +} + +static inline IUINT32 _ibound_(IUINT32 lower, IUINT32 middle, IUINT32 upper) +{ + return _imin_(_imax_(lower, middle), upper); +} + +static inline long _itimediff(IUINT32 later, IUINT32 earlier) +{ + return ((IINT32)(later - earlier)); +} + +//--------------------------------------------------------------------- +// manage segment +//--------------------------------------------------------------------- +typedef struct IKCPSEG IKCPSEG; + +static void* (*ikcp_malloc_hook)(size_t) = NULL; +static void (*ikcp_free_hook)(void *) = NULL; + +// internal malloc +static void* ikcp_malloc(size_t size) { + if (ikcp_malloc_hook) + return ikcp_malloc_hook(size); + return malloc(size); +} + +// internal free +static void ikcp_free(void *ptr) { + if (ikcp_free_hook) { + ikcp_free_hook(ptr); + } else { + free(ptr); + } +} + +// redefine allocator +void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)) +{ + ikcp_malloc_hook = new_malloc; + ikcp_free_hook = new_free; +} + +// allocate a new kcp segment +static IKCPSEG* ikcp_segment_new(ikcpcb *kcp, int size) +{ + return (IKCPSEG*)ikcp_malloc(sizeof(IKCPSEG) + size); +} + +// delete a segment +static void ikcp_segment_delete(ikcpcb *kcp, IKCPSEG *seg) +{ + ikcp_free(seg); +} + +// write log +void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...) +{ + char buffer[1024]; + va_list argptr; + if ((mask & kcp->logmask) == 0 || kcp->writelog == 0) return; + va_start(argptr, fmt); + vsprintf(buffer, fmt, argptr); + va_end(argptr); + kcp->writelog(buffer, kcp, kcp->user); +} + +// check log mask +static int ikcp_canlog(const ikcpcb *kcp, int mask) +{ + if ((mask & kcp->logmask) == 0 || kcp->writelog == NULL) return 0; + return 1; +} + +// output segment +static int ikcp_output(ikcpcb *kcp, const void *data, int size) +{ + assert(kcp); + assert(kcp->output); + if (ikcp_canlog(kcp, IKCP_LOG_OUTPUT)) { + ikcp_log(kcp, IKCP_LOG_OUTPUT, "[RO] %ld bytes", (long)size); + } + if (size == 0) return 0; + return kcp->output((const char*)data, size, kcp, kcp->user); +} + +// output queue +void ikcp_qprint(const char *name, const struct IQUEUEHEAD *head) +{ +#if 0 + const struct IQUEUEHEAD *p; + printf("<%s>: [", name); + for (p = head->next; p != head; p = p->next) { + const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node); + printf("(%lu %d)", (unsigned long)seg->sn, (int)(seg->ts % 10000)); + if (p->next != head) printf(","); + } + printf("]\n"); +#endif +} + + +//--------------------------------------------------------------------- +// create a new kcpcb +//--------------------------------------------------------------------- +ikcpcb* ikcp_create(IUINT32 conv, void *user) +{ + ikcpcb *kcp = (ikcpcb*)ikcp_malloc(sizeof(struct IKCPCB)); + if (kcp == NULL) return NULL; + kcp->conv = conv; + kcp->user = user; + kcp->snd_una = 0; + kcp->snd_nxt = 0; + kcp->rcv_nxt = 0; + kcp->ts_recent = 0; + kcp->ts_lastack = 0; + kcp->ts_probe = 0; + kcp->probe_wait = 0; + kcp->snd_wnd = IKCP_WND_SND; + kcp->rcv_wnd = IKCP_WND_RCV; + kcp->rmt_wnd = IKCP_WND_RCV; + kcp->cwnd = 0; + kcp->incr = 0; + kcp->probe = 0; + kcp->mtu = IKCP_MTU_DEF; + kcp->mss = kcp->mtu - IKCP_OVERHEAD; + kcp->stream = 0; + + kcp->buffer = (char*)ikcp_malloc((kcp->mtu + IKCP_OVERHEAD) * 3); + if (kcp->buffer == NULL) { + ikcp_free(kcp); + return NULL; + } + + iqueue_init(&kcp->snd_queue); + iqueue_init(&kcp->rcv_queue); + iqueue_init(&kcp->snd_buf); + iqueue_init(&kcp->rcv_buf); + kcp->nrcv_buf = 0; + kcp->nsnd_buf = 0; + kcp->nrcv_que = 0; + kcp->nsnd_que = 0; + kcp->state = 0; + kcp->acklist = NULL; + kcp->ackblock = 0; + kcp->ackcount = 0; + kcp->rx_srtt = 0; + kcp->rx_rttval = 0; + kcp->rx_rto = IKCP_RTO_DEF; + kcp->rx_minrto = IKCP_RTO_MIN; + kcp->current = 0; + kcp->interval = IKCP_INTERVAL; + kcp->ts_flush = IKCP_INTERVAL; + kcp->nodelay = 0; + kcp->updated = 0; + kcp->logmask = 0; + kcp->ssthresh = IKCP_THRESH_INIT; + kcp->fastresend = 0; + kcp->fastlimit = IKCP_FASTACK_LIMIT; + kcp->nocwnd = 0; + kcp->xmit = 0; + kcp->dead_link = IKCP_DEADLINK; + kcp->output = NULL; + kcp->writelog = NULL; + + return kcp; +} + + +//--------------------------------------------------------------------- +// release a new kcpcb +//--------------------------------------------------------------------- +void ikcp_release(ikcpcb *kcp) +{ + assert(kcp); + if (kcp) { + IKCPSEG *seg; + while (!iqueue_is_empty(&kcp->snd_buf)) { + seg = iqueue_entry(kcp->snd_buf.next, IKCPSEG, node); + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + } + while (!iqueue_is_empty(&kcp->rcv_buf)) { + seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + } + while (!iqueue_is_empty(&kcp->snd_queue)) { + seg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node); + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + } + while (!iqueue_is_empty(&kcp->rcv_queue)) { + seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node); + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + } + if (kcp->buffer) { + ikcp_free(kcp->buffer); + } + if (kcp->acklist) { + ikcp_free(kcp->acklist); + } + + kcp->nrcv_buf = 0; + kcp->nsnd_buf = 0; + kcp->nrcv_que = 0; + kcp->nsnd_que = 0; + kcp->ackcount = 0; + kcp->buffer = NULL; + kcp->acklist = NULL; + ikcp_free(kcp); + } +} + + +//--------------------------------------------------------------------- +// set output callback, which will be invoked by kcp +//--------------------------------------------------------------------- +void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len, + ikcpcb *kcp, void *user)) +{ + kcp->output = output; +} + + +//--------------------------------------------------------------------- +// user/upper level recv: returns size, returns below zero for EAGAIN +//--------------------------------------------------------------------- +int ikcp_recv(ikcpcb *kcp, char *buffer, int len) +{ + struct IQUEUEHEAD *p; + int ispeek = (len < 0)? 1 : 0; + int peeksize; + int recover = 0; + IKCPSEG *seg; + assert(kcp); + + if (iqueue_is_empty(&kcp->rcv_queue)) + return -1; + + if (len < 0) len = -len; + + peeksize = ikcp_peeksize(kcp); + + if (peeksize < 0) + return -2; + + if (peeksize > len) + return -3; + + if (kcp->nrcv_que >= kcp->rcv_wnd) + recover = 1; + + // merge fragment + for (len = 0, p = kcp->rcv_queue.next; p != &kcp->rcv_queue; ) { + int fragment; + seg = iqueue_entry(p, IKCPSEG, node); + p = p->next; + + if (buffer) { + memcpy(buffer, seg->data, seg->len); + buffer += seg->len; + } + + len += seg->len; + fragment = seg->frg; + + if (ikcp_canlog(kcp, IKCP_LOG_RECV)) { + ikcp_log(kcp, IKCP_LOG_RECV, "recv sn=%lu", (unsigned long)seg->sn); + } + + if (ispeek == 0) { + iqueue_del(&seg->node); + ikcp_segment_delete(kcp, seg); + kcp->nrcv_que--; + } + + if (fragment == 0) + break; + } + + assert(len == peeksize); + + // move available data from rcv_buf -> rcv_queue + while (! iqueue_is_empty(&kcp->rcv_buf)) { + seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); + if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) { + iqueue_del(&seg->node); + kcp->nrcv_buf--; + iqueue_add_tail(&seg->node, &kcp->rcv_queue); + kcp->nrcv_que++; + kcp->rcv_nxt++; + } else { + break; + } + } + + // fast recover + if (kcp->nrcv_que < kcp->rcv_wnd && recover) { + // ready to send back IKCP_CMD_WINS in ikcp_flush + // tell remote my window size + kcp->probe |= IKCP_ASK_TELL; + } + + return len; +} + + +//--------------------------------------------------------------------- +// peek data size +//--------------------------------------------------------------------- +int ikcp_peeksize(const ikcpcb *kcp) +{ + struct IQUEUEHEAD *p; + IKCPSEG *seg; + int length = 0; + + assert(kcp); + + if (iqueue_is_empty(&kcp->rcv_queue)) return -1; + + seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node); + if (seg->frg == 0) return seg->len; + + if (kcp->nrcv_que < seg->frg + 1) return -1; + + for (p = kcp->rcv_queue.next; p != &kcp->rcv_queue; p = p->next) { + seg = iqueue_entry(p, IKCPSEG, node); + length += seg->len; + if (seg->frg == 0) break; + } + + return length; +} + + +//--------------------------------------------------------------------- +// user/upper level send, returns below zero for error +//--------------------------------------------------------------------- +int ikcp_send(ikcpcb *kcp, const char *buffer, int len) +{ + IKCPSEG *seg; + int count, i; + + assert(kcp->mss > 0); + if (len < 0) return -1; + + // append to previous segment in streaming mode (if possible) + if (kcp->stream != 0) { + if (!iqueue_is_empty(&kcp->snd_queue)) { + IKCPSEG *old = iqueue_entry(kcp->snd_queue.prev, IKCPSEG, node); + if (old->len < kcp->mss) { + int capacity = kcp->mss - old->len; + int extend = (len < capacity)? len : capacity; + seg = ikcp_segment_new(kcp, old->len + extend); + assert(seg); + if (seg == NULL) { + return -2; + } + iqueue_add_tail(&seg->node, &kcp->snd_queue); + memcpy(seg->data, old->data, old->len); + if (buffer) { + memcpy(seg->data + old->len, buffer, extend); + buffer += extend; + } + seg->len = old->len + extend; + seg->frg = 0; + len -= extend; + iqueue_del_init(&old->node); + ikcp_segment_delete(kcp, old); + } + } + if (len <= 0) { + return 0; + } + } + + if (len <= (int)kcp->mss) count = 1; + else count = (len + kcp->mss - 1) / kcp->mss; + + if (count >= (int)IKCP_WND_RCV) return -2; + + if (count == 0) count = 1; + + // fragment + for (i = 0; i < count; i++) { + int size = len > (int)kcp->mss ? (int)kcp->mss : len; + seg = ikcp_segment_new(kcp, size); + assert(seg); + if (seg == NULL) { + return -2; + } + if (buffer && len > 0) { + memcpy(seg->data, buffer, size); + } + seg->len = size; + seg->frg = (kcp->stream == 0)? (count - i - 1) : 0; + iqueue_init(&seg->node); + iqueue_add_tail(&seg->node, &kcp->snd_queue); + kcp->nsnd_que++; + if (buffer) { + buffer += size; + } + len -= size; + } + + return 0; +} + + +//--------------------------------------------------------------------- +// parse ack +//--------------------------------------------------------------------- +static void ikcp_update_ack(ikcpcb *kcp, IINT32 rtt) +{ + IINT32 rto = 0; + if (kcp->rx_srtt == 0) { + kcp->rx_srtt = rtt; + kcp->rx_rttval = rtt / 2; + } else { + long delta = rtt - kcp->rx_srtt; + if (delta < 0) delta = -delta; + kcp->rx_rttval = (3 * kcp->rx_rttval + delta) / 4; + kcp->rx_srtt = (7 * kcp->rx_srtt + rtt) / 8; + if (kcp->rx_srtt < 1) kcp->rx_srtt = 1; + } + rto = kcp->rx_srtt + _imax_(kcp->interval, 4 * kcp->rx_rttval); + kcp->rx_rto = _ibound_(kcp->rx_minrto, rto, IKCP_RTO_MAX); +} + +static void ikcp_shrink_buf(ikcpcb *kcp) +{ + struct IQUEUEHEAD *p = kcp->snd_buf.next; + if (p != &kcp->snd_buf) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + kcp->snd_una = seg->sn; + } else { + kcp->snd_una = kcp->snd_nxt; + } +} + +static void ikcp_parse_ack(ikcpcb *kcp, IUINT32 sn) +{ + struct IQUEUEHEAD *p, *next; + + if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0) + return; + + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + next = p->next; + if (sn == seg->sn) { + iqueue_del(p); + ikcp_segment_delete(kcp, seg); + kcp->nsnd_buf--; + break; + } + if (_itimediff(sn, seg->sn) < 0) { + break; + } + } +} + +static void ikcp_parse_una(ikcpcb *kcp, IUINT32 una) +{ + struct IQUEUEHEAD *p, *next; + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + next = p->next; + if (_itimediff(una, seg->sn) > 0) { + iqueue_del(p); + ikcp_segment_delete(kcp, seg); + kcp->nsnd_buf--; + } else { + break; + } + } +} + +static void ikcp_parse_fastack(ikcpcb *kcp, IUINT32 sn, IUINT32 ts) +{ + struct IQUEUEHEAD *p, *next; + + if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0) + return; + + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + next = p->next; + if (_itimediff(sn, seg->sn) < 0) { + break; + } + else if (sn != seg->sn) { + #ifndef IKCP_FASTACK_CONSERVE + seg->fastack++; + #else + if (_itimediff(ts, seg->ts) >= 0) + seg->fastack++; + #endif + } + } +} + + +//--------------------------------------------------------------------- +// ack append +//--------------------------------------------------------------------- +static void ikcp_ack_push(ikcpcb *kcp, IUINT32 sn, IUINT32 ts) +{ + IUINT32 newsize = kcp->ackcount + 1; + IUINT32 *ptr; + + if (newsize > kcp->ackblock) { + IUINT32 *acklist; + IUINT32 newblock; + + for (newblock = 8; newblock < newsize; newblock <<= 1); + acklist = (IUINT32*)ikcp_malloc(newblock * sizeof(IUINT32) * 2); + + if (acklist == NULL) { + assert(acklist != NULL); + abort(); + } + + if (kcp->acklist != NULL) { + IUINT32 x; + for (x = 0; x < kcp->ackcount; x++) { + acklist[x * 2 + 0] = kcp->acklist[x * 2 + 0]; + acklist[x * 2 + 1] = kcp->acklist[x * 2 + 1]; + } + ikcp_free(kcp->acklist); + } + + kcp->acklist = acklist; + kcp->ackblock = newblock; + } + + ptr = &kcp->acklist[kcp->ackcount * 2]; + ptr[0] = sn; + ptr[1] = ts; + kcp->ackcount++; +} + +static void ikcp_ack_get(const ikcpcb *kcp, int p, IUINT32 *sn, IUINT32 *ts) +{ + if (sn) sn[0] = kcp->acklist[p * 2 + 0]; + if (ts) ts[0] = kcp->acklist[p * 2 + 1]; +} + + +//--------------------------------------------------------------------- +// parse data +//--------------------------------------------------------------------- +void ikcp_parse_data(ikcpcb *kcp, IKCPSEG *newseg) +{ + struct IQUEUEHEAD *p, *prev; + IUINT32 sn = newseg->sn; + int repeat = 0; + + if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) >= 0 || + _itimediff(sn, kcp->rcv_nxt) < 0) { + ikcp_segment_delete(kcp, newseg); + return; + } + + for (p = kcp->rcv_buf.prev; p != &kcp->rcv_buf; p = prev) { + IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node); + prev = p->prev; + if (seg->sn == sn) { + repeat = 1; + break; + } + if (_itimediff(sn, seg->sn) > 0) { + break; + } + } + + if (repeat == 0) { + iqueue_init(&newseg->node); + iqueue_add(&newseg->node, p); + kcp->nrcv_buf++; + } else { + ikcp_segment_delete(kcp, newseg); + } + +#if 0 + ikcp_qprint("rcvbuf", &kcp->rcv_buf); + printf("rcv_nxt=%lu\n", kcp->rcv_nxt); +#endif + + // move available data from rcv_buf -> rcv_queue + while (! iqueue_is_empty(&kcp->rcv_buf)) { + IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node); + if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) { + iqueue_del(&seg->node); + kcp->nrcv_buf--; + iqueue_add_tail(&seg->node, &kcp->rcv_queue); + kcp->nrcv_que++; + kcp->rcv_nxt++; + } else { + break; + } + } + +#if 0 + ikcp_qprint("queue", &kcp->rcv_queue); + printf("rcv_nxt=%lu\n", kcp->rcv_nxt); +#endif + +#if 1 +// printf("snd(buf=%d, queue=%d)\n", kcp->nsnd_buf, kcp->nsnd_que); +// printf("rcv(buf=%d, queue=%d)\n", kcp->nrcv_buf, kcp->nrcv_que); +#endif +} + + +//--------------------------------------------------------------------- +// input data +//--------------------------------------------------------------------- +int ikcp_input(ikcpcb *kcp, const char *data, long size) +{ + IUINT32 prev_una = kcp->snd_una; + IUINT32 maxack = 0, latest_ts = 0; + int flag = 0; + + if (ikcp_canlog(kcp, IKCP_LOG_INPUT)) { + ikcp_log(kcp, IKCP_LOG_INPUT, "[RI] %d bytes", (int)size); + } + + if (data == NULL || (int)size < (int)IKCP_OVERHEAD) return -1; + + while (1) { + IUINT32 ts, sn, len, una, conv; + IUINT16 wnd; + IUINT8 cmd, frg; + IKCPSEG *seg; + + if (size < (int)IKCP_OVERHEAD) break; + + data = ikcp_decode32u(data, &conv); + // !!! modify !!! + // if (conv != kcp->conv) return -1; + conv = kcp->conv; + + data = ikcp_decode8u(data, &cmd); + data = ikcp_decode8u(data, &frg); + data = ikcp_decode16u(data, &wnd); + data = ikcp_decode32u(data, &ts); + data = ikcp_decode32u(data, &sn); + data = ikcp_decode32u(data, &una); + data = ikcp_decode32u(data, &len); + + size -= IKCP_OVERHEAD; + + if ((long)size < (long)len || (int)len < 0) return -2; + + if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK && + cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS) + return -3; + + kcp->rmt_wnd = wnd; + ikcp_parse_una(kcp, una); + ikcp_shrink_buf(kcp); + + if (cmd == IKCP_CMD_ACK) { + if (_itimediff(kcp->current, ts) >= 0) { + ikcp_update_ack(kcp, _itimediff(kcp->current, ts)); + } + ikcp_parse_ack(kcp, sn); + ikcp_shrink_buf(kcp); + if (flag == 0) { + flag = 1; + maxack = sn; + latest_ts = ts; + } else { + if (_itimediff(sn, maxack) > 0) { + #ifndef IKCP_FASTACK_CONSERVE + maxack = sn; + latest_ts = ts; + #else + if (_itimediff(ts, latest_ts) > 0) { + maxack = sn; + latest_ts = ts; + } + #endif + } + } + if (ikcp_canlog(kcp, IKCP_LOG_IN_ACK)) { + ikcp_log(kcp, IKCP_LOG_IN_ACK, + "input ack: sn=%lu rtt=%ld rto=%ld", (unsigned long)sn, + (long)_itimediff(kcp->current, ts), + (long)kcp->rx_rto); + } + } + else if (cmd == IKCP_CMD_PUSH) { + if (ikcp_canlog(kcp, IKCP_LOG_IN_DATA)) { + ikcp_log(kcp, IKCP_LOG_IN_DATA, + "input psh: sn=%lu ts=%lu", (unsigned long)sn, (unsigned long)ts); + } + if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) < 0) { + ikcp_ack_push(kcp, sn, ts); + if (_itimediff(sn, kcp->rcv_nxt) >= 0) { + seg = ikcp_segment_new(kcp, len); + seg->conv = conv; + seg->cmd = cmd; + seg->frg = frg; + seg->wnd = wnd; + seg->ts = ts; + seg->sn = sn; + seg->una = una; + seg->len = len; + + if (len > 0) { + memcpy(seg->data, data, len); + } + + ikcp_parse_data(kcp, seg); + } + } + } + else if (cmd == IKCP_CMD_WASK) { + // ready to send back IKCP_CMD_WINS in ikcp_flush + // tell remote my window size + kcp->probe |= IKCP_ASK_TELL; + if (ikcp_canlog(kcp, IKCP_LOG_IN_PROBE)) { + ikcp_log(kcp, IKCP_LOG_IN_PROBE, "input probe"); + } + } + else if (cmd == IKCP_CMD_WINS) { + // do nothing + if (ikcp_canlog(kcp, IKCP_LOG_IN_WINS)) { + ikcp_log(kcp, IKCP_LOG_IN_WINS, + "input wins: %lu", (unsigned long)(wnd)); + } + } + else { + return -3; + } + + data += len; + size -= len; + } + + if (flag != 0) { + ikcp_parse_fastack(kcp, maxack, latest_ts); + } + + if (_itimediff(kcp->snd_una, prev_una) > 0) { + if (kcp->cwnd < kcp->rmt_wnd) { + IUINT32 mss = kcp->mss; + if (kcp->cwnd < kcp->ssthresh) { + kcp->cwnd++; + kcp->incr += mss; + } else { + if (kcp->incr < mss) kcp->incr = mss; + kcp->incr += (mss * mss) / kcp->incr + (mss / 16); + if ((kcp->cwnd + 1) * mss <= kcp->incr) { + #if 1 + kcp->cwnd = (kcp->incr + mss - 1) / ((mss > 0)? mss : 1); + #else + kcp->cwnd++; + #endif + } + } + if (kcp->cwnd > kcp->rmt_wnd) { + kcp->cwnd = kcp->rmt_wnd; + kcp->incr = kcp->rmt_wnd * mss; + } + } + } + + return 0; +} + + +//--------------------------------------------------------------------- +// ikcp_encode_seg +//--------------------------------------------------------------------- +static char *ikcp_encode_seg(char *ptr, const IKCPSEG *seg) +{ + ptr = ikcp_encode32u(ptr, seg->conv); + ptr = ikcp_encode8u(ptr, (IUINT8)seg->cmd); + ptr = ikcp_encode8u(ptr, (IUINT8)seg->frg); + ptr = ikcp_encode16u(ptr, (IUINT16)seg->wnd); + ptr = ikcp_encode32u(ptr, seg->ts); + ptr = ikcp_encode32u(ptr, seg->sn); + ptr = ikcp_encode32u(ptr, seg->una); + ptr = ikcp_encode32u(ptr, seg->len); + return ptr; +} + +static int ikcp_wnd_unused(const ikcpcb *kcp) +{ + if (kcp->nrcv_que < kcp->rcv_wnd) { + return kcp->rcv_wnd - kcp->nrcv_que; + } + return 0; +} + + +//--------------------------------------------------------------------- +// ikcp_flush +//--------------------------------------------------------------------- +void ikcp_flush(ikcpcb *kcp) +{ + IUINT32 current = kcp->current; + char *buffer = kcp->buffer; + char *ptr = buffer; + int count, size, i; + IUINT32 resent, cwnd; + IUINT32 rtomin; + struct IQUEUEHEAD *p; + int change = 0; + int lost = 0; + IKCPSEG seg; + + // 'ikcp_update' haven't been called. + if (kcp->updated == 0) return; + + seg.conv = kcp->conv; + seg.cmd = IKCP_CMD_ACK; + seg.frg = 0; + seg.wnd = ikcp_wnd_unused(kcp); + seg.una = kcp->rcv_nxt; + seg.len = 0; + seg.sn = 0; + seg.ts = 0; + + // flush acknowledges + count = kcp->ackcount; + for (i = 0; i < count; i++) { + size = (int)(ptr - buffer); + if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { + ikcp_output(kcp, buffer, size); + ptr = buffer; + } + ikcp_ack_get(kcp, i, &seg.sn, &seg.ts); + ptr = ikcp_encode_seg(ptr, &seg); + } + + kcp->ackcount = 0; + + // probe window size (if remote window size equals zero) + if (kcp->rmt_wnd == 0) { + if (kcp->probe_wait == 0) { + kcp->probe_wait = IKCP_PROBE_INIT; + kcp->ts_probe = kcp->current + kcp->probe_wait; + } + else { + if (_itimediff(kcp->current, kcp->ts_probe) >= 0) { + if (kcp->probe_wait < IKCP_PROBE_INIT) + kcp->probe_wait = IKCP_PROBE_INIT; + kcp->probe_wait += kcp->probe_wait / 2; + if (kcp->probe_wait > IKCP_PROBE_LIMIT) + kcp->probe_wait = IKCP_PROBE_LIMIT; + kcp->ts_probe = kcp->current + kcp->probe_wait; + kcp->probe |= IKCP_ASK_SEND; + } + } + } else { + kcp->ts_probe = 0; + kcp->probe_wait = 0; + } + + // flush window probing commands + if (kcp->probe & IKCP_ASK_SEND) { + seg.cmd = IKCP_CMD_WASK; + size = (int)(ptr - buffer); + if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { + ikcp_output(kcp, buffer, size); + ptr = buffer; + } + ptr = ikcp_encode_seg(ptr, &seg); + } + + // flush window probing commands + if (kcp->probe & IKCP_ASK_TELL) { + seg.cmd = IKCP_CMD_WINS; + size = (int)(ptr - buffer); + if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) { + ikcp_output(kcp, buffer, size); + ptr = buffer; + } + ptr = ikcp_encode_seg(ptr, &seg); + } + + kcp->probe = 0; + + // calculate window size + cwnd = _imin_(kcp->snd_wnd, kcp->rmt_wnd); + if (kcp->nocwnd == 0) cwnd = _imin_(kcp->cwnd, cwnd); + + // move data from snd_queue to snd_buf + while (_itimediff(kcp->snd_nxt, kcp->snd_una + cwnd) < 0) { + IKCPSEG *newseg; + if (iqueue_is_empty(&kcp->snd_queue)) break; + + newseg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node); + + iqueue_del(&newseg->node); + iqueue_add_tail(&newseg->node, &kcp->snd_buf); + kcp->nsnd_que--; + kcp->nsnd_buf++; + + newseg->conv = kcp->conv; + newseg->cmd = IKCP_CMD_PUSH; + newseg->wnd = seg.wnd; + newseg->ts = current; + newseg->sn = kcp->snd_nxt++; + newseg->una = kcp->rcv_nxt; + newseg->resendts = current; + newseg->rto = kcp->rx_rto; + newseg->fastack = 0; + newseg->xmit = 0; + } + + // calculate resent + resent = (kcp->fastresend > 0)? (IUINT32)kcp->fastresend : 0xffffffff; + rtomin = (kcp->nodelay == 0)? (kcp->rx_rto >> 3) : 0; + + // flush data segments + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) { + IKCPSEG *segment = iqueue_entry(p, IKCPSEG, node); + int needsend = 0; + if (segment->xmit == 0) { + needsend = 1; + segment->xmit++; + segment->rto = kcp->rx_rto; + segment->resendts = current + segment->rto + rtomin; + } + else if (_itimediff(current, segment->resendts) >= 0) { + needsend = 1; + segment->xmit++; + kcp->xmit++; + if (kcp->nodelay == 0) { + segment->rto += _imax_(segment->rto, (IUINT32)kcp->rx_rto); + } else { + IINT32 step = (kcp->nodelay < 2)? + ((IINT32)(segment->rto)) : kcp->rx_rto; + segment->rto += step / 2; + } + segment->resendts = current + segment->rto; + lost = 1; + } + else if (segment->fastack >= resent) { + if ((int)segment->xmit <= kcp->fastlimit || + kcp->fastlimit <= 0) { + needsend = 1; + segment->xmit++; + segment->fastack = 0; + segment->resendts = current + segment->rto; + change++; + } + } + + if (needsend) { + int need; + segment->ts = current; + segment->wnd = seg.wnd; + segment->una = kcp->rcv_nxt; + + size = (int)(ptr - buffer); + need = IKCP_OVERHEAD + segment->len; + + if (size + need > (int)kcp->mtu) { + ikcp_output(kcp, buffer, size); + ptr = buffer; + } + + ptr = ikcp_encode_seg(ptr, segment); + + if (segment->len > 0) { + memcpy(ptr, segment->data, segment->len); + ptr += segment->len; + } + + if (segment->xmit >= kcp->dead_link) { + kcp->state = (IUINT32)-1; + } + } + } + + // flash remain segments + size = (int)(ptr - buffer); + if (size > 0) { + ikcp_output(kcp, buffer, size); + } + + // update ssthresh + if (change) { + IUINT32 inflight = kcp->snd_nxt - kcp->snd_una; + kcp->ssthresh = inflight / 2; + if (kcp->ssthresh < IKCP_THRESH_MIN) + kcp->ssthresh = IKCP_THRESH_MIN; + kcp->cwnd = kcp->ssthresh + resent; + kcp->incr = kcp->cwnd * kcp->mss; + } + + if (lost) { + kcp->ssthresh = cwnd / 2; + if (kcp->ssthresh < IKCP_THRESH_MIN) + kcp->ssthresh = IKCP_THRESH_MIN; + kcp->cwnd = 1; + kcp->incr = kcp->mss; + } + + if (kcp->cwnd < 1) { + kcp->cwnd = 1; + kcp->incr = kcp->mss; + } +} + + +//--------------------------------------------------------------------- +// update state (call it repeatedly, every 10ms-100ms), or you can ask +// ikcp_check when to call it again (without ikcp_input/_send calling). +// 'current' - current timestamp in millisec. +//--------------------------------------------------------------------- +void ikcp_update(ikcpcb *kcp, IUINT32 current) +{ + IINT32 slap; + + kcp->current = current; + + if (kcp->updated == 0) { + kcp->updated = 1; + kcp->ts_flush = kcp->current; + } + + slap = _itimediff(kcp->current, kcp->ts_flush); + + if (slap >= 10000 || slap < -10000) { + kcp->ts_flush = kcp->current; + slap = 0; + } + + if (slap >= 0) { + kcp->ts_flush += kcp->interval; + if (_itimediff(kcp->current, kcp->ts_flush) >= 0) { + kcp->ts_flush = kcp->current + kcp->interval; + } + ikcp_flush(kcp); + } +} + + +//--------------------------------------------------------------------- +// Determine when should you invoke ikcp_update: +// returns when you should invoke ikcp_update in millisec, if there +// is no ikcp_input/_send calling. you can call ikcp_update in that +// time, instead of call update repeatly. +// Important to reduce unnacessary ikcp_update invoking. use it to +// schedule ikcp_update (eg. implementing an epoll-like mechanism, +// or optimize ikcp_update when handling massive kcp connections) +//--------------------------------------------------------------------- +IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current) +{ + IUINT32 ts_flush = kcp->ts_flush; + IINT32 tm_flush = 0x7fffffff; + IINT32 tm_packet = 0x7fffffff; + IUINT32 minimal = 0; + struct IQUEUEHEAD *p; + + if (kcp->updated == 0) { + return current; + } + + if (_itimediff(current, ts_flush) >= 10000 || + _itimediff(current, ts_flush) < -10000) { + ts_flush = current; + } + + if (_itimediff(current, ts_flush) >= 0) { + return current; + } + + tm_flush = _itimediff(ts_flush, current); + + for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) { + const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node); + IINT32 diff = _itimediff(seg->resendts, current); + if (diff <= 0) { + return current; + } + if (diff < tm_packet) tm_packet = diff; + } + + minimal = (IUINT32)(tm_packet < tm_flush ? tm_packet : tm_flush); + if (minimal >= kcp->interval) minimal = kcp->interval; + + return current + minimal; +} + + + +int ikcp_setmtu(ikcpcb *kcp, int mtu) +{ + char *buffer; + if (mtu < 50 || mtu < (int)IKCP_OVERHEAD) + return -1; + buffer = (char*)ikcp_malloc((mtu + IKCP_OVERHEAD) * 3); + if (buffer == NULL) + return -2; + kcp->mtu = mtu; + kcp->mss = kcp->mtu - IKCP_OVERHEAD; + ikcp_free(kcp->buffer); + kcp->buffer = buffer; + return 0; +} + +int ikcp_interval(ikcpcb *kcp, int interval) +{ + if (interval > 5000) interval = 5000; + else if (interval < 10) interval = 10; + kcp->interval = interval; + return 0; +} + +int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc) +{ + if (nodelay >= 0) { + kcp->nodelay = nodelay; + if (nodelay) { + kcp->rx_minrto = IKCP_RTO_NDL; + } + else { + kcp->rx_minrto = IKCP_RTO_MIN; + } + } + if (interval >= 0) { + if (interval > 5000) interval = 5000; + else if (interval < 10) interval = 10; + kcp->interval = interval; + } + if (resend >= 0) { + kcp->fastresend = resend; + } + if (nc >= 0) { + kcp->nocwnd = nc; + } + return 0; +} + + +int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd) +{ + if (kcp) { + if (sndwnd > 0) { + kcp->snd_wnd = sndwnd; + } + if (rcvwnd > 0) { // must >= max fragment size + kcp->rcv_wnd = _imax_(rcvwnd, IKCP_WND_RCV); + } + } + return 0; +} + +int ikcp_waitsnd(const ikcpcb *kcp) +{ + return kcp->nsnd_buf + kcp->nsnd_que; +} + + +// read conv +IUINT32 ikcp_getconv(const void *ptr) +{ + IUINT32 conv; + ikcp_decode32u((const char*)ptr, &conv); + return conv; +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/common/kcp/ikcp.h b/common/kcp/ikcp.h new file mode 100644 index 0000000..b8c337f --- /dev/null +++ b/common/kcp/ikcp.h @@ -0,0 +1,416 @@ +//===================================================================== +// +// KCP - A Better ARQ Protocol Implementation +// skywind3000 (at) gmail.com, 2010-2011 +// +// Features: +// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. +// + Maximum RTT reduce three times vs tcp. +// + Lightweight, distributed as a single source file. +// +//===================================================================== +#ifndef __IKCP_H__ +#define __IKCP_H__ + +#include +#include +#include + + +//===================================================================== +// 32BIT INTEGER DEFINITION +//===================================================================== +#ifndef __INTEGER_32_BITS__ +#define __INTEGER_32_BITS__ +#if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \ + defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \ + defined(_M_AMD64) + typedef unsigned int ISTDUINT32; + typedef int ISTDINT32; +#elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \ + defined(__i386) || defined(_M_X86) + typedef unsigned long ISTDUINT32; + typedef long ISTDINT32; +#elif defined(__MACOS__) + typedef UInt32 ISTDUINT32; + typedef SInt32 ISTDINT32; +#elif defined(__APPLE__) && defined(__MACH__) + #include + typedef u_int32_t ISTDUINT32; + typedef int32_t ISTDINT32; +#elif defined(__BEOS__) + #include + typedef u_int32_t ISTDUINT32; + typedef int32_t ISTDINT32; +#elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__)) + typedef unsigned __int32 ISTDUINT32; + typedef __int32 ISTDINT32; +#elif defined(__GNUC__) + #include + typedef uint32_t ISTDUINT32; + typedef int32_t ISTDINT32; +#else + typedef unsigned long ISTDUINT32; + typedef long ISTDINT32; +#endif +#endif + + +//===================================================================== +// Integer Definition +//===================================================================== +#ifndef __IINT8_DEFINED +#define __IINT8_DEFINED +typedef char IINT8; +#endif + +#ifndef __IUINT8_DEFINED +#define __IUINT8_DEFINED +typedef unsigned char IUINT8; +#endif + +#ifndef __IUINT16_DEFINED +#define __IUINT16_DEFINED +typedef unsigned short IUINT16; +#endif + +#ifndef __IINT16_DEFINED +#define __IINT16_DEFINED +typedef short IINT16; +#endif + +#ifndef __IINT32_DEFINED +#define __IINT32_DEFINED +typedef ISTDINT32 IINT32; +#endif + +#ifndef __IUINT32_DEFINED +#define __IUINT32_DEFINED +typedef ISTDUINT32 IUINT32; +#endif + +#ifndef __IINT64_DEFINED +#define __IINT64_DEFINED +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 IINT64; +#else +typedef long long IINT64; +#endif +#endif + +#ifndef __IUINT64_DEFINED +#define __IUINT64_DEFINED +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 IUINT64; +#else +typedef unsigned long long IUINT64; +#endif +#endif + +#ifndef INLINE +#if defined(__GNUC__) + +#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) +#define INLINE __inline__ __attribute__((always_inline)) +#else +#define INLINE __inline__ +#endif + +#elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__)) +#define INLINE __inline +#else +#define INLINE +#endif +#endif + +#if (!defined(__cplusplus)) && (!defined(inline)) +#define inline INLINE +#endif + + +//===================================================================== +// QUEUE DEFINITION +//===================================================================== +#ifndef __IQUEUE_DEF__ +#define __IQUEUE_DEF__ + +struct IQUEUEHEAD { + struct IQUEUEHEAD *next, *prev; +}; + +typedef struct IQUEUEHEAD iqueue_head; + + +//--------------------------------------------------------------------- +// queue init +//--------------------------------------------------------------------- +#define IQUEUE_HEAD_INIT(name) { &(name), &(name) } +#define IQUEUE_HEAD(name) \ + struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name) + +#define IQUEUE_INIT(ptr) ( \ + (ptr)->next = (ptr), (ptr)->prev = (ptr)) + +#define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define ICONTAINEROF(ptr, type, member) ( \ + (type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) ) + +#define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member) + + +//--------------------------------------------------------------------- +// queue operation +//--------------------------------------------------------------------- +#define IQUEUE_ADD(node, head) ( \ + (node)->prev = (head), (node)->next = (head)->next, \ + (head)->next->prev = (node), (head)->next = (node)) + +#define IQUEUE_ADD_TAIL(node, head) ( \ + (node)->prev = (head)->prev, (node)->next = (head), \ + (head)->prev->next = (node), (head)->prev = (node)) + +#define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n)) + +#define IQUEUE_DEL(entry) (\ + (entry)->next->prev = (entry)->prev, \ + (entry)->prev->next = (entry)->next, \ + (entry)->next = 0, (entry)->prev = 0) + +#define IQUEUE_DEL_INIT(entry) do { \ + IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0) + +#define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next) + +#define iqueue_init IQUEUE_INIT +#define iqueue_entry IQUEUE_ENTRY +#define iqueue_add IQUEUE_ADD +#define iqueue_add_tail IQUEUE_ADD_TAIL +#define iqueue_del IQUEUE_DEL +#define iqueue_del_init IQUEUE_DEL_INIT +#define iqueue_is_empty IQUEUE_IS_EMPTY + +#define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \ + for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \ + &((iterator)->MEMBER) != (head); \ + (iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER)) + +#define iqueue_foreach(iterator, head, TYPE, MEMBER) \ + IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) + +#define iqueue_foreach_entry(pos, head) \ + for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next ) + + +#define __iqueue_splice(list, head) do { \ + iqueue_head *first = (list)->next, *last = (list)->prev; \ + iqueue_head *at = (head)->next; \ + (first)->prev = (head), (head)->next = (first); \ + (last)->next = (at), (at)->prev = (last); } while (0) + +#define iqueue_splice(list, head) do { \ + if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0) + +#define iqueue_splice_init(list, head) do { \ + iqueue_splice(list, head); iqueue_init(list); } while (0) + + +#ifdef _MSC_VER +#pragma warning(disable:4311) +#pragma warning(disable:4312) +#pragma warning(disable:4996) +#endif + +#endif + + +//--------------------------------------------------------------------- +// BYTE ORDER & ALIGNMENT +//--------------------------------------------------------------------- +#ifndef IWORDS_BIG_ENDIAN + #ifdef _BIG_ENDIAN_ + #if _BIG_ENDIAN_ + #define IWORDS_BIG_ENDIAN 1 + #endif + #endif + #ifndef IWORDS_BIG_ENDIAN + #if defined(__hppa__) || \ + defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ + (defined(__MIPS__) && defined(__MIPSEB__)) || \ + defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ + defined(__sparc__) || defined(__powerpc__) || \ + defined(__mc68000__) || defined(__s390x__) || defined(__s390__) + #define IWORDS_BIG_ENDIAN 1 + #endif + #endif + #ifndef IWORDS_BIG_ENDIAN + #define IWORDS_BIG_ENDIAN 0 + #endif +#endif + +#ifndef IWORDS_MUST_ALIGN + #if defined(__i386__) || defined(__i386) || defined(_i386_) + #define IWORDS_MUST_ALIGN 0 + #elif defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) + #define IWORDS_MUST_ALIGN 0 + #elif defined(__amd64) || defined(__amd64__) + #define IWORDS_MUST_ALIGN 0 + #else + #define IWORDS_MUST_ALIGN 1 + #endif +#endif + + +//===================================================================== +// SEGMENT +//===================================================================== +struct IKCPSEG +{ + struct IQUEUEHEAD node; + IUINT32 conv; + IUINT32 cmd; + IUINT32 frg; + IUINT32 wnd; + IUINT32 ts; + IUINT32 sn; + IUINT32 una; + IUINT32 len; + IUINT32 resendts; + IUINT32 rto; + IUINT32 fastack; + IUINT32 xmit; + char data[1]; +}; + + +//--------------------------------------------------------------------- +// IKCPCB +//--------------------------------------------------------------------- +struct IKCPCB +{ + IUINT32 conv, mtu, mss, state; + IUINT32 snd_una, snd_nxt, rcv_nxt; + IUINT32 ts_recent, ts_lastack, ssthresh; + IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto; + IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe; + IUINT32 current, interval, ts_flush, xmit; + IUINT32 nrcv_buf, nsnd_buf; + IUINT32 nrcv_que, nsnd_que; + IUINT32 nodelay, updated; + IUINT32 ts_probe, probe_wait; + IUINT32 dead_link, incr; + struct IQUEUEHEAD snd_queue; + struct IQUEUEHEAD rcv_queue; + struct IQUEUEHEAD snd_buf; + struct IQUEUEHEAD rcv_buf; + IUINT32 *acklist; + IUINT32 ackcount; + IUINT32 ackblock; + void *user; + char *buffer; + int fastresend; + int fastlimit; + int nocwnd, stream; + int logmask; + int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user); + void (*writelog)(const char *log, struct IKCPCB *kcp, void *user); +}; + + +typedef struct IKCPCB ikcpcb; + +#define IKCP_LOG_OUTPUT 1 +#define IKCP_LOG_INPUT 2 +#define IKCP_LOG_SEND 4 +#define IKCP_LOG_RECV 8 +#define IKCP_LOG_IN_DATA 16 +#define IKCP_LOG_IN_ACK 32 +#define IKCP_LOG_IN_PROBE 64 +#define IKCP_LOG_IN_WINS 128 +#define IKCP_LOG_OUT_DATA 256 +#define IKCP_LOG_OUT_ACK 512 +#define IKCP_LOG_OUT_PROBE 1024 +#define IKCP_LOG_OUT_WINS 2048 + +#ifdef __cplusplus +extern "C" { +#endif + +//--------------------------------------------------------------------- +// interface +//--------------------------------------------------------------------- + +// create a new kcp control object, 'conv' must equal in two endpoint +// from the same connection. 'user' will be passed to the output callback +// output callback can be setup like this: 'kcp->output = my_udp_output' +ikcpcb* ikcp_create(IUINT32 conv, void *user); + +// release kcp control object +void ikcp_release(ikcpcb *kcp); + +// set output callback, which will be invoked by kcp +void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len, + ikcpcb *kcp, void *user)); + +// user/upper level recv: returns size, returns below zero for EAGAIN +int ikcp_recv(ikcpcb *kcp, char *buffer, int len); + +// user/upper level send, returns below zero for error +int ikcp_send(ikcpcb *kcp, const char *buffer, int len); + +// update state (call it repeatedly, every 10ms-100ms), or you can ask +// ikcp_check when to call it again (without ikcp_input/_send calling). +// 'current' - current timestamp in millisec. +void ikcp_update(ikcpcb *kcp, IUINT32 current); + +// Determine when should you invoke ikcp_update: +// returns when you should invoke ikcp_update in millisec, if there +// is no ikcp_input/_send calling. you can call ikcp_update in that +// time, instead of call update repeatly. +// Important to reduce unnacessary ikcp_update invoking. use it to +// schedule ikcp_update (eg. implementing an epoll-like mechanism, +// or optimize ikcp_update when handling massive kcp connections) +IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current); + +// when you received a low level packet (eg. UDP packet), call it +int ikcp_input(ikcpcb *kcp, const char *data, long size); + +// flush pending data +void ikcp_flush(ikcpcb *kcp); + +// check the size of next message in the recv queue +int ikcp_peeksize(const ikcpcb *kcp); + +// change MTU size, default is 1400 +int ikcp_setmtu(ikcpcb *kcp, int mtu); + +// set maximum window size: sndwnd=32, rcvwnd=32 by default +int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd); + +// get how many packet is waiting to be sent +int ikcp_waitsnd(const ikcpcb *kcp); + +// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1) +// nodelay: 0:disable(default), 1:enable +// interval: internal update timer interval in millisec, default is 100ms +// resend: 0:disable fast resend(default), 1:enable fast resend +// nc: 0:normal congestion control(default), 1:disable congestion control +int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc); + + +void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...); + +// setup allocator +void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)); + +// read conv +IUINT32 ikcp_getconv(const void *ptr); + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/hpsocket/GlobalDef.h b/hpsocket/GlobalDef.h new file mode 100644 index 0000000..abca8ed --- /dev/null +++ b/hpsocket/GlobalDef.h @@ -0,0 +1,278 @@ +/* + * 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 + +typedef int BOOL; +typedef float FLOAT; +typedef FLOAT *PFLOAT, *LPFLOAT; +typedef double DOUBLE; +typedef DOUBLE *PDOUBLE, *LPDOUBLE; +typedef short SHORT, INT16; +typedef SHORT *PSHORT, *LPSHORT; +typedef unsigned short USHORT, UINT16; +typedef USHORT *PUSHORT, *LPUSHORT; +typedef unsigned short WORD; +typedef WORD *PWORD, *LPWORD; +typedef unsigned int DWORD; +typedef DWORD *PDWORD, *LPDWORD; +typedef long LONG, LID; +typedef LONG *PLONG, *LPLONG; +typedef unsigned long ULONG, ULID; +typedef ULONG *PULONG, *LPULONG; +typedef long long LONGLONG, LLONG, INT64; +typedef LONGLONG *PLONGLONG, *LPLONGLONG, *PLLONG, *LPLLONG; +typedef unsigned long long ULONGLONG, ULLONG, UINT64; +typedef ULONGLONG *PULONGLONG, *LPULONGLONG, *PULLONG, *LPULLONG; +typedef int INT, IID, INT32; +typedef INT *PINT, *LPINT; +typedef unsigned int UINT, UIID, UINT32; +typedef UINT *PUINT, *LPUINT; +typedef void VOID; +typedef VOID *PVOID, *LPVOID; +typedef char CHAR, INT8; +typedef CHAR *PCHAR, *LPCHAR, *PSTR, *LPSTR; +typedef const char *PCSTR, *LPCSTR; +typedef unsigned char BYTE, UINT8; +typedef BYTE *PBYTE, *LPBYTE; +typedef const BYTE *PCBYTE, *LPCBYTE; +typedef wchar_t WCHAR; +typedef WCHAR *PWSTR, *LPWSTR; +typedef const WCHAR *PCWSTR, *LPCWSTR; +typedef LONG INT_PTR, LONG_PTR, LPARAM; +typedef ULONG UINT_PTR, ULONG_PTR, DWORD_PTR, WPARAM; + +typedef IID FD, HANDLE, SOCKET; +typedef INT LRESULT, HRESULT; + +typedef LLONG __time64_t; +typedef INT __time32_t; + +typedef LID NTHR_ID; + +#ifdef __ANDROID__ + typedef LID THR_ID; +#else + typedef ULID THR_ID; +#endif + +typedef size_t SIZE_T; +typedef ssize_t SSIZE_T; + +#ifdef _UNICODE + typedef WCHAR TCHAR; +#else + typedef CHAR TCHAR; +#endif + +typedef TCHAR *PTSTR, *LPTSTR; +typedef const TCHAR *PCTSTR, *LPCTSTR; + +#define MAXUINT8 ((UINT8)~((UINT8)0)) +#define MAXINT8 ((INT8)(MAXUINT8 >> 1)) +#define MININT8 ((INT8)~MAXINT8) + +#define MAXUINT16 ((UINT16)~((UINT16)0)) +#define MAXINT16 ((INT16)(MAXUINT16 >> 1)) +#define MININT16 ((INT16)~MAXINT16) + +#define MAXUINT32 ((UINT32)~((UINT32)0)) +#define MAXINT32 ((INT32)(MAXUINT32 >> 1)) +#define MININT32 ((INT32)~MAXINT32) + +#define MAXUINT64 ((UINT64)~((UINT64)0)) +#define MAXINT64 ((INT64)(MAXUINT64 >> 1)) +#define MININT64 ((INT64)~MAXINT64) + +#define MAXULONG ((ULONG)~((ULONG)0)) +#define MAXLONG ((LONG)(MAXULONG >> 1)) +#define MINLONG ((LONG)~MAXLONG) + +#define MAXULONGLONG ((ULONGLONG)~((ULONGLONG)0)) +#define MINLONGLONG ((LONGLONG)~MAXLONGLONG) + +#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0)) +#define MAXSSIZE_T ((SSIZE_T)(MAXSIZE_T >> 1)) +#define MINSSIZE_T ((SSIZE_T)~MAXSSIZE_T) + +#define MAXUINT ((UINT)~((UINT)0)) +#define MAXINT ((INT)(MAXUINT >> 1)) +#define MININT ((INT)~MAXINT) + +#define MAXDWORD32 ((DWORD32)~((DWORD32)0)) +#define MAXDWORD64 ((DWORD64)~((DWORD64)0)) + +#define MINBYTE 0x00 +#define MAXBYTE 0xFF +#define MINCHAR 0x80 +#define MAXCHAR 0x7F +#define MINSHORT 0x8000 +#define MAXSHORT 0x7FFF +#define MINUSHORT 0x0000 +#define MAXUSHORT 0xFFFF +#define MINWORD 0x0000 +#define MAXWORD 0xFFFF +#define MINDWORD 0x00000000 +#define MAXDWORD 0xFFFFFFFF + +#ifdef _UNICODE + #define __T(x) L ## x +#else + #define __T(x) x + #define T2A(p) (p) + #define A2T(p) (p) + #define A2CT(p) (p) + #define T2CA(p) (p) + #define CT2A(p) (p) + #define CA2T(p) (p) + #define CA2CT(p) (p) +#endif + +#define _T(x) __T(x) +#define _TEXT(x) __T(x) + +#define _In_ +#define _Out_ +#define _Inout_ +#define _In_opt_ +#define _Out_opt_ +#define _Inout_opt_ +#define USES_CONVERSION + +#define INFINITE -1 +#define NO_ERROR 0 +#define HAS_ERROR -1 +#define TIMEOUT 0 +#define RS_OK NO_ERROR +#define RS_FAIL HAS_ERROR +#define RS_TIMEOUT TIMEOUT +#define INVALID_FD -1 +#define INVALID_HANDLE_VALUE INVALID_FD +#define INVALID_PVOID ((PVOID)-1) +#define _MAX_PATH 256 +#define MAX_PATH _MAX_PATH +#define TRUE 1 +#define FALSE 0 +#define CONST const + +#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8)) +#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16)) +#define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff)) +#define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff)) +#define LOBYTE(w) ((BYTE)(((DWORD_PTR)(w)) & 0xff)) +#define HIBYTE(w) ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff)) + +#if !defined(MAX) + #define MAX(a,b) (((a) >= (b)) ? (a) : (b)) +#endif + +#if !defined(MIN) + #define MIN(a,b) (((a) <= (b)) ? (a) : (b)) +#endif + +#if !defined(_max) + #define _max(a,b) MAX(a,b) +#endif + +#if !defined(_min) + #define _min(a,b) MIN(a,b) +#endif + +#if defined(NDEBUG) + #if !defined(_NDEBUG) + #define _NDEBUG + #endif + #if defined(DEBUG) + #undef DEBUG + #endif + #if defined(_DEBUG) + #undef _DEBUG + #endif + #if defined(DEBUG_TRACE) + #undef DEBUG_TRACE + #endif +#else + #if defined(_NDEBUG) + #undef _NDEBUG + #endif + #if !defined(DEBUG) + #define DEBUG + #endif + #if !defined(_DEBUG) + #define _DEBUG + #endif +#endif + +#if defined(__arm64__) && !defined(__aarch64__) + #define __aarch64__ __arm64__ +#elif defined(__aarch64__) && !defined(__arm64__) + #define __arm64__ __aarch64__ +#endif + +#ifdef __cplusplus + #define EXTERN_C extern "C" + #define EXTERN_C_BEGIN EXTERN_C { + #define EXTERN_C_END } +#else + #define EXTERN_C extern + #define EXTERN_C_BEGIN + #define EXTERN_C_END +#endif + +#if !defined(__stdcall) + #define __stdcall __attribute__ ((__stdcall__)) +#endif + +#if !defined(__cdecl) + #define __cdecl __attribute__ ((__cdecl__)) +#endif + +#if !defined(_GNU_SOURCE) + #define _GNU_SOURCE +#endif + +#if !defined(CALLBACK) + #define CALLBACK +#endif + +#if !defined(WINAPI) + #define WINAPI +#endif + +#if !defined(FORCEINLINE) + #ifdef __GNUC__ + #define FORCEINLINE __attribute__ ((always_inline)) + #else + #define FORCEINLINE inline + #endif +#endif + +#if !defined(STATIC_FORCEINLINE) + #define STATIC_FORCEINLINE static FORCEINLINE +#endif + +#if !defined(EXTERN_FORCEINLINE) + #define EXTERN_FORCEINLINE extern FORCEINLINE +#endif diff --git a/hpsocket/GlobalErrno.h b/hpsocket/GlobalErrno.h new file mode 100644 index 0000000..4e1eed9 --- /dev/null +++ b/hpsocket/GlobalErrno.h @@ -0,0 +1,252 @@ +/* + * 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 "GlobalDef.h" + +#include +#include + +#define ERROR_INVALID_STATE EPERM +#define ERROR_INVALID_PARAMETER EINVAL +#define ERROR_BROKEN_PIPE EPIPE +#define ERROR_AGAIN EAGAIN +#define ERROR_WOULDBLOCK EAGAIN +#define ERROR_READ_FAULT EFAULT +#define ERROR_WRITE_FAULT EFAULT +#define ERROR_HANDLES_CLOSED EBADFD +#define ERROR_IO_PENDING EINPROGRESS +#define ERROR_INTR EINTR +#define ERROR_EMPTY ENODATA +#define ERROR_NO_DATA ENODATA +#define ERROR_FILE_TOO_LARGE EFBIG +#define ERROR_INVALID_OPERATION EPERM +#define ERROR_CANCELLED ECANCELED +#define ERROR_UNKNOWN ENOMSG +#define ERROR_OBJECT_NOT_FOUND EBADSLT +#define ERROR_NOT_FOUND EBADSLT +#define ERROR_INVALID_INDEX ENOANO +#define ERROR_OPERATION_ABORTED ECANCELED +#define ERROR_CONNABORTED ECONNABORTED +#define ERROR_ADDRNOTAVAIL EADDRNOTAVAIL +#define ERROR_INCORRECT_ADDRESS EADDRNOTAVAIL +#define ERROR_PFNOSUPPORT EPFNOSUPPORT +#define ERROR_AFNOSUPPORT EAFNOSUPPORT +#define ERROR_TIMEOUT ETIMEDOUT +#define ERROR_TIMEDOUT ETIMEDOUT +#define ERROR_PROTO EPROTO +#define ERROR_CONNECTION_COUNT_LIMIT ENOSR +#define ERROR_VERIFY_CHECK EBADRQC +#define ERROR_CREATE_FAILED EMFILE +#define ERROR_INVALID_DATA EBADMSG +#define ERROR_BAD_LENGTH EMSGSIZE +#define ERROR_CALL_NOT_IMPLEMENTED EPERM +#define ERROR_INCORRECT_SIZE EMSGSIZE +#define ERROR_CONNRESET ECONNRESET +#define ERROR_CONNREFUSED ECONNREFUSED +#define ERROR_HOSTUNREACH EHOSTUNREACH +#define ERROR_INVALID_NAME ENOENT +#define ERROR_BAD_FILE_TYPE EBADF +#define ERROR_FILE_NOT_FOUND ENOENT +#define ERROR_FUNCTION_FAILED EFAULT +#define ERROR_INVALID_PASSWORD EACCES +#define ERROR_INVALID_ACCESS EACCES +#define ERROR_NOT_READY EPERM +#define ERROR_NOT_SUPPORTED EPERM +#define ERROR_BAD_FORMAT EBADMSG +#define ERROR_BUFFER_OVERFLOW E2BIG +#define ERROR_OUT_OF_RANGE ERANGE +#define ERROR_DESTINATION_ELEMENT_FULL EXFULL +#define ERROR_ALREADY_INITIALIZED EALREADY +#define ERROR_CANT_WAIT EIO + +#define EXIT_CODE_OK EX_OK +#define EXIT_CODE_CONFIG EX_CONFIG +#define EXIT_CODE_SOFTWARE EX_SOFTWARE + +/* +* Socket error codes. +*/ +#ifndef WSABASEERR + +/* +* All Sockets error constants are biased by WSABASEERR from +* the "normal" +*/ +#define WSABASEERR 10000 + +/* +* Sockets definitions of regular Microsoft C error constants +*/ +#define WSAEINTR (WSABASEERR+4) +#define WSAEBADF (WSABASEERR+9) +#define WSAEACCES (WSABASEERR+13) +#define WSAEFAULT (WSABASEERR+14) +#define WSAEINVAL (WSABASEERR+22) +#define WSAEMFILE (WSABASEERR+24) + +/* + * Sockets definitions of regular Berkeley error constants + */ +#define WSAEWOULDBLOCK (WSABASEERR+35) +#define WSAEINPROGRESS (WSABASEERR+36) +#define WSAEALREADY (WSABASEERR+37) +#define WSAENOTSOCK (WSABASEERR+38) +#define WSAEDESTADDRREQ (WSABASEERR+39) +#define WSAEMSGSIZE (WSABASEERR+40) +#define WSAEPROTOTYPE (WSABASEERR+41) +#define WSAENOPROTOOPT (WSABASEERR+42) +#define WSAEPROTONOSUPPORT (WSABASEERR+43) +#define WSAESOCKTNOSUPPORT (WSABASEERR+44) +#define WSAEOPNOTSUPP (WSABASEERR+45) +#define WSAEPFNOSUPPORT (WSABASEERR+46) +#define WSAEAFNOSUPPORT (WSABASEERR+47) +#define WSAEADDRINUSE (WSABASEERR+48) +#define WSAEADDRNOTAVAIL (WSABASEERR+49) +#define WSAENETDOWN (WSABASEERR+50) +#define WSAENETUNREACH (WSABASEERR+51) +#define WSAENETRESET (WSABASEERR+52) +#define WSAECONNABORTED (WSABASEERR+53) +#define WSAECONNRESET (WSABASEERR+54) +#define WSAENOBUFS (WSABASEERR+55) +#define WSAEISCONN (WSABASEERR+56) +#define WSAENOTCONN (WSABASEERR+57) +#define WSAESHUTDOWN (WSABASEERR+58) +#define WSAETOOMANYREFS (WSABASEERR+59) +#define WSAETIMEDOUT (WSABASEERR+60) +#define WSAECONNREFUSED (WSABASEERR+61) +#define WSAELOOP (WSABASEERR+62) +#define WSAENAMETOOLONG (WSABASEERR+63) +#define WSAEHOSTDOWN (WSABASEERR+64) +#define WSAEHOSTUNREACH (WSABASEERR+65) +#define WSAENOTEMPTY (WSABASEERR+66) +#define WSAEPROCLIM (WSABASEERR+67) +#define WSAEUSERS (WSABASEERR+68) +#define WSAEDQUOT (WSABASEERR+69) +#define WSAESTALE (WSABASEERR+70) +#define WSAEREMOTE (WSABASEERR+71) + +/* + * Extended Sockets error constant definitions + */ +#define WSASYSNOTREADY (WSABASEERR+91) +#define WSAVERNOTSUPPORTED (WSABASEERR+92) +#define WSANOTINITIALISED (WSABASEERR+93) +#define WSAEDISCON (WSABASEERR+101) +#define WSAENOMORE (WSABASEERR+102) +#define WSAECANCELLED (WSABASEERR+103) +#define WSAEINVALIDPROCTABLE (WSABASEERR+104) +#define WSAEINVALIDPROVIDER (WSABASEERR+105) +#define WSAEPROVIDERFAILEDINIT (WSABASEERR+106) +#define WSASYSCALLFAILURE (WSABASEERR+107) +#define WSASERVICE_NOT_FOUND (WSABASEERR+108) +#define WSATYPE_NOT_FOUND (WSABASEERR+109) +#define WSA_E_NO_MORE (WSABASEERR+110) +#define WSA_E_CANCELLED (WSABASEERR+111) +#define WSAEREFUSED (WSABASEERR+112) + +/* + * Error return codes from gethostbyname() and gethostbyaddr() + * (when using the resolver). Note that these errors are + * retrieved via WSAGetLastError() and must therefore follow + * the rules for avoiding clashes with error numbers from + * specific implementations or language run-time systems. + * For this reason the codes are based at WSABASEERR+1001. + * Note also that [WSA]NO_ADDRESS is defined only for + * compatibility purposes. + */ + +/* Authoritative Answer: Host not found */ +#define WSAHOST_NOT_FOUND (WSABASEERR+1001) + +/* Non-Authoritative: Host not found, or SERVERFAIL */ +#define WSATRY_AGAIN (WSABASEERR+1002) + +/* Non-recoverable errors, FORMERR, REFUSED, NOTIMP */ +#define WSANO_RECOVERY (WSABASEERR+1003) + +/* Valid name, no data record of requested type */ +#define WSANO_DATA (WSABASEERR+1004) + +/* + * Define QOS related error return codes + * + */ +#define WSA_QOS_RECEIVERS (WSABASEERR + 1005) + /* at least one Reserve has arrived */ +#define WSA_QOS_SENDERS (WSABASEERR + 1006) + /* at least one Path has arrived */ +#define WSA_QOS_NO_SENDERS (WSABASEERR + 1007) + /* there are no senders */ +#define WSA_QOS_NO_RECEIVERS (WSABASEERR + 1008) + /* there are no receivers */ +#define WSA_QOS_REQUEST_CONFIRMED (WSABASEERR + 1009) + /* Reserve has been confirmed */ +#define WSA_QOS_ADMISSION_FAILURE (WSABASEERR + 1010) + /* error due to lack of resources */ +#define WSA_QOS_POLICY_FAILURE (WSABASEERR + 1011) + /* rejected for administrative reasons - bad credentials */ +#define WSA_QOS_BAD_STYLE (WSABASEERR + 1012) + /* unknown or conflicting style */ +#define WSA_QOS_BAD_OBJECT (WSABASEERR + 1013) + /* problem with some part of the filterspec or providerspecific + * buffer in general */ +#define WSA_QOS_TRAFFIC_CTRL_ERROR (WSABASEERR + 1014) + /* problem with some part of the flowspec */ +#define WSA_QOS_GENERIC_ERROR (WSABASEERR + 1015) + /* general error */ +#define WSA_QOS_ESERVICETYPE (WSABASEERR + 1016) + /* invalid service type in flowspec */ +#define WSA_QOS_EFLOWSPEC (WSABASEERR + 1017) + /* invalid flowspec */ +#define WSA_QOS_EPROVSPECBUF (WSABASEERR + 1018) + /* invalid provider specific buffer */ +#define WSA_QOS_EFILTERSTYLE (WSABASEERR + 1019) + /* invalid filter style */ +#define WSA_QOS_EFILTERTYPE (WSABASEERR + 1020) + /* invalid filter type */ +#define WSA_QOS_EFILTERCOUNT (WSABASEERR + 1021) + /* incorrect number of filters */ +#define WSA_QOS_EOBJLENGTH (WSABASEERR + 1022) + /* invalid object length */ +#define WSA_QOS_EFLOWCOUNT (WSABASEERR + 1023) + /* incorrect number of flows */ +#define WSA_QOS_EUNKOWNPSOBJ (WSABASEERR + 1024) + /* unknown object in provider specific buffer */ +#define WSA_QOS_EPOLICYOBJ (WSABASEERR + 1025) + /* invalid policy object in provider specific buffer */ +#define WSA_QOS_EFLOWDESC (WSABASEERR + 1026) + /* invalid flow descriptor in the list */ +#define WSA_QOS_EPSFLOWSPEC (WSABASEERR + 1027) + /* inconsistent flow spec in provider specific buffer */ +#define WSA_QOS_EPSFILTERSPEC (WSABASEERR + 1028) + /* invalid filter spec in provider specific buffer */ +#define WSA_QOS_ESDMODEOBJ (WSABASEERR + 1029) + /* invalid shape discard mode object in provider specific buffer */ +#define WSA_QOS_ESHAPERATEOBJ (WSABASEERR + 1030) + /* invalid shaping rate object in provider specific buffer */ +#define WSA_QOS_RESERVED_PETYPE (WSABASEERR + 1031) + /* reserved policy element in provider specific buffer */ + +#endif /* ifdef WSABASEERR */ diff --git a/hpsocket/HPSocket-SSL.h b/hpsocket/HPSocket-SSL.h new file mode 100644 index 0000000..966a1a1 --- /dev/null +++ b/hpsocket/HPSocket-SSL.h @@ -0,0 +1,338 @@ +/* + * 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.h" + +#ifdef _SSL_SUPPORT + +/*****************************************************************************************************************************************************/ +/******************************************************************** SSL Exports ********************************************************************/ +/*****************************************************************************************************************************************************/ + +/**************************************************/ +/************** HPSocket-SSL 导出函数 **************/ + +// 创建 SSL ITcpServer 对象 +HPSOCKET_API ITcpServer* HP_Create_SSLServer(ITcpServerListener* pListener); +// 创建 SSL ITcpAgent 对象 +HPSOCKET_API ITcpAgent* HP_Create_SSLAgent(ITcpAgentListener* pListener); +// 创建 SSL ITcpClient 对象 +HPSOCKET_API ITcpClient* HP_Create_SSLClient(ITcpClientListener* pListener); +// 创建 SSL ITcpPullServer 对象 +HPSOCKET_API ITcpPullServer* HP_Create_SSLPullServer(ITcpServerListener* pListener); +// 创建 SSL ITcpPullAgent 对象 +HPSOCKET_API ITcpPullAgent* HP_Create_SSLPullAgent(ITcpAgentListener* pListener); +// 创建 SSL ITcpPullClient 对象 +HPSOCKET_API ITcpPullClient* HP_Create_SSLPullClient(ITcpClientListener* pListener); +// 创建 SSL ITcpPackServer 对象 +HPSOCKET_API ITcpPackServer* HP_Create_SSLPackServer(ITcpServerListener* pListener); +// 创建 SSL ITcpPackAgent 对象 +HPSOCKET_API ITcpPackAgent* HP_Create_SSLPackAgent(ITcpAgentListener* pListener); +// 创建 SSL ITcpPackClient 对象 +HPSOCKET_API ITcpPackClient* HP_Create_SSLPackClient(ITcpClientListener* pListener); + +// 销毁 SSL ITcpServer 对象 +HPSOCKET_API void HP_Destroy_SSLServer(ITcpServer* pServer); +// 销毁 SSL ITcpAgent 对象 +HPSOCKET_API void HP_Destroy_SSLAgent(ITcpAgent* pAgent); +// 销毁 SSL ITcpClient 对象 +HPSOCKET_API void HP_Destroy_SSLClient(ITcpClient* pClient); +// 销毁 SSL ITcpPullServer 对象 +HPSOCKET_API void HP_Destroy_SSLPullServer(ITcpPullServer* pServer); +// 销毁 SSL ITcpPullAgent 对象 +HPSOCKET_API void HP_Destroy_SSLPullAgent(ITcpPullAgent* pAgent); +// 销毁 SSL ITcpPullClient 对象 +HPSOCKET_API void HP_Destroy_SSLPullClient(ITcpPullClient* pClient); +// 销毁 SSL ITcpPackServer 对象 +HPSOCKET_API void HP_Destroy_SSLPackServer(ITcpPackServer* pServer); +// 销毁 SSL ITcpPackAgent 对象 +HPSOCKET_API void HP_Destroy_SSLPackAgent(ITcpPackAgent* pAgent); +// 销毁 SSL ITcpPackClient 对象 +HPSOCKET_API void HP_Destroy_SSLPackClient(ITcpPackClient* pClient); + +// SSL ITcpServer 对象创建器 +struct SSLServer_Creator +{ + static ITcpServer* Create(ITcpServerListener* pListener) + { + return HP_Create_SSLServer(pListener); + } + + static void Destroy(ITcpServer* pServer) + { + HP_Destroy_SSLServer(pServer); + } +}; + +// SSL ITcpAgent 对象创建器 +struct SSLAgent_Creator +{ + static ITcpAgent* Create(ITcpAgentListener* pListener) + { + return HP_Create_SSLAgent(pListener); + } + + static void Destroy(ITcpAgent* pAgent) + { + HP_Destroy_SSLAgent(pAgent); + } +}; + +// SSL ITcpClient 对象创建器 +struct SSLClient_Creator +{ + static ITcpClient* Create(ITcpClientListener* pListener) + { + return HP_Create_SSLClient(pListener); + } + + static void Destroy(ITcpClient* pClient) + { + HP_Destroy_SSLClient(pClient); + } +}; + +// SSL ITcpPullServer 对象创建器 +struct SSLPullServer_Creator +{ + static ITcpPullServer* Create(ITcpServerListener* pListener) + { + return HP_Create_SSLPullServer(pListener); + } + + static void Destroy(ITcpPullServer* pServer) + { + HP_Destroy_SSLPullServer(pServer); + } +}; + +// SSL ITcpPullAgent 对象创建器 +struct SSLPullAgent_Creator +{ + static ITcpPullAgent* Create(ITcpAgentListener* pListener) + { + return HP_Create_SSLPullAgent(pListener); + } + + static void Destroy(ITcpPullAgent* pAgent) + { + HP_Destroy_SSLPullAgent(pAgent); + } +}; + +// SSL ITcpPullClient 对象创建器 +struct SSLPullClient_Creator +{ + static ITcpPullClient* Create(ITcpClientListener* pListener) + { + return HP_Create_SSLPullClient(pListener); + } + + static void Destroy(ITcpPullClient* pClient) + { + HP_Destroy_SSLPullClient(pClient); + } +}; + +// SSL ITcpPackServer 对象创建器 +struct SSLPackServer_Creator +{ + static ITcpPackServer* Create(ITcpServerListener* pListener) + { + return HP_Create_SSLPackServer(pListener); + } + + static void Destroy(ITcpPackServer* pServer) + { + HP_Destroy_SSLPackServer(pServer); + } +}; + +// SSL ITcpPackAgent 对象创建器 +struct SSLPackAgent_Creator +{ + static ITcpPackAgent* Create(ITcpAgentListener* pListener) + { + return HP_Create_SSLPackAgent(pListener); + } + + static void Destroy(ITcpPackAgent* pAgent) + { + HP_Destroy_SSLPackAgent(pAgent); + } +}; + +// SSL ITcpPackClient 对象创建器 +struct SSLPackClient_Creator +{ + static ITcpPackClient* Create(ITcpClientListener* pListener) + { + return HP_Create_SSLPackClient(pListener); + } + + static void Destroy(ITcpPackClient* pClient) + { + HP_Destroy_SSLPackClient(pClient); + } +}; + +// SSL ITcpServer 对象智能指针 +typedef CHPObjectPtr CSSLServerPtr; +// SSL ITcpAgent 对象智能指针 +typedef CHPObjectPtr CSSLAgentPtr; +// SSL ITcpClient 对象智能指针 +typedef CHPObjectPtr CSSLClientPtr; +// SSL ITcpPullServer 对象智能指针 +typedef CHPObjectPtr CSSLPullServerPtr; +// SSL ITcpPullAgent 对象智能指针 +typedef CHPObjectPtr CSSLPullAgentPtr; +// SSL ITcpPullClient 对象智能指针 +typedef CHPObjectPtr CSSLPullClientPtr; +// SSL ITcpPackServer 对象智能指针 +typedef CHPObjectPtr CSSLPackServerPtr; +// SSL ITcpPackAgent 对象智能指针 +typedef CHPObjectPtr CSSLPackAgentPtr; +// SSL ITcpPackClient 对象智能指针 +typedef CHPObjectPtr CSSLPackClientPtr; + +/*****************************************************************************************************************************************************/ +/*************************************************************** Global Function Exports *************************************************************/ +/*****************************************************************************************************************************************************/ + +/* +* 名称:SNI 默认回调函数 +* 描述:SSL Server 的 SetupSSLContext 方法中如果不指定 SNI 回调函数则使用此 SNI 默认回调函数 +* +* 参数: lpszServerName -- 请求域名 +* pContext -- SSL Context 对象 +* +* 返回值:SNI 主机证书对应的索引 +*/ +HPSOCKET_API int __HP_CALL HP_SSL_DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext); + +/* +* 名称:清理线程局部环境 SSL 资源 +* 描述:任何一个操作 SSL 的线程,通信结束时都需要清理线程局部环境 SSL 资源 +* 1、主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本方法 +* 2、特殊情况下,当自定义线程参与 HP-Socket 通信操作并检查到 SSL 内存泄漏时,需在每次通信结束时自定义线程调用本方法 +* +* 参数: dwThreadID -- 线程 ID(0:当前线程) +* +* 返回值:无 +*/ +HPSOCKET_API void HP_SSL_RemoveThreadLocalState(THR_ID dwThreadID); + +/*****************************************************************************************************************************************************/ +/******************************************************************** HTTPS Exports ******************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _HTTP_SUPPORT + +// 创建 IHttpsServer 对象 +HPSOCKET_API IHttpServer* HP_Create_HttpsServer(IHttpServerListener* pListener); +// 创建 IHttpsAgent 对象 +HPSOCKET_API IHttpAgent* HP_Create_HttpsAgent(IHttpAgentListener* pListener); +// 创建 IHttpsClient 对象 +HPSOCKET_API IHttpClient* HP_Create_HttpsClient(IHttpClientListener* pListener); +// 创建 IHttpsSyncClient 对象 +HPSOCKET_API IHttpSyncClient* HP_Create_HttpsSyncClient(IHttpClientListener* pListener = nullptr); + +// 销毁 IHttpsServer 对象 +HPSOCKET_API void HP_Destroy_HttpsServer(IHttpServer* pServer); +// 销毁 IHttpsAgent 对象 +HPSOCKET_API void HP_Destroy_HttpsAgent(IHttpAgent* pAgent); +// 销毁 IHttpsClient 对象 +HPSOCKET_API void HP_Destroy_HttpsClient(IHttpClient* pClient); +// 销毁 IHttpsSyncClient 对象 +HPSOCKET_API void HP_Destroy_HttpsSyncClient(IHttpSyncClient* pClient); + +// IHttpsServer 对象创建器 +struct HttpsServer_Creator +{ + static IHttpServer* Create(IHttpServerListener* pListener) + { + return HP_Create_HttpsServer(pListener); + } + + static void Destroy(IHttpServer* pServer) + { + HP_Destroy_HttpsServer(pServer); + } +}; + +// IHttpsAgent 对象创建器 +struct HttpsAgent_Creator +{ + static IHttpAgent* Create(IHttpAgentListener* pListener) + { + return HP_Create_HttpsAgent(pListener); + } + + static void Destroy(IHttpAgent* pAgent) + { + HP_Destroy_HttpsAgent(pAgent); + } +}; + +// IHttpsClient 对象创建器 +struct HttpsClient_Creator +{ + static IHttpClient* Create(IHttpClientListener* pListener) + { + return HP_Create_HttpsClient(pListener); + } + + static void Destroy(IHttpClient* pClient) + { + HP_Destroy_HttpsClient(pClient); + } +}; + +// IHttpsSyncClient 对象创建器 +struct HttpsSyncClient_Creator +{ + static IHttpSyncClient* Create(IHttpClientListener* pListener = nullptr) + { + return HP_Create_HttpsSyncClient(pListener); + } + + static void Destroy(IHttpSyncClient* pClient) + { + HP_Destroy_HttpsSyncClient(pClient); + } +}; + +// IHttpsServer 对象智能指针 +typedef CHPObjectPtr CHttpsServerPtr; +// IHttpsAgent 对象智能指针 +typedef CHPObjectPtr CHttpsAgentPtr; +// IHttpsClient 对象智能指针 +typedef CHPObjectPtr CHttpsClientPtr; +// IHttpsSyncClient 对象智能指针 +typedef CHPObjectPtr CHttpsSyncClientPtr; + +#endif + +#endif \ No newline at end of file diff --git a/hpsocket/HPSocket.h b/hpsocket/HPSocket.h new file mode 100644 index 0000000..4f96948 --- /dev/null +++ b/hpsocket/HPSocket.h @@ -0,0 +1,818 @@ +/* + * 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. + */ + +/****************************************************************************** +Module: HPSocket + +Usage: + 方法一: + -------------------------------------------------------------------------------------- + 0. 应用程序包含 HPTypeDef.h / SocketInterface.h / HPSocket.h 头文件 + 1. 调用 HP_Create_Xxx() 函数创建 HPSocket 对象 + 2. 使用完毕后调用 HP_Destroy_Xxx() 函数销毁 HPSocket 对象 + + 方法二: + -------------------------------------------------------------------------------------- + 0. 应用程序包含 SocketInterface.h 和 HPSocket.h 头文件 + 1. 创建 CXxxPtr 智能指针,通过智能指针使用 HPSocket 对象 + +Release: + <-- 动态链接库 --> + 1. x86/libhpsocket.so - (32位/MBCS/Release) + 2. x86/libhpsocket_d.so - (32位/MBCS/DeBug) + 3. x64/libhpsocket.so - (64位/MBCS/Release) + 4. x64/libhpsocket_d.so - (64位/MBCS/DeBug) + + <-- 静态链接库 --> + 1. x86/static/libhpsocket.a - (32位/MBCS/Release) + 2. x86/static/libhpsocket_d.a - (32位/MBCS/DeBug) + 3. x64/static/libhpsocket.a - (64位/MBCS/Release) + 4. x64/static/libhpsocket_d.a - (64位/MBCS/DeBug) + +******************************************************************************/ + +#pragma once + +#include "SocketInterface.h" + +/*****************************************************************************************************************************************************/ +/****************************************************************** TCP/UDP Exports ******************************************************************/ +/*****************************************************************************************************************************************************/ + +/**************************************************/ +/************** HPSocket 对象智能指针 **************/ + +template class CHPObjectPtr +{ +public: + CHPObjectPtr& Reset(T* pObj = nullptr) + { + if(pObj != m_pObj) + { + if(m_pObj) + _Creator::Destroy(m_pObj); + + m_pObj = pObj; + } + + return *this; + } + + CHPObjectPtr& Attach(T* pObj) + { + return Reset(pObj); + } + + T* Detach() + { + T* pObj = m_pObj; + m_pObj = nullptr; + + return pObj; + } + + BOOL IsValid () const {return m_pObj != nullptr ;} + T* Get () const {return m_pObj ;} + T* operator -> () const {return m_pObj ;} + operator T* () const {return m_pObj ;} + + CHPObjectPtr& operator = (T* pObj) {return Reset(pObj) ;} + +public: + CHPObjectPtr(_Listener* pListener = nullptr) + { + m_pObj = _Creator::Create(pListener); + } + + CHPObjectPtr(BOOL bCreate, _Listener* pListener = nullptr) + { + m_pObj = bCreate ? _Creator::Create(pListener) : nullptr; + } + + virtual ~CHPObjectPtr() + { + Reset(); + } + +private: + CHPObjectPtr(const CHPObjectPtr&) = delete; + CHPObjectPtr& operator = (const CHPObjectPtr&) = delete; + +protected: + T* m_pObj; +}; + +/**************************************************/ +/**************** HPSocket 导出函数 ****************/ + +// 创建 ITcpServer 对象 +HPSOCKET_API ITcpServer* HP_Create_TcpServer(ITcpServerListener* pListener); +// 创建 ITcpAgent 对象 +HPSOCKET_API ITcpAgent* HP_Create_TcpAgent(ITcpAgentListener* pListener); +// 创建 ITcpClient 对象 +HPSOCKET_API ITcpClient* HP_Create_TcpClient(ITcpClientListener* pListener); +// 创建 ITcpPullServer 对象 +HPSOCKET_API ITcpPullServer* HP_Create_TcpPullServer(ITcpServerListener* pListener); +// 创建 ITcpPullAgent 对象 +HPSOCKET_API ITcpPullAgent* HP_Create_TcpPullAgent(ITcpAgentListener* pListener); +// 创建 ITcpPullClient 对象 +HPSOCKET_API ITcpPullClient* HP_Create_TcpPullClient(ITcpClientListener* pListener); +// 创建 ITcpPackServer 对象 +HPSOCKET_API ITcpPackServer* HP_Create_TcpPackServer(ITcpServerListener* pListener); +// 创建 ITcpPackAgent 对象 +HPSOCKET_API ITcpPackAgent* HP_Create_TcpPackAgent(ITcpAgentListener* pListener); +// 创建 ITcpPackClient 对象 +HPSOCKET_API ITcpPackClient* HP_Create_TcpPackClient(ITcpClientListener* pListener); + +// 销毁 ITcpServer 对象 +HPSOCKET_API void HP_Destroy_TcpServer(ITcpServer* pServer); +// 销毁 ITcpAgent 对象 +HPSOCKET_API void HP_Destroy_TcpAgent(ITcpAgent* pAgent); +// 销毁 ITcpClient 对象 +HPSOCKET_API void HP_Destroy_TcpClient(ITcpClient* pClient); +// 销毁 ITcpPullServer 对象 +HPSOCKET_API void HP_Destroy_TcpPullServer(ITcpPullServer* pServer); +// 销毁 ITcpPullAgent 对象 +HPSOCKET_API void HP_Destroy_TcpPullAgent(ITcpPullAgent* pAgent); +// 销毁 ITcpPullClient 对象 +HPSOCKET_API void HP_Destroy_TcpPullClient(ITcpPullClient* pClient); +// 销毁 ITcpPackServer 对象 +HPSOCKET_API void HP_Destroy_TcpPackServer(ITcpPackServer* pServer); +// 销毁 ITcpPackAgent 对象 +HPSOCKET_API void HP_Destroy_TcpPackAgent(ITcpPackAgent* pAgent); +// 销毁 ITcpPackClient 对象 +HPSOCKET_API void HP_Destroy_TcpPackClient(ITcpPackClient* pClient); + +#ifdef _UDP_SUPPORT + +// 创建 IUdpServer 对象 +HPSOCKET_API IUdpServer* HP_Create_UdpServer(IUdpServerListener* pListener); +// 创建 IUdpClient 对象 +HPSOCKET_API IUdpClient* HP_Create_UdpClient(IUdpClientListener* pListener); +// 创建 IUdpCast 对象 +HPSOCKET_API IUdpCast* HP_Create_UdpCast(IUdpCastListener* pListener); +// 创建 IUdpNode 对象 +HPSOCKET_API IUdpNode* HP_Create_UdpNode(IUdpNodeListener* pListener); +// 创建 IUdpArqServer 对象 +HPSOCKET_API IUdpArqServer* HP_Create_UdpArqServer(IUdpServerListener* pListener); +// 创建 IUdpArqClient 对象 +HPSOCKET_API IUdpArqClient* HP_Create_UdpArqClient(IUdpClientListener* pListener); + +// 销毁 IUdpServer 对象 +HPSOCKET_API void HP_Destroy_UdpServer(IUdpServer* pServer); +// 销毁 IUdpClient 对象 +HPSOCKET_API void HP_Destroy_UdpClient(IUdpClient* pClient); +// 销毁 IUdpCast 对象 +HPSOCKET_API void HP_Destroy_UdpCast(IUdpCast* pCast); +// 销毁 IUdpNode 对象 +HPSOCKET_API void HP_Destroy_UdpNode(IUdpNode* pNode); +// 销毁 IUdpArqServer 对象 +HPSOCKET_API void HP_Destroy_UdpArqServer(IUdpArqServer* pServer); +// 销毁 IUdpArqClient 对象 +HPSOCKET_API void HP_Destroy_UdpArqClient(IUdpArqClient* pClient); + +#endif + +// ITcpServer 对象创建器 +struct TcpServer_Creator +{ + static ITcpServer* Create(ITcpServerListener* pListener) + { + return HP_Create_TcpServer(pListener); + } + + static void Destroy(ITcpServer* pServer) + { + HP_Destroy_TcpServer(pServer); + } +}; + +// ITcpAgent 对象创建器 +struct TcpAgent_Creator +{ + static ITcpAgent* Create(ITcpAgentListener* pListener) + { + return HP_Create_TcpAgent(pListener); + } + + static void Destroy(ITcpAgent* pAgent) + { + HP_Destroy_TcpAgent(pAgent); + } +}; + +// ITcpClient 对象创建器 +struct TcpClient_Creator +{ + static ITcpClient* Create(ITcpClientListener* pListener) + { + return HP_Create_TcpClient(pListener); + } + + static void Destroy(ITcpClient* pClient) + { + HP_Destroy_TcpClient(pClient); + } +}; + +// ITcpPullServer 对象创建器 +struct TcpPullServer_Creator +{ + static ITcpPullServer* Create(ITcpServerListener* pListener) + { + return HP_Create_TcpPullServer(pListener); + } + + static void Destroy(ITcpPullServer* pServer) + { + HP_Destroy_TcpPullServer(pServer); + } +}; + +// ITcpPullAgent 对象创建器 +struct TcpPullAgent_Creator +{ + static ITcpPullAgent* Create(ITcpAgentListener* pListener) + { + return HP_Create_TcpPullAgent(pListener); + } + + static void Destroy(ITcpPullAgent* pAgent) + { + HP_Destroy_TcpPullAgent(pAgent); + } +}; + +// ITcpPullClient 对象创建器 +struct TcpPullClient_Creator +{ + static ITcpPullClient* Create(ITcpClientListener* pListener) + { + return HP_Create_TcpPullClient(pListener); + } + + static void Destroy(ITcpPullClient* pClient) + { + HP_Destroy_TcpPullClient(pClient); + } +}; + +// ITcpPackServer 对象创建器 +struct TcpPackServer_Creator +{ + static ITcpPackServer* Create(ITcpServerListener* pListener) + { + return HP_Create_TcpPackServer(pListener); + } + + static void Destroy(ITcpPackServer* pServer) + { + HP_Destroy_TcpPackServer(pServer); + } +}; + +// ITcpPackAgent 对象创建器 +struct TcpPackAgent_Creator +{ + static ITcpPackAgent* Create(ITcpAgentListener* pListener) + { + return HP_Create_TcpPackAgent(pListener); + } + + static void Destroy(ITcpPackAgent* pAgent) + { + HP_Destroy_TcpPackAgent(pAgent); + } +}; + +// ITcpPackClient 对象创建器 +struct TcpPackClient_Creator +{ + static ITcpPackClient* Create(ITcpClientListener* pListener) + { + return HP_Create_TcpPackClient(pListener); + } + + static void Destroy(ITcpPackClient* pClient) + { + HP_Destroy_TcpPackClient(pClient); + } +}; + +// ITcpServer 对象智能指针 +typedef CHPObjectPtr CTcpServerPtr; +// ITcpAgent 对象智能指针 +typedef CHPObjectPtr CTcpAgentPtr; +// ITcpClient 对象智能指针 +typedef CHPObjectPtr CTcpClientPtr; +// ITcpPullServer 对象智能指针 +typedef CHPObjectPtr CTcpPullServerPtr; +// ITcpPullAgent 对象智能指针 +typedef CHPObjectPtr CTcpPullAgentPtr; +// ITcpPullClient 对象智能指针 +typedef CHPObjectPtr CTcpPullClientPtr; +// ITcpPackServer 对象智能指针 +typedef CHPObjectPtr CTcpPackServerPtr; +// ITcpPackAgent 对象智能指针 +typedef CHPObjectPtr CTcpPackAgentPtr; +// ITcpPackClient 对象智能指针 +typedef CHPObjectPtr CTcpPackClientPtr; + +#ifdef _UDP_SUPPORT + +// IUdpServer 对象创建器 +struct UdpServer_Creator +{ + static IUdpServer* Create(IUdpServerListener* pListener) + { + return HP_Create_UdpServer(pListener); + } + + static void Destroy(IUdpServer* pServer) + { + HP_Destroy_UdpServer(pServer); + } +}; + +// IUdpClient 对象创建器 +struct UdpClient_Creator +{ + static IUdpClient* Create(IUdpClientListener* pListener) + { + return HP_Create_UdpClient(pListener); + } + + static void Destroy(IUdpClient* pClient) + { + HP_Destroy_UdpClient(pClient); + } +}; + +// IUdpCast 对象创建器 +struct UdpCast_Creator +{ + static IUdpCast* Create(IUdpCastListener* pListener) + { + return HP_Create_UdpCast(pListener); + } + + static void Destroy(IUdpCast* pCast) + { + HP_Destroy_UdpCast(pCast); + } +}; + +// IUdpNode 对象创建器 +struct UdpNode_Creator +{ + static IUdpNode* Create(IUdpNodeListener* pListener) + { + return HP_Create_UdpNode(pListener); + } + + static void Destroy(IUdpNode* pNode) + { + HP_Destroy_UdpNode(pNode); + } +}; + +// IUdpArqServer 对象创建器 +struct UdpArqServer_Creator +{ + static IUdpArqServer* Create(IUdpServerListener* pListener) + { + return HP_Create_UdpArqServer(pListener); + } + + static void Destroy(IUdpArqServer* pServer) + { + HP_Destroy_UdpArqServer(pServer); + } +}; + +// IUdpArqClient 对象创建器 +struct UdpArqClient_Creator +{ + static IUdpArqClient* Create(IUdpClientListener* pListener) + { + return HP_Create_UdpArqClient(pListener); + } + + static void Destroy(IUdpArqClient* pClient) + { + HP_Destroy_UdpArqClient(pClient); + } +}; + +// IUdpServer 对象智能指针 +typedef CHPObjectPtr CUdpServerPtr; +// IUdpClient 对象智能指针 +typedef CHPObjectPtr CUdpClientPtr; +// IUdpCast 对象智能指针 +typedef CHPObjectPtr CUdpCastPtr; +// IUdpNode 对象智能指针 +typedef CHPObjectPtr CUdpNodePtr; +// IUdpArqServer 对象智能指针 +typedef CHPObjectPtr CUdpArqServerPtr; +// IUdpArqClient 对象智能指针 +typedef CHPObjectPtr CUdpArqClientPtr; + +#endif + +/*****************************************************************************************************************************************************/ +/*************************************************************** Global Function Exports *************************************************************/ +/*****************************************************************************************************************************************************/ + +// 获取 HPSocket 版本号(4 个字节分别为:主版本号,子版本号,修正版本号,构建编号) +HPSOCKET_API DWORD HP_GetHPSocketVersion(); + +// 获取错误描述文本 +HPSOCKET_API LPCTSTR HP_GetSocketErrorDesc(EnSocketError enCode); +// 调用系统的 errno 方法获取系统错误代码 +HPSOCKET_API DWORD SYS_GetLastError(); +// 调用系统的 strerror() 方法获取系统错误代码描述 +HPSOCKET_API LPCSTR SYS_GetLastErrorStr(); +// 调用系统的 setsockopt() +HPSOCKET_API int SYS_SetSocketOption(SOCKET sock, int level, int name, LPVOID val, int len); +// 调用系统的 getsockopt() +HPSOCKET_API int SYS_GetSocketOption(SOCKET sock, int level, int name, LPVOID val, int* len); +// 调用系统的 ioctlsocket() +HPSOCKET_API int SYS_IoctlSocket(SOCKET sock, long cmd, ULONG* arg); + +// 调用系统的 fcntl() 设置 F_SETFL 属性 +HPSOCKET_API BOOL SYS_fcntl_SETFL(FD fd, INT fl, BOOL bSet = TRUE); + +// 设置 FD 选项:O_NONBLOCK +HPSOCKET_API int SYS_SSO_NoBlock(SOCKET sock, BOOL bNoBlock = TRUE); +// 设置 socket 选项:IPPROTO_TCP -> TCP_NODELAY +HPSOCKET_API int SYS_SSO_NoDelay(SOCKET sock, BOOL bNoDelay = TRUE); +// 设置 socket 选项:SOL_SOCKET -> SO_DONTLINGER +HPSOCKET_API int SYS_SSO_DontLinger(SOCKET sock, BOOL bDont = TRUE); +// 设置 socket 选项:SOL_SOCKET -> SO_LINGER +HPSOCKET_API int SYS_SSO_Linger(SOCKET sock, USHORT l_onoff, USHORT l_linger); +// 设置 socket 选项:SOL_SOCKET -> SO_RCVBUF +HPSOCKET_API int SYS_SSO_RecvBuffSize(SOCKET sock, int size); +// 设置 socket 选项:SOL_SOCKET -> SO_SNDBUF +HPSOCKET_API int SYS_SSO_SendBuffSize(SOCKET sock, int size); +// 设置 socket 选项:SOL_SOCKET -> SO_RCVTIMEO +HPSOCKET_API int SYS_SSO_RecvTimeOut(SOCKET sock, int ms); +// 设置 socket 选项:SOL_SOCKET -> SO_SNDTIMEO +HPSOCKET_API int SYS_SSO_SendTimeOut(SOCKET sock, int ms); +// 设置 socket 选项:SOL_SOCKET -> SO_REUSEADDR / SO_REUSEPORT +HPSOCKET_API int SYS_SSO_ReuseAddress(SOCKET sock, EnReuseAddressPolicy opt); + +// 获取 SOCKET 本地地址信息 +HPSOCKET_API BOOL SYS_GetSocketLocalAddress(SOCKET socket, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort); +// 获取 SOCKET 远程地址信息 +HPSOCKET_API BOOL SYS_GetSocketRemoteAddress(SOCKET socket, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort); + +/* 枚举主机 IP 地址 */ +HPSOCKET_API BOOL SYS_EnumHostIPAddresses(LPCTSTR lpszHost, EnIPAddrType enType, LPTIPAddr** lpppIPAddr, int& iIPAddrCount); +/* 释放 LPTIPAddr* */ +HPSOCKET_API BOOL SYS_FreeHostIPAddresses(LPTIPAddr* lppIPAddr); +/* 检查字符串是否符合 IP 地址格式 */ +HPSOCKET_API BOOL SYS_IsIPAddress(LPCTSTR lpszAddress, EnIPAddrType* penType = nullptr); +/* 通过主机名获取 IP 地址 */ +HPSOCKET_API BOOL SYS_GetIPAddress(LPCTSTR lpszHost, TCHAR lpszIP[], int& iIPLenth, EnIPAddrType& enType); + +/* 64 位网络字节序转主机字节序 */ +HPSOCKET_API ULONGLONG SYS_NToH64(ULONGLONG value); +/* 64 位主机字节序转网络字节序 */ +HPSOCKET_API ULONGLONG SYS_HToN64(ULONGLONG value); +/* 短整型高低字节交换 */ +HPSOCKET_API USHORT SYS_SwapEndian16(USHORT value); +/* 长整型高低字节交换 */ +HPSOCKET_API DWORD SYS_SwapEndian32(DWORD value); +/* 检查是否小端字节序 */ +HPSOCKET_API BOOL SYS_IsLittleEndian(); + +/* 分配内存 */ +HPSOCKET_API LPBYTE SYS_Malloc(int size); +/* 重新分配内存 */ +HPSOCKET_API LPBYTE SYS_Realloc(LPBYTE p, int size); +/* 释放内存 */ +HPSOCKET_API VOID SYS_Free(LPBYTE p); +/* 分配内存块 */ +HPSOCKET_API LPVOID SYS_Calloc(int number, int size); + +// 计算 Base64 编码后长度 +HPSOCKET_API DWORD SYS_GuessBase64EncodeBound(DWORD dwSrcLen); +// 计算 Base64 解码后长度 +HPSOCKET_API DWORD SYS_GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen); +// Base64 编码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int SYS_Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); +// Base64 解码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int SYS_Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); + +// 计算 URL 编码后长度 +HPSOCKET_API DWORD SYS_GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen); +// 计算 URL 解码后长度 +HPSOCKET_API DWORD SYS_GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen); +// URL 编码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int SYS_UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); +// URL 解码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int SYS_UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); + +#ifdef _ZLIB_SUPPORT + +// 普通压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +// (默认参数:iLevel -> -1,iMethod -> 8,iWindowBits -> 15,iMemLevel -> 8,iStrategy -> 0) +HPSOCKET_API int SYS_Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); +// 高级压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +//(默认参数:iLevel -> -1,iMethod -> 8,iWindowBits -> 15,iMemLevel -> 8,iStrategy -> 0) +HPSOCKET_API int SYS_CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel = -1, int iMethod = 8, int iWindowBits = 15, int iMemLevel = 8, int iStrategy = 0); +// 普通解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +//(默认参数:iWindowBits -> 15) +HPSOCKET_API int SYS_Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); +// 高级解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +//(默认参数:iWindowBits -> 15) +HPSOCKET_API int SYS_UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits = 15); +// 推测压缩结果长度 +HPSOCKET_API DWORD SYS_GuessCompressBound(DWORD dwSrcLen, BOOL bGZip = FALSE); + +// Gzip 压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int SYS_GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); +// Gzip 解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int SYS_GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); +// 推测 Gzip 解压结果长度(如果返回 0 或不合理值则说明输入内容并非有效的 Gzip 格式) +HPSOCKET_API DWORD SYS_GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen); + +#endif + +#ifdef _BROTLI_SUPPORT + +// Brotli 压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +// (默认参数:iQuality -> 11,iWindow -> 22,iMode -> 0) +HPSOCKET_API int SYS_BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); +// Brotli 高级压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +//(默认参数:iQuality -> 11,iWindow -> 22,iMode -> 0) +HPSOCKET_API int SYS_BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iQuality = 11, int iWindow = 22, int iMode = 0); +// Brotli 解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int SYS_BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen); +// Brotli 推测压缩结果长度 +HPSOCKET_API DWORD SYS_BrotliGuessCompressBound(DWORD dwSrcLen); + +#endif + +#ifdef _ICONV_SUPPORT + +// Charset A -> Charset B +HPSOCKET_API BOOL SYS_CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int& iOutBufLen); + +// GBK -> UNICODE +HPSOCKET_API BOOL SYS_GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength); +// UNICODE -> GBK +HPSOCKET_API BOOL SYS_UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength); +// UTF8 -> UNICODE +HPSOCKET_API BOOL SYS_Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength); +// UNICODE -> UTF8 +HPSOCKET_API BOOL SYS_UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength); +// GBK -> UTF8 +HPSOCKET_API BOOL SYS_GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength); +// UTF8 -> GBK +HPSOCKET_API BOOL SYS_Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength); + +// GBK -> UNICODE +HPSOCKET_API BOOL SYS_GbkToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength); +// UNICODE -> GBK +HPSOCKET_API BOOL SYS_UnicodeToGbk(const WCHAR szSrc[], char szDest[], int& iDestLength); +// UTF8 -> UNICODE +HPSOCKET_API BOOL SYS_Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength); +// UNICODE -> UTF8 +HPSOCKET_API BOOL SYS_UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int& iDestLength); +// GBK -> UTF8 +HPSOCKET_API BOOL SYS_GbkToUtf8(const char szSrc[], char szDest[], int& iDestLength); +// UTF8 -> GBK +HPSOCKET_API BOOL SYS_Utf8ToGbk(const char szSrc[], char szDest[], int& iDestLength); + +#endif + +/*****************************************************************************************************************************************************/ +/******************************************************************** HTTP Exports *******************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _HTTP_SUPPORT + +// 创建 IHttpServer 对象 +HPSOCKET_API IHttpServer* HP_Create_HttpServer(IHttpServerListener* pListener); +// 创建 IHttpAgent 对象 +HPSOCKET_API IHttpAgent* HP_Create_HttpAgent(IHttpAgentListener* pListener); +// 创建 IHttpClient 对象 +HPSOCKET_API IHttpClient* HP_Create_HttpClient(IHttpClientListener* pListener); +// 创建 IHttpSyncClient 对象 +HPSOCKET_API IHttpSyncClient* HP_Create_HttpSyncClient(IHttpClientListener* pListener = nullptr); + +// 销毁 IHttpServer 对象 +HPSOCKET_API void HP_Destroy_HttpServer(IHttpServer* pServer); +// 销毁 IHttpAgent 对象 +HPSOCKET_API void HP_Destroy_HttpAgent(IHttpAgent* pAgent); +// 销毁 IHttpClient 对象 +HPSOCKET_API void HP_Destroy_HttpClient(IHttpClient* pClient); +// 销毁 IHttpSyncClient 对象 +HPSOCKET_API void HP_Destroy_HttpSyncClient(IHttpSyncClient* pClient); + +// IHttpServer 对象创建器 +struct HttpServer_Creator +{ + static IHttpServer* Create(IHttpServerListener* pListener) + { + return HP_Create_HttpServer(pListener); + } + + static void Destroy(IHttpServer* pServer) + { + HP_Destroy_HttpServer(pServer); + } +}; + +// IHttpAgent 对象创建器 +struct HttpAgent_Creator +{ + static IHttpAgent* Create(IHttpAgentListener* pListener) + { + return HP_Create_HttpAgent(pListener); + } + + static void Destroy(IHttpAgent* pAgent) + { + HP_Destroy_HttpAgent(pAgent); + } +}; + +// IHttpClient 对象创建器 +struct HttpClient_Creator +{ + static IHttpClient* Create(IHttpClientListener* pListener) + { + return HP_Create_HttpClient(pListener); + } + + static void Destroy(IHttpClient* pClient) + { + HP_Destroy_HttpClient(pClient); + } +}; + +// IHttpSyncClient 对象创建器 +struct HttpSyncClient_Creator +{ + static IHttpSyncClient* Create(IHttpClientListener* pListener = nullptr) + { + return HP_Create_HttpSyncClient(pListener); + } + + static void Destroy(IHttpSyncClient* pClient) + { + HP_Destroy_HttpSyncClient(pClient); + } +}; + +// IHttpServer 对象智能指针 +typedef CHPObjectPtr CHttpServerPtr; +// IHttpAgent 对象智能指针 +typedef CHPObjectPtr CHttpAgentPtr; +// IHttpClient 对象智能指针 +typedef CHPObjectPtr CHttpClientPtr; +// IHttpSyncClient 对象智能指针 +typedef CHPObjectPtr CHttpSyncClientPtr; + +/**************************************************************************/ +/*************************** HTTP Cookie 管理方法 **************************/ + +/* 从文件加载 Cookie */ +HPSOCKET_API BOOL HP_HttpCookie_MGR_LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists = TRUE); +/* 保存 Cookie 到文件 */ +HPSOCKET_API BOOL HP_HttpCookie_MGR_SaveToFile(LPCSTR lpszFile, BOOL bKeepExists = TRUE); +/* 清理 Cookie */ +HPSOCKET_API BOOL HP_HttpCookie_MGR_ClearCookies(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr); +/* 清理过期 Cookie */ +HPSOCKET_API BOOL HP_HttpCookie_MGR_RemoveExpiredCookies(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr); +/* 设置 Cookie */ +HPSOCKET_API BOOL HP_HttpCookie_MGR_SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, int enSameSite = 0, BOOL bOnlyUpdateValueIfExists = TRUE); +/* 删除 Cookie */ +HPSOCKET_API BOOL HP_HttpCookie_MGR_DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName); +/* 设置是否允许第三方 Cookie */ +HPSOCKET_API void HP_HttpCookie_MGR_SetEnableThirdPartyCookie(BOOL bEnableThirdPartyCookie = TRUE); +/* 检查是否允许第三方 Cookie */ +HPSOCKET_API BOOL HP_HttpCookie_MGR_IsEnableThirdPartyCookie(); + +/* Cookie expires 字符串转换为整数 */ +HPSOCKET_API BOOL HP_HttpCookie_HLP_ParseExpires(LPCSTR lpszExpires, __time64_t& tmExpires); +/* 整数转换为 Cookie expires 字符串 */ +HPSOCKET_API BOOL HP_HttpCookie_HLP_MakeExpiresStr(char lpszBuff[], int& iBuffLen, __time64_t tmExpires); +/* 生成 Cookie 字符串 */ +HPSOCKET_API BOOL HP_HttpCookie_HLP_ToString(char lpszBuff[], int& iBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge /*= -1*/, BOOL bHttpOnly /*= FALSE*/, BOOL bSecure /*= FALSE*/, int enSameSite /*= 0*/); +/* 获取当前 UTC 时间 */ +HPSOCKET_API __time64_t HP_HttpCookie_HLP_CurrentUTCTime(); +/* Max-Age -> expires */ +HPSOCKET_API __time64_t HP_HttpCookie_HLP_MaxAgeToExpires(int iMaxAge); +/* expires -> Max-Age */ +HPSOCKET_API int HP_HttpCookie_HLP_ExpiresToMaxAge(__time64_t tmExpires); + +/*****************************************************************************************************************************************************/ +/************************************************************* HTTP Global Function Exports **********************************************************/ +/*****************************************************************************************************************************************************/ + +#endif + +/*****************************************************************************************************************************************************/ +/**************************************************************** Thread Pool Exports ****************************************************************/ +/*****************************************************************************************************************************************************/ + +// 创建 IHPThreadPool 对象 +HPSOCKET_API IHPThreadPool* HP_Create_ThreadPool(IHPThreadPoolListener* pListener = nullptr); +// 销毁 IHPThreadPool 对象 +HPSOCKET_API void HP_Destroy_ThreadPool(IHPThreadPool* pThreadPool); + +/* +* 名称:创建 TSocketTask 对象 +* 描述:创建任务对象,该对象最终需由 HP_Destroy_SocketTaskObj() 销毁 +* +* 参数: fnTaskProc -- 任务处理函数 +* pSender -- 发起对象 +* dwConnID -- 连接 ID +* pBuffer -- 数据缓冲区 +* iBuffLen -- 数据缓冲区长度 +* enBuffType -- 数据缓冲区类型(默认:TBT_COPY) +* TBT_COPY :(深拷贝)pBuffer 复制到 TSocketTask 对象。此后 TSocketTask 对象与 pBuffer 不再有任何关联 +* -> 适用于 pBuffer 不大或 pBuffer 生命周期不受控的场景 +* TBT_REFER :(浅拷贝)pBuffer 不复制到 TSocketTask 对象,需确保 TSocketTask 对象生命周期内 pBuffer 必须有效 +* -> 适用于 pBuffer 较大或 pBuffer 可重用,并且 pBuffer 生命周期受控的场景 +* TBT_ATTACH :(附属)执行浅拷贝,但 TSocketTask 对象会获得 pBuffer 的所有权,并负责释放 pBuffer,避免多次缓冲区拷贝 +* -> 注意:pBuffer 必须由 SYS_Malloc()/SYS_Calloc() 函数分配才能使用本类型,否则可能会发生内存访问错误 +* wParam -- 自定义参数 +* lParam -- 自定义参数 +* 返回值: LPTSocketTask +*/ +HPSOCKET_API LPTSocketTask HP_Create_SocketTaskObj(Fn_SocketTaskProc fnTaskProc, PVOID pSender, CONNID dwConnID, LPCBYTE pBuffer, INT iBuffLen, EnTaskBufferType enBuffType = TBT_COPY, WPARAM wParam = 0, LPARAM lParam = 0); + +// 销毁 TSocketTask 对象 +HPSOCKET_API void HP_Destroy_SocketTaskObj(LPTSocketTask pTask); + +// IHPThreadPool 对象创建器 +struct HPThreadPool_Creator +{ + static IHPThreadPool* Create(IHPThreadPoolListener* pListener = nullptr) + { + return HP_Create_ThreadPool(pListener); + } + + static void Destroy(IHPThreadPool* pThreadPool) + { + HP_Destroy_ThreadPool(pThreadPool); + } +}; + +// IHPThreadPool 对象智能指针 +typedef CHPObjectPtr CHPThreadPoolPtr; + +/*****************************************************************************************************************************************************/ +/********************************************************* Compressor / Decompressor Exports *********************************************************/ +/*****************************************************************************************************************************************************/ + +/* 销毁压缩器对象 */ +HPSOCKET_API void HP_Destroy_Compressor(IHPCompressor* pCompressor); +/* 销毁解压器对象 */ +HPSOCKET_API void HP_Destroy_Decompressor(IHPDecompressor* pDecompressor); + +#ifdef _ZLIB_SUPPORT + +/* 创建 ZLib 压缩器对象 */ +HPSOCKET_API IHPCompressor* HP_Create_ZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits = 15, int iLevel = -1, int iMethod = 8, int iMemLevel = 8, int iStrategy = 0, DWORD dwBuffSize = 16 * 1024); +/* 创建 GZip 压缩器对象 */ +HPSOCKET_API IHPCompressor* HP_Create_GZipCompressor(Fn_CompressDataCallback fnCallback, int iLevel = -1, int iMethod = 8, int iMemLevel = 8, int iStrategy = 0, DWORD dwBuffSize = 16 * 1024); +/* 创建 ZLib 解压器对象 */ +HPSOCKET_API IHPDecompressor* HP_Create_ZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits = 15, DWORD dwBuffSize = 16 * 1024); +/* 创建 GZip 解压器对象 */ +HPSOCKET_API IHPDecompressor* HP_Create_GZipDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize = 16 * 1024); + +#endif + +#ifdef _BROTLI_SUPPORT + +/* 创建 Brotli 压缩器对象 */ +HPSOCKET_API IHPCompressor* HP_Create_BrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality = 11, int iWindow = 22, int iMode = 0, DWORD dwBuffSize = 16 * 1024); +/* 创建 Brotli 解压器对象 */ +HPSOCKET_API IHPDecompressor* HP_Create_BrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize = 16 * 1024); + +#endif diff --git a/hpsocket/HPSocket4C-SSL.h b/hpsocket/HPSocket4C-SSL.h new file mode 100644 index 0000000..2dd9e24 --- /dev/null +++ b/hpsocket/HPSocket4C-SSL.h @@ -0,0 +1,411 @@ +/* + * 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 "HPSocket4C.h" + +#ifdef _SSL_SUPPORT + +/************************************************************************ +名称:定义 SSL Socket 对象指针类型别名 +描述:把 SSL Socket 对象指针定义为更直观的别名 +************************************************************************/ + +typedef HP_Object HP_SSLServer; +typedef HP_Object HP_SSLAgent; +typedef HP_Object HP_SSLClient; +typedef HP_Object HP_SSLPullServer; +typedef HP_Object HP_SSLPullAgent; +typedef HP_Object HP_SSLPullClient; +typedef HP_Object HP_SSLPackServer; +typedef HP_Object HP_SSLPackAgent; +typedef HP_Object HP_SSLPackClient; + +typedef HP_Object HP_HttpsServer; +typedef HP_Object HP_HttpsAgent; +typedef HP_Object HP_HttpsClient; +typedef HP_Object HP_HttpsSyncClient; + +/*****************************************************************************************************************************************************/ +/******************************************************************** SSL Exports ********************************************************************/ +/*****************************************************************************************************************************************************/ + +/********************************************************/ +/************** HPSocket4C-SSL 对象创建函数 **************/ + +// 创建 HP_SSLServer 对象 +HPSOCKET_API HP_SSLServer __HP_CALL Create_HP_SSLServer(HP_TcpServerListener pListener); +// 创建 HP_SSLAgent 对象 +HPSOCKET_API HP_SSLAgent __HP_CALL Create_HP_SSLAgent(HP_TcpAgentListener pListener); +// 创建 HP_SSLClient 对象 +HPSOCKET_API HP_SSLClient __HP_CALL Create_HP_SSLClient(HP_TcpClientListener pListener); +// 创建 HP_SSLPullServer 对象 +HPSOCKET_API HP_SSLPullServer __HP_CALL Create_HP_SSLPullServer(HP_TcpPullServerListener pListener); +// 创建 HP_SSLPullAgent 对象 +HPSOCKET_API HP_SSLPullAgent __HP_CALL Create_HP_SSLPullAgent(HP_TcpPullAgentListener pListener); +// 创建 HP_SSLPullClient 对象 +HPSOCKET_API HP_SSLPullClient __HP_CALL Create_HP_SSLPullClient(HP_TcpPullClientListener pListener); +// 创建 HP_SSLPackServer 对象 +HPSOCKET_API HP_SSLPackServer __HP_CALL Create_HP_SSLPackServer(HP_TcpServerListener pListener); +// 创建 HP_SSLPackAgent 对象 +HPSOCKET_API HP_SSLPackAgent __HP_CALL Create_HP_SSLPackAgent(HP_TcpAgentListener pListener); +// 创建 HP_SSLPackClient 对象 +HPSOCKET_API HP_SSLPackClient __HP_CALL Create_HP_SSLPackClient(HP_TcpClientListener pListener); + +// 销毁 HP_SSLServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLServer(HP_SSLServer pServer); +// 销毁 HP_SSLAgent 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLAgent(HP_SSLAgent pAgent); +// 销毁 HP_SSLClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLClient(HP_SSLClient pClient); +// 销毁 HP_SSLPullServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullServer(HP_SSLPullServer pServer); +// 销毁 HP_SSLPullAgent 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullAgent(HP_SSLPullAgent pAgent); +// 销毁 HP_SSLPullClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullClient(HP_SSLPullClient pClient); +// 销毁 HP_SSLPackServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackServer(HP_SSLPackServer pServer); +// 销毁 HP_SSLPackAgent 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackAgent(HP_SSLPackAgent pAgent); +// 销毁 HP_SSLPackClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackClient(HP_SSLPackClient pClient); + +/*****************************************************************************************************************************************************/ +/*************************************************************** Global Function Exports *************************************************************/ +/*****************************************************************************************************************************************************/ + +/***************************************************************************************/ +/************************************ SSL 初始化方法 ************************************/ + +/* +* 名称:SNI 默认回调函数 +* 描述:HP_SSLServer_SetupSSLContext 方法中如果不指定 SNI 回调函数则使用此 SNI 默认回调函数 +* +* 参数: lpszServerName -- 请求域名 +* pContext -- SSL Context 对象 +* +* 返回值:SNI 主机证书对应的索引 +*/ +HPSOCKET_API int __HP_CALL HP_SSL_DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext); + +/* +* 名称:清理线程局部环境 SSL 资源 +* 描述:任何一个操作 SSL 的线程,通信结束时都需要清理线程局部环境 SSL 资源 +* 1、主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本方法 +* 2、特殊情况下,当自定义线程参与 HP-Socket 通信操作并检查到 SSL 内存泄漏时,需在每次通信结束时自定义线程调用本方法 +* +* 参数: dwThreadID -- 线程 ID(0:当前线程) +* +* 返回值:无 +*/ +HPSOCKET_API void __HP_CALL HP_SSL_RemoveThreadLocalState(THR_ID dwThreadID); + +/* +* 名称:初始化通信组件 SSL 环境参数 +* 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 +* +* 参数: iVerifyMode -- SSL 验证模式(参考 En_HP_SSLVerifyMode) +* lpszPemCertFile -- 证书文件 +* lpszPemKeyFile -- 私钥文件 +* lpszKeyPassword -- 私钥密码(没有密码则为空) +* lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证或客户端可选) +* fnServerNameCallback -- SNI 回调函数指针(可选,如果为 nullptr 则使用 SNI 默认回调函数) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLServer_SetupSSLContext(HP_SSLServer pServer, int iVerifyMode /* SSL_VM_NONE */, LPCTSTR lpszPemCertFile /* nullptr */, LPCTSTR lpszPemKeyFile /* nullptr */, LPCTSTR lpszKeyPassword /* nullptr */, LPCTSTR lpszCAPemCertFileOrPath /* nullptr */, HP_Fn_SNI_ServerNameCallback fnServerNameCallback /* nullptr */); + +/* +* 名称:初始化通信组件 SSL 环境参数(通过内存加载证书) +* 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 +* +* 参数: iVerifyMode -- SSL 验证模式(参考 En_HP_SSLVerifyMode) +* lpszPemCert -- 证书内容 +* lpszPemKey -- 私钥内容 +* lpszKeyPassword -- 私钥密码(没有密码则为空) +* lpszCAPemCert -- CA 证书内容(单向验证或客户端可选) +* fnServerNameCallback -- SNI 回调函数指针(可选,如果为 nullptr 则使用 SNI 默认回调函数) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLServer_SetupSSLContextByMemory(HP_SSLServer pServer, int iVerifyMode /* SSL_VM_NONE */, LPCSTR lpszPemCert /* nullptr */, LPCSTR lpszPemKey /* nullptr */, LPCSTR lpszKeyPassword /* nullptr */, LPCSTR lpszCAPemCert /* nullptr */, HP_Fn_SNI_ServerNameCallback fnServerNameCallback /* nullptr */); + +/* +* 名称:增加 SNI 主机证书 +* 描述:SSL 服务端在 SetupSSLContext() 成功后可以调用本方法增加多个 SNI 主机证书 +* +* 参数: iVerifyMode -- SSL 验证模式(参考 En_HP_SSLVerifyMode) +* lpszPemCertFile -- 证书文件 +* lpszPemKeyFile -- 私钥文件 +* lpszKeyPassword -- 私钥密码(没有密码则为空) +* lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证可选) +* +* 返回值: 正数 -- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机 +* 负数 -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API int __HP_CALL HP_SSLServer_AddSSLContext(HP_SSLServer pServer, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword /* nullptr */, LPCTSTR lpszCAPemCertFileOrPath /* nullptr */); + +/* +* 名称:增加 SNI 主机证书(通过内存加载证书) +* 描述:SSL 服务端在 SetupSSLContext() 成功后可以调用本方法增加多个 SNI 主机证书 +* +* 参数: iVerifyMode -- SSL 验证模式(参考 En_HP_SSLVerifyMode) +* lpszPemCert -- 证书内容 +* lpszPemKey -- 私钥内容 +* lpszKeyPassword -- 私钥密码(没有密码则为空) +* lpszCAPemCert -- CA 证书内容(单向验证可选) +* +* 返回值: 正数 -- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机 +* 负数 -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API int __HP_CALL HP_SSLServer_AddSSLContextByMemory(HP_SSLServer pServer, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword /* nullptr */, LPCSTR lpszCAPemCert /* nullptr */); + +/* +* 名称:绑定 SNI 主机域名 +* 描述:SSL 服务端在 AddSSLContext() 成功后可以调用本方法绑定主机域名到 SNI 主机证书 +* +* 参数: lpszServerName -- 主机域名 +* iContextIndex -- SNI 主机证书对应的索引 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLServer_BindSSLServerName(HP_SSLServer pServer, LPCTSTR lpszServerName, int iContextIndex); + +/* +* 名称:清理通信组件 SSL 运行环境 +* 描述:清理通信组件 SSL 运行环境,回收 SSL 相关内存 +* 1、通信组件析构时会自动调用本方法 +* 2、当要重新设置通信组件 SSL 环境参数时,需要先调用本方法清理原先的环境参数 +* +* 参数: 无 +* +* 返回值:无 +*/ +HPSOCKET_API void __HP_CALL HP_SSLServer_CleanupSSLContext(HP_SSLServer pServer); + +/* +* 名称:初始化通信组件 SSL 环境参数 +* 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 +* +* 参数: iVerifyMode -- SSL 验证模式(参考 En_HP_SSLVerifyMode) +* lpszPemCertFile -- 证书文件(客户端可选) +* lpszPemKeyFile -- 私钥文件(客户端可选) +* lpszKeyPassword -- 私钥密码(没有密码则为空) +* lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证或客户端可选) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_SetupSSLContext(HP_SSLAgent pAgent, int iVerifyMode /* SSL_VM_NONE */, LPCTSTR lpszPemCertFile /* nullptr */, LPCTSTR lpszPemKeyFile /* nullptr */, LPCTSTR lpszKeyPassword /* nullptr */, LPCTSTR lpszCAPemCertFileOrPath /* nullptr */); + +/* +* 名称:初始化通信组件 SSL 环境参数(通过内存加载证书) +* 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 +* +* 参数: iVerifyMode -- SSL 验证模式(参考 En_HP_SSLVerifyMode) +* lpszPemCert -- 证书内容 +* lpszPemKey -- 私钥内容 +* lpszKeyPassword -- 私钥密码(没有密码则为空) +* lpszCAPemCert -- CA 证书内容(单向验证或客户端可选) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_SetupSSLContextByMemory(HP_SSLAgent pAgent, int iVerifyMode /* SSL_VM_NONE */, LPCSTR lpszPemCert /* nullptr */, LPCSTR lpszPemKey /* nullptr */, LPCSTR lpszKeyPassword /* nullptr */, LPCSTR lpszCAPemCert /* nullptr */); + +/* +* 名称:清理通信组件 SSL 运行环境 +* 描述:清理通信组件 SSL 运行环境,回收 SSL 相关内存 +* 1、通信组件析构时会自动调用本方法 +* 2、当要重新设置通信组件 SSL 环境参数时,需要先调用本方法清理原先的环境参数 +* +* 参数: 无 +* +* 返回值:无 +*/ +HPSOCKET_API void __HP_CALL HP_SSLAgent_CleanupSSLContext(HP_SSLAgent pAgent); + +/* +* 名称:初始化通信组件 SSL 环境参数 +* 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 +* +* 参数: iVerifyMode -- SSL 验证模式(参考 En_HP_SSLVerifyMode) +* lpszPemCertFile -- 证书文件(客户端可选) +* lpszPemKeyFile -- 私钥文件(客户端可选) +* lpszKeyPassword -- 私钥密码(没有密码则为空) +* lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证或客户端可选) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLClient_SetupSSLContext(HP_SSLClient pClient, int iVerifyMode /* SSL_VM_NONE */, LPCTSTR lpszPemCertFile /* nullptr */, LPCTSTR lpszPemKeyFile /* nullptr */, LPCTSTR lpszKeyPassword /* nullptr */, LPCTSTR lpszCAPemCertFileOrPath /* nullptr */); + +/* +* 名称:初始化通信组件 SSL 环境参数(通过内存加载证书) +* 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 +* +* 参数: iVerifyMode -- SSL 验证模式(参考 En_HP_SSLVerifyMode) +* lpszPemCert -- 证书内容 +* lpszPemKey -- 私钥内容 +* lpszKeyPassword -- 私钥密码(没有密码则为空) +* lpszCAPemCert -- CA 证书内容(单向验证或客户端可选) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLClient_SetupSSLContextByMemory(HP_SSLClient pClient, int iVerifyMode /* SSL_VM_NONE */, LPCSTR lpszPemCert /* nullptr */, LPCSTR lpszPemKey /* nullptr */, LPCSTR lpszKeyPassword /* nullptr */, LPCSTR lpszCAPemCert /* nullptr */); + +/* +* 名称:清理通信组件 SSL 运行环境 +* 描述:清理通信组件 SSL 运行环境,回收 SSL 相关内存 +* 1、通信组件析构时会自动调用本方法 +* 2、当要重新设置通信组件 SSL 环境参数时,需要先调用本方法清理原先的环境参数 +* +* 参数: 无 +* +* 返回值:无 +*/ +HPSOCKET_API void __HP_CALL HP_SSLClient_CleanupSSLContext(HP_SSLClient pClient); + +/***************************************************************************************/ +/************************************* SSL 操作方法 ************************************/ + +/* +* 名称:启动 SSL 握手 +* 描述:当通信组件设置为非自动握手时,需要调用本方法启动 SSL 握手 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLServer_StartSSLHandShake(HP_SSLServer pServer, HP_CONNID dwConnID); + +/* 设置通信组件握手方式(默认:TRUE,自动握手) */ +HPSOCKET_API void __HP_CALL HP_SSLServer_SetSSLAutoHandShake(HP_SSLServer pServer, BOOL bAutoHandShake); +/* 获取通信组件握手方式 */ +HPSOCKET_API BOOL __HP_CALL HP_SSLServer_IsSSLAutoHandShake(HP_SSLServer pServer); + +/* 设置 SSL 加密算法列表 */ +HPSOCKET_API void __HP_CALL HP_SSLServer_SetSSLCipherList(HP_SSLServer pServer, LPCTSTR lpszCipherList); +/* 获取 SSL 加密算法列表 */ +HPSOCKET_API LPCTSTR __HP_CALL HP_SSLServer_GetSSLCipherList(HP_SSLServer pServer); + +/* +* 名称:获取 SSL Session 信息 +* 描述:获取指定类型的 SSL Session 信息(输出类型参考:En_HP_SSLSessionInfo) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLServer_GetSSLSessionInfo(HP_SSLServer pServer, HP_CONNID dwConnID, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo); + +/* +* 名称:启动 SSL 握手 +* 描述:当通信组件设置为非自动握手时,需要调用本方法启动 SSL 握手 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_StartSSLHandShake(HP_SSLAgent pAgent, HP_CONNID dwConnID); + +/* 设置通信组件握手方式(默认:TRUE,自动握手) */ +HPSOCKET_API void __HP_CALL HP_SSLAgent_SetSSLAutoHandShake(HP_SSLAgent pAgent, BOOL bAutoHandShake); +/* 获取通信组件握手方式 */ +HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_IsSSLAutoHandShake(HP_SSLAgent pAgent); + +/* 设置 SSL 加密算法列表 */ +HPSOCKET_API void __HP_CALL HP_SSLAgent_SetSSLCipherList(HP_SSLAgent pAgent, LPCTSTR lpszCipherList); +/* 获取 SSL 加密算法列表 */ +HPSOCKET_API LPCTSTR __HP_CALL HP_SSLAgent_GetSSLCipherList(HP_SSLAgent pAgent); + +/* +* 名称:获取 SSL Session 信息 +* 描述:获取指定类型的 SSL Session 信息(输出类型参考:En_HP_SSLSessionInfo) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_GetSSLSessionInfo(HP_SSLAgent pAgent, HP_CONNID dwConnID, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo); + +/* +* 名称:启动 SSL 握手 +* 描述:当通信组件设置为非自动握手时,需要调用本方法启动 SSL 握手 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLClient_StartSSLHandShake(HP_SSLClient pClient); + +/* 设置通信组件握手方式(默认:TRUE,自动握手) */ +HPSOCKET_API void __HP_CALL HP_SSLClient_SetSSLAutoHandShake(HP_SSLClient pClient, BOOL bAutoHandShake); +/* 获取通信组件握手方式 */ +HPSOCKET_API BOOL __HP_CALL HP_SSLClient_IsSSLAutoHandShake(HP_SSLClient pClient); + +/* 设置 SSL 加密算法列表 */ +HPSOCKET_API void __HP_CALL HP_SSLClient_SetSSLCipherList(HP_SSLClient pClient, LPCTSTR lpszCipherList); +/* 获取 SSL 加密算法列表 */ +HPSOCKET_API LPCTSTR __HP_CALL HP_SSLClient_GetSSLCipherList(HP_SSLClient pClient); + +/* +* 名称:获取 SSL Session 信息 +* 描述:获取指定类型的 SSL Session 信息(输出类型参考:En_HP_SSLSessionInfo) +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_SSLClient_GetSSLSessionInfo(HP_SSLClient pClient, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo); + +/*****************************************************************************************************************************************************/ +/******************************************************************** HTTPS Exports ******************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _HTTP_SUPPORT + +/****************************************************/ +/**************** HTTPS 对象创建函数 *****************/ + +// 创建 HP_HttpsServer 对象 +HPSOCKET_API HP_HttpsServer __HP_CALL Create_HP_HttpsServer(HP_HttpServerListener pListener); +// 创建 HP_HttpsAgent 对象 +HPSOCKET_API HP_HttpsAgent __HP_CALL Create_HP_HttpsAgent(HP_HttpAgentListener pListener); +// 创建 HP_HttpsClient 对象 +HPSOCKET_API HP_HttpsClient __HP_CALL Create_HP_HttpsClient(HP_HttpClientListener pListener); +// 创建 HP_HttpsSyncClient 对象(pListener 参数可传入 nullptr) +HPSOCKET_API HP_HttpsSyncClient __HP_CALL Create_HP_HttpsSyncClient(HP_HttpClientListener pListener /*= nullptr*/); + +// 销毁 HP_HttpsServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpsServer(HP_HttpsServer pServer); +// 销毁 HP_HttpsAgent 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpsAgent(HP_HttpsAgent pAgent); +// 销毁 HP_HttpsClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpsClient(HP_HttpsClient pClient); +// 销毁 HP_HttpsSyncClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpsSyncClient(HP_HttpsSyncClient pClient); + +#endif + +#endif \ No newline at end of file diff --git a/hpsocket/HPSocket4C.h b/hpsocket/HPSocket4C.h new file mode 100644 index 0000000..22f612a --- /dev/null +++ b/hpsocket/HPSocket4C.h @@ -0,0 +1,2833 @@ +/* + * 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. + */ + +/****************************************************************************** +Module: HPSocket for C + +Desc: 导出纯 C 函数,让其它语言(如:C / C# / Delphi 等)能方便地使用 HPSocket + +Usage: + 方法一: + -------------------------------------------------------------------------------------- + 0. (C/C++ 程序)包含 HPTypeDef.h / HPSocket4C.h 头文件 + 1. 调用 ::Create_HP_XxxListener() 函数创建监听器对象 + 2. 调用 ::Create_HP_Xxx(pListener) 函数创建 HPSocket 对象 + 3. 调用 ::HP_Set_FN_Xxx_OnYyy(pListener, ...) 函数设置监听器的回调函数 + 4. 调用相关导出函数操作 HPSocket 对象 + 5. ...... ...... + 6. 调用 ::Destroy_HP_Xxx(pSocket) 函数销毁 HPSocket 对象 + 7. 调用 ::Destroy_HP_XxxListener(pListener) 函数销毁监听器对象 + + 方法二: + -------------------------------------------------------------------------------------- + 1. 应用程序把需要用到的导出函数封装到特定语言的包装类中 + 2. 通过包装类封装后,以面向对象的方式使用 HPSocket + +Release: + <-- 动态链接库 --> + 1. x86/libhpsocket4c.so - (32位/MBCS/Release) + 2. x86/libhpsocket4c_d.so - (32位/MBCS/DeBug) + 3. x64/libhpsocket4c.so - (64位/MBCS/Release) + 4. x64/libhpsocket4c_d.so - (64位/MBCS/DeBug) + + <-- 静态链接库 --> + 1. x86/static/libhpsocket4c.a - (32位/MBCS/Release) + 2. x86/static/libhpsocket4c_d.a - (32位/MBCS/DeBug) + 3. x64/static/libhpsocket4c.a - (64位/MBCS/Release) + 4. x64/static/libhpsocket4c_d.a - (64位/MBCS/DeBug) + +******************************************************************************/ + +#pragma once + +#include "HPTypeDef.h" + +/************************************************************************ +名称:定义 Socket 对象指针类型别名 +描述:把 Socket 对象指针定义为更直观的别名 +************************************************************************/ + +typedef PVOID HP_Object; + +typedef HP_Object HP_Server; +typedef HP_Object HP_Agent; +typedef HP_Object HP_Client; +typedef HP_Object HP_Node; +typedef HP_Object HP_TcpServer; +typedef HP_Object HP_TcpAgent; +typedef HP_Object HP_TcpClient; +typedef HP_Object HP_PullSocket; +typedef HP_Object HP_PullClient; +typedef HP_Object HP_TcpPullServer; +typedef HP_Object HP_TcpPullAgent; +typedef HP_Object HP_TcpPullClient; +typedef HP_Object HP_PackSocket; +typedef HP_Object HP_PackClient; +typedef HP_Object HP_TcpPackServer; +typedef HP_Object HP_TcpPackAgent; +typedef HP_Object HP_TcpPackClient; +typedef HP_Object HP_ThreadPool; +typedef HP_Object HP_Compressor; +typedef HP_Object HP_Decompressor; + +typedef HP_Object HP_Listener; +typedef HP_Object HP_ServerListener; +typedef HP_Object HP_AgentListener; +typedef HP_Object HP_ClientListener; +typedef HP_Object HP_TcpServerListener; +typedef HP_Object HP_TcpAgentListener; +typedef HP_Object HP_TcpClientListener; +typedef HP_Object HP_PullSocketListener; +typedef HP_Object HP_PullClientListener; +typedef HP_Object HP_TcpPullServerListener; +typedef HP_Object HP_TcpPullAgentListener; +typedef HP_Object HP_TcpPullClientListener; +typedef HP_Object HP_PackSocketListener; +typedef HP_Object HP_PackClientListener; +typedef HP_Object HP_TcpPackServerListener; +typedef HP_Object HP_TcpPackAgentListener; +typedef HP_Object HP_TcpPackClientListener; +typedef HP_Object HP_ThreadPoolListener; + +#ifdef _UDP_SUPPORT + +typedef HP_Object HP_UdpServer; +typedef HP_Object HP_UdpClient; +typedef HP_Object HP_UdpCast; +typedef HP_Object HP_UdpNode; +typedef HP_Object HP_UdpArqServer; +typedef HP_Object HP_UdpArqClient; + +typedef HP_Object HP_UdpServerListener; +typedef HP_Object HP_UdpClientListener; +typedef HP_Object HP_UdpCastListener; +typedef HP_Object HP_UdpNodeListener; +typedef HP_Object HP_UdpArqServerListener; +typedef HP_Object HP_UdpArqClientListener; + +#endif + +#ifdef _HTTP_SUPPORT + +typedef HP_Object HP_Http; +typedef HP_Object HP_HttpServer; +typedef HP_Object HP_HttpAgent; +typedef HP_Object HP_HttpClient; +typedef HP_Object HP_HttpSyncClient; + +typedef HP_Object HP_HttpServerListener; +typedef HP_Object HP_HttpAgentListener; +typedef HP_Object HP_HttpClientListener; + +#endif + +/*****************************************************************************************************************************************************/ +/****************************************************************** TCP/UDP Exports ******************************************************************/ +/*****************************************************************************************************************************************************/ + +/****************************************************/ +/***************** TCP/UDP 回调函数 ******************/ + +/* Server 回调函数 */ +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Server_OnPrepareListen) (HP_Server pSender, UINT_PTR soListen); +// 如果为 TCP 连接,pClient为 SOCKET 句柄;如果为 UDP 连接,pClient为 SOCKADDR 指针; +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Server_OnAccept) (HP_Server pSender, HP_CONNID dwConnID, UINT_PTR pClient); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Server_OnHandShake) (HP_Server pSender, HP_CONNID dwConnID); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Server_OnSend) (HP_Server pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Server_OnReceive) (HP_Server pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Server_OnPullReceive) (HP_Server pSender, HP_CONNID dwConnID, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Server_OnClose) (HP_Server pSender, HP_CONNID dwConnID, En_HP_SocketOperation enOperation, int iErrorCode); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Server_OnShutdown) (HP_Server pSender); + +/* Agent 回调函数 */ +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Agent_OnPrepareConnect) (HP_Agent pSender, HP_CONNID dwConnID, UINT_PTR socket); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Agent_OnConnect) (HP_Agent pSender, HP_CONNID dwConnID); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Agent_OnHandShake) (HP_Agent pSender, HP_CONNID dwConnID); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Agent_OnSend) (HP_Agent pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Agent_OnReceive) (HP_Agent pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Agent_OnPullReceive) (HP_Agent pSender, HP_CONNID dwConnID, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Agent_OnClose) (HP_Agent pSender, HP_CONNID dwConnID, En_HP_SocketOperation enOperation, int iErrorCode); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Agent_OnShutdown) (HP_Agent pSender); + +/* Client 回调函数 */ +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Client_OnPrepareConnect) (HP_Client pSender, HP_CONNID dwConnID, UINT_PTR socket); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Client_OnConnect) (HP_Client pSender, HP_CONNID dwConnID); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Client_OnHandShake) (HP_Client pSender, HP_CONNID dwConnID); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Client_OnSend) (HP_Client pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Client_OnReceive) (HP_Client pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Client_OnPullReceive) (HP_Client pSender, HP_CONNID dwConnID, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Client_OnClose) (HP_Client pSender, HP_CONNID dwConnID, En_HP_SocketOperation enOperation, int iErrorCode); + +#ifdef _UDP_SUPPORT + +/* UdpNode 回调函数 */ +typedef En_HP_HandleResult (__HP_CALL *HP_FN_UdpNode_OnPrepareListen) (HP_UdpNode pSender, UINT_PTR soListen); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_UdpNode_OnSend) (HP_UdpNode pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_UdpNode_OnReceive) (HP_UdpNode pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_UdpNode_OnError) (HP_UdpNode pSender, En_HP_SocketOperation enOperation, int iErrorCode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_UdpNode_OnShutdown) (HP_UdpNode pSender); + +#endif + +/****************************************************/ +/*************** TCP/UDP 对象创建函数 ****************/ + +// 创建 HP_TcpServer 对象 +HPSOCKET_API HP_TcpServer __HP_CALL Create_HP_TcpServer(HP_TcpServerListener pListener); +// 创建 HP_TcpAgent 对象 +HPSOCKET_API HP_TcpAgent __HP_CALL Create_HP_TcpAgent(HP_TcpAgentListener pListener); +// 创建 HP_TcpClient 对象 +HPSOCKET_API HP_TcpClient __HP_CALL Create_HP_TcpClient(HP_TcpClientListener pListener); +// 创建 HP_TcpPullServer 对象 +HPSOCKET_API HP_TcpPullServer __HP_CALL Create_HP_TcpPullServer(HP_TcpPullServerListener pListener); +// 创建 HP_TcpPullAgent 对象 +HPSOCKET_API HP_TcpPullAgent __HP_CALL Create_HP_TcpPullAgent(HP_TcpPullAgentListener pListener); +// 创建 HP_TcpPullClient 对象 +HPSOCKET_API HP_TcpPullClient __HP_CALL Create_HP_TcpPullClient(HP_TcpPullClientListener pListener); +// 创建 HP_TcpPackServer 对象 +HPSOCKET_API HP_TcpPackServer __HP_CALL Create_HP_TcpPackServer(HP_TcpServerListener pListener); +// 创建 HP_TcpPackAgent 对象 +HPSOCKET_API HP_TcpPackAgent __HP_CALL Create_HP_TcpPackAgent(HP_TcpAgentListener pListener); +// 创建 HP_TcpPackClient 对象 +HPSOCKET_API HP_TcpPackClient __HP_CALL Create_HP_TcpPackClient(HP_TcpClientListener pListener); + +// 销毁 HP_TcpServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpServer(HP_TcpServer pServer); +// 销毁 HP_TcpAgent 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpAgent(HP_TcpAgent pAgent); +// 销毁 HP_TcpClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpClient(HP_TcpClient pClient); +// 销毁 HP_TcpPullServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullServer(HP_TcpPullServer pServer); +// 销毁 HP_TcpPullAgent 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullAgent(HP_TcpPullAgent pAgent); +// 销毁 HP_TcpPullClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullClient(HP_TcpPullClient pClient); +// 销毁 HP_TcpPackServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackServer(HP_TcpPackServer pServer); +// 销毁 HP_TcpPackAgent 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackAgent(HP_TcpPackAgent pAgent); +// 销毁 HP_TcpPackClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackClient(HP_TcpPackClient pClient); + +// 创建 HP_TcpServerListener 对象 +HPSOCKET_API HP_TcpServerListener __HP_CALL Create_HP_TcpServerListener(); +// 创建 HP_TcpAgentListener 对象 +HPSOCKET_API HP_TcpAgentListener __HP_CALL Create_HP_TcpAgentListener(); +// 创建 HP_TcpClientListener 对象 +HPSOCKET_API HP_TcpClientListener __HP_CALL Create_HP_TcpClientListener(); +// 创建 HP_TcpPullServerListener 对象 +HPSOCKET_API HP_TcpPullServerListener __HP_CALL Create_HP_TcpPullServerListener(); +// 创建 HP_TcpPullAgentListener 对象 +HPSOCKET_API HP_TcpPullAgentListener __HP_CALL Create_HP_TcpPullAgentListener(); +// 创建 HP_TcpPullClientListener 对象 +HPSOCKET_API HP_TcpPullClientListener __HP_CALL Create_HP_TcpPullClientListener(); +// 创建 HP_TcpPackServerListener 对象 +HPSOCKET_API HP_TcpPackServerListener __HP_CALL Create_HP_TcpPackServerListener(); +// 创建 HP_TcpPackAgentListener 对象 +HPSOCKET_API HP_TcpPackAgentListener __HP_CALL Create_HP_TcpPackAgentListener(); +// 创建 HP_TcpPackClientListener 对象 +HPSOCKET_API HP_TcpPackClientListener __HP_CALL Create_HP_TcpPackClientListener(); + +// 销毁 HP_TcpServerListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpServerListener(HP_TcpServerListener pListener); +// 销毁 HP_TcpAgentListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpAgentListener(HP_TcpAgentListener pListener); +// 销毁 HP_TcpClientListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpClientListener(HP_TcpClientListener pListener); +// 销毁 HP_TcpPullServerListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullServerListener(HP_TcpPullServerListener pListener); +// 销毁 HP_TcpPullAgentListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullAgentListener(HP_TcpPullAgentListener pListener); +// 销毁 HP_TcpPullClientListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPullClientListener(HP_TcpPullClientListener pListener); +// 销毁 HP_TcpPackServerListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackServerListener(HP_TcpPackServerListener pListener); +// 销毁 HP_TcpPackAgentListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackAgentListener(HP_TcpPackAgentListener pListener); +// 销毁 HP_TcpPackClientListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_TcpPackClientListener(HP_TcpPackClientListener pListener); + +#ifdef _UDP_SUPPORT + +// 创建 HP_UdpServer 对象 +HPSOCKET_API HP_UdpServer __HP_CALL Create_HP_UdpServer(HP_UdpServerListener pListener); +// 创建 HP_UdpClient 对象 +HPSOCKET_API HP_UdpClient __HP_CALL Create_HP_UdpClient(HP_UdpClientListener pListener); +// 创建 HP_UdpCast 对象 +HPSOCKET_API HP_UdpCast __HP_CALL Create_HP_UdpCast(HP_UdpCastListener pListener); +// 创建 HP_UdpNode 对象 +HPSOCKET_API HP_UdpNode __HP_CALL Create_HP_UdpNode(HP_UdpNodeListener pListener); +// 创建 HP_UdpArqServer 对象 +HPSOCKET_API HP_UdpArqServer __HP_CALL Create_HP_UdpArqServer(HP_UdpServerListener pListener); +// 创建 HP_UdpArqClient 对象 +HPSOCKET_API HP_UdpArqClient __HP_CALL Create_HP_UdpArqClient(HP_UdpClientListener pListener); + +// 销毁 HP_UdpServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpServer(HP_UdpServer pServer); +// 销毁 HP_UdpClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpClient(HP_UdpClient pClient); +// 销毁 HP_UdpCast 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpCast(HP_UdpCast pCast); +// 销毁 HP_UdpNode 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpNode(HP_UdpNode pNode); +// 销毁 HP_UdpArqServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqServer(HP_UdpArqServer pServer); +// 销毁 HP_UdpArqClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqClient(HP_UdpArqClient pClient); + +// 创建 HP_UdpServerListener 对象 +HPSOCKET_API HP_UdpServerListener __HP_CALL Create_HP_UdpServerListener(); +// 创建 HP_UdpClientListener 对象 +HPSOCKET_API HP_UdpClientListener __HP_CALL Create_HP_UdpClientListener(); +// 创建 HP_UdpCastListener 对象 +HPSOCKET_API HP_UdpCastListener __HP_CALL Create_HP_UdpCastListener(); +// 创建 HP_UdpNodeListener 对象 +HPSOCKET_API HP_UdpNodeListener __HP_CALL Create_HP_UdpNodeListener(); +// 创建 HP_UdpArqServerListener 对象 +HPSOCKET_API HP_UdpArqServerListener __HP_CALL Create_HP_UdpArqServerListener(); +// 创建 HP_UdpArqClientListener 对象 +HPSOCKET_API HP_UdpArqClientListener __HP_CALL Create_HP_UdpArqClientListener(); + +// 销毁 HP_UdpServerListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpServerListener(HP_UdpServerListener pListener); +// 销毁 HP_UdpClientListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpClientListener(HP_UdpClientListener pListener); +// 销毁 HP_UdpCastListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpCastListener(HP_UdpCastListener pListener); +// 销毁 HP_UdpNodeListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpNodeListener(HP_UdpNodeListener pListener); +// 销毁 HP_UdpArqServerListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqServerListener(HP_UdpArqServerListener pListener); +// 销毁 HP_UdpArqClientListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_UdpArqClientListener(HP_UdpArqClientListener pListener); + +#endif + +/**********************************************************************************/ +/***************************** Server 回调函数设置方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnPrepareListen(HP_ServerListener pListener , HP_FN_Server_OnPrepareListen fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnAccept(HP_ServerListener pListener , HP_FN_Server_OnAccept fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnHandShake(HP_ServerListener pListener , HP_FN_Server_OnHandShake fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnSend(HP_ServerListener pListener , HP_FN_Server_OnSend fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnReceive(HP_ServerListener pListener , HP_FN_Server_OnReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnPullReceive(HP_ServerListener pListener , HP_FN_Server_OnPullReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnClose(HP_ServerListener pListener , HP_FN_Server_OnClose fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Server_OnShutdown(HP_ServerListener pListener , HP_FN_Server_OnShutdown fn); + +/**********************************************************************************/ +/****************************** Agent 回调函数设置方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnPrepareConnect(HP_AgentListener pListener , HP_FN_Agent_OnPrepareConnect fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnConnect(HP_AgentListener pListener , HP_FN_Agent_OnConnect fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnHandShake(HP_AgentListener pListener , HP_FN_Agent_OnHandShake fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnSend(HP_AgentListener pListener , HP_FN_Agent_OnSend fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnReceive(HP_AgentListener pListener , HP_FN_Agent_OnReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnPullReceive(HP_AgentListener pListener , HP_FN_Agent_OnPullReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnClose(HP_AgentListener pListener , HP_FN_Agent_OnClose fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Agent_OnShutdown(HP_AgentListener pListener , HP_FN_Agent_OnShutdown fn); + +/**********************************************************************************/ +/***************************** Client 回调函数设置方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnPrepareConnect(HP_ClientListener pListener , HP_FN_Client_OnPrepareConnect fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnConnect(HP_ClientListener pListener , HP_FN_Client_OnConnect fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnHandShake(HP_ClientListener pListener , HP_FN_Client_OnHandShake fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnSend(HP_ClientListener pListener , HP_FN_Client_OnSend fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnReceive(HP_ClientListener pListener , HP_FN_Client_OnReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnPullReceive(HP_ClientListener pListener , HP_FN_Client_OnPullReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_Client_OnClose(HP_ClientListener pListener , HP_FN_Client_OnClose fn); + +#ifdef _UDP_SUPPORT + +/**********************************************************************************/ +/***************************** UdpNode 回调函数设置方法 *****************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnPrepareListen(HP_UdpNodeListener pListener , HP_FN_UdpNode_OnPrepareListen fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnSend(HP_UdpNodeListener pListener , HP_FN_UdpNode_OnSend fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnReceive(HP_UdpNodeListener pListener , HP_FN_UdpNode_OnReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnError(HP_UdpNodeListener pListener , HP_FN_UdpNode_OnError fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_UdpNode_OnShutdown(HP_UdpNodeListener pListener , HP_FN_UdpNode_OnShutdown fn); + +#endif + +/**************************************************************************/ +/***************************** Server 操作方法 *****************************/ + +/* +* 名称:启动通信组件 +* 描述:启动服务端通信组件,启动完成后可开始接收客户端连接并收发数据 +* +* 参数: lpszBindAddress -- 监听地址 +* usPort -- 监听端口 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 HP_Server_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_Start(HP_Server pServer, LPCTSTR lpszBindAddress, USHORT usPort); + +/* +* 名称:关闭通信组件 +* 描述:关闭服务端通信组件,关闭完成后断开所有客户端连接并释放所有资源 +* +* 参数: +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 HP_Server_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_Stop(HP_Server pServer); + +/* +* 名称:等待 +* 描述:等待通信组件停止运行 +* +* 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_Wait(HP_Server pServer, DWORD dwMilliseconds); + +/* +* 名称:发送数据 +* 描述:向指定连接发送数据 +* +* 参数: dwConnID -- 连接 ID +* pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_Send(HP_Server pServer, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength); + +/* +* 名称:发送数据 +* 描述:向指定连接发送数据 +* +* 参数: dwConnID -- 连接 ID +* pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* iOffset -- 发送缓冲区指针偏移量 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_SendPart(HP_Server pServer, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset); + +/* +* 名称:发送多组数据 +* 描述:向指定连接发送多组数据 +* TCP - 顺序发送所有数据包 +* UDP - 把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) +* +* 参数: dwConnID -- 连接 ID +* pBuffers -- 发送缓冲区数组 +* iCount -- 发送缓冲区数目 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_SendPackets(HP_Server pServer, HP_CONNID dwConnID, const WSABUF pBuffers[], int iCount); + +/* +* 名称:暂停/恢复接收 +* 描述:暂停/恢复某个连接的数据接收工作 +* +* 参数: dwConnID -- 连接 ID +* bPause -- TRUE - 暂停, FALSE - 恢复 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_PauseReceive(HP_Server pServer, HP_CONNID dwConnID, BOOL bPause); + +/* +* 名称:断开连接 +* 描述:断开与某个客户端的连接 +* +* 参数: dwConnID -- 连接 ID +* bForce -- 是否强制断开连接 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_Disconnect(HP_Server pServer, HP_CONNID dwConnID, BOOL bForce); + +/* +* 名称:断开超时连接 +* 描述:断开超过指定时长的连接 +* +* 参数: dwPeriod -- 时长(毫秒) +* bForce -- 是否强制断开连接 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_DisconnectLongConnections(HP_Server pServer, DWORD dwPeriod, BOOL bForce); + +/* +* 名称:断开静默连接 +* 描述:断开超过指定时长的静默连接 +* +* 参数: dwPeriod -- 时长(毫秒) +* bForce -- 是否强制断开连接 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_DisconnectSilenceConnections(HP_Server pServer, DWORD dwPeriod, BOOL bForce); + +/******************************************************************************/ +/***************************** Server 属性访问方法 *****************************/ + +/* +* 名称:设置连接的附加数据 +* 描述:是否为连接绑定附加数据或者绑定什么样的数据,均由应用程序只身决定 +* +* 参数: dwConnID -- 连接 ID +* pv -- 数据 +* 返回值: TRUE -- 成功 +* FALSE -- 失败(无效的连接 ID) +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_SetConnectionExtra(HP_Server pServer, HP_CONNID dwConnID, PVOID pExtra); + +/* +* 名称:获取连接的附加数据 +* 描述:是否为连接绑定附加数据或者绑定什么样的数据,均由应用程序只身决定 +* +* 参数: dwConnID -- 连接 ID +* ppv -- 数据指针 +* 返回值: TRUE -- 成功 +* FALSE -- 失败(无效的连接 ID) +*/ +HPSOCKET_API BOOL __HP_CALL HP_Server_GetConnectionExtra(HP_Server pServer, HP_CONNID dwConnID, PVOID* ppExtra); + +/* 检测是否为安全连接(SSL/HTTPS) */ +HPSOCKET_API BOOL __HP_CALL HP_Server_IsSecure(HP_Server pServer); +/* 检查通信组件是否已启动 */ +HPSOCKET_API BOOL __HP_CALL HP_Server_HasStarted(HP_Server pServer); +/* 查看通信组件当前状态 */ +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_Server_GetState(HP_Server pServer); +/* 获取最近一次失败操作的错误代码 */ +HPSOCKET_API En_HP_SocketError __HP_CALL HP_Server_GetLastError(HP_Server pServer); +/* 获取最近一次失败操作的错误描述 */ +HPSOCKET_API LPCTSTR __HP_CALL HP_Server_GetLastErrorDesc(HP_Server pServer); +/* 获取连接中未发出数据的长度 */ +HPSOCKET_API BOOL __HP_CALL HP_Server_GetPendingDataLength(HP_Server pServer, HP_CONNID dwConnID, int* piPending); +/* 获取连接的数据接收状态 */ +HPSOCKET_API BOOL __HP_CALL HP_Server_IsPauseReceive(HP_Server pServer, HP_CONNID dwConnID, BOOL* pbPaused); +/* 检测是否有效连接 */ +HPSOCKET_API BOOL __HP_CALL HP_Server_IsConnected(HP_Server pServer, HP_CONNID dwConnID); +/* 获取客户端连接数 */ +HPSOCKET_API DWORD __HP_CALL HP_Server_GetConnectionCount(HP_Server pServer); +/* 获取所有连接的 HP_CONNID */ +HPSOCKET_API BOOL __HP_CALL HP_Server_GetAllConnectionIDs(HP_Server pServer, HP_CONNID pIDs[], DWORD* pdwCount); +/* 获取某个客户端连接时长(毫秒) */ +HPSOCKET_API BOOL __HP_CALL HP_Server_GetConnectPeriod(HP_Server pServer, HP_CONNID dwConnID, DWORD* pdwPeriod); +/* 获取某个连接静默时间(毫秒) */ +HPSOCKET_API BOOL __HP_CALL HP_Server_GetSilencePeriod(HP_Server pServer, HP_CONNID dwConnID, DWORD* pdwPeriod); +/* 获取监听 Socket 的地址信息 */ +HPSOCKET_API BOOL __HP_CALL HP_Server_GetListenAddress(HP_Server pServer, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +/* 获取某个连接的本地地址信息 */ +HPSOCKET_API BOOL __HP_CALL HP_Server_GetLocalAddress(HP_Server pServer, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +/* 获取某个连接的远程地址信息 */ +HPSOCKET_API BOOL __HP_CALL HP_Server_GetRemoteAddress(HP_Server pServer, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); + +/* 设置地址重用选项 */ +HPSOCKET_API void __HP_CALL HP_Server_SetReuseAddressPolicy(HP_Server pServer, En_HP_ReuseAddressPolicy enReusePolicy); +/* 设置数据发送策略(对 Linux 平台组件无效) */ +HPSOCKET_API void __HP_CALL HP_Server_SetSendPolicy(HP_Server pServer, En_HP_SendPolicy enSendPolicy); +/* 设置 OnSend 事件同步策略(对 Linux 平台组件无效) */ +HPSOCKET_API void __HP_CALL HP_Server_SetOnSendSyncPolicy(HP_Server pServer, En_HP_OnSendSyncPolicy enSyncPolicy); +//* 设置 Socket 缓存对象锁定时间(毫秒,在锁定期间该 Socket 缓存对象不能被获取使用) */ +HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjLockTime(HP_Server pServer, DWORD dwFreeSocketObjLockTime); +/* 设置 Socket 缓存池大小(通常设置为平均并发连接数量的 1/3 - 1/2) */ +HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjPool(HP_Server pServer, DWORD dwFreeSocketObjPool); +/* 设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍) */ +HPSOCKET_API void __HP_CALL HP_Server_SetFreeBufferObjPool(HP_Server pServer, DWORD dwFreeBufferObjPool); +/* 设置 Socket 缓存池回收阀值(通常设置为 Socket 缓存池大小的 3 倍) */ +HPSOCKET_API void __HP_CALL HP_Server_SetFreeSocketObjHold(HP_Server pServer, DWORD dwFreeSocketObjHold); +/* 设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍) */ +HPSOCKET_API void __HP_CALL HP_Server_SetFreeBufferObjHold(HP_Server pServer, DWORD dwFreeBufferObjHold); +/* 设置最大连接数(组件会根据设置值预分配内存,因此需要根据实际情况设置,不宜过大)*/ +HPSOCKET_API void __HP_CALL HP_Server_SetMaxConnectionCount(HP_Server pServer, DWORD dwMaxConnectionCount); +/* 设置工作线程数量(通常设置为 2 * CPU + 2) */ +HPSOCKET_API void __HP_CALL HP_Server_SetWorkerThreadCount(HP_Server pServer, DWORD dwWorkerThreadCount); +/* 设置是否标记静默时间(设置为 TRUE 时 DisconnectSilenceConnections() 和 GetSilencePeriod() 才有效,默认:TRUE) */ +HPSOCKET_API void __HP_CALL HP_Server_SetMarkSilence(HP_Server pServer, BOOL bMarkSilence); + +/* 获取地址重用选项 */ +HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Server_GetReuseAddressPolicy(HP_Server pServer); +/* 获取数据发送策略(对 Linux 平台组件无效) */ +HPSOCKET_API En_HP_SendPolicy __HP_CALL HP_Server_GetSendPolicy(HP_Server pServer); +/* 获取 OnSend 事件同步策略(对 Linux 平台组件无效) */ +HPSOCKET_API En_HP_OnSendSyncPolicy __HP_CALL HP_Server_GetOnSendSyncPolicy(HP_Server pServer); +//* 获取 Socket 缓存对象锁定时间 */ +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjLockTime(HP_Server pServer); +/* 获取 Socket 缓存池大小 */ +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjPool(HP_Server pServer); +/* 获取内存块缓存池大小 */ +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeBufferObjPool(HP_Server pServer); +/* 获取 Socket 缓存池回收阀值 */ +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeSocketObjHold(HP_Server pServer); +/* 获取内存块缓存池回收阀值 */ +HPSOCKET_API DWORD __HP_CALL HP_Server_GetFreeBufferObjHold(HP_Server pServer); +/* 获取最大连接数 */ +HPSOCKET_API DWORD __HP_CALL HP_Server_GetMaxConnectionCount(HP_Server pServer); +/* 获取工作线程数量 */ +HPSOCKET_API DWORD __HP_CALL HP_Server_GetWorkerThreadCount(HP_Server pServer); +/* 检测是否标记静默时间 */ +HPSOCKET_API BOOL __HP_CALL HP_Server_IsMarkSilence(HP_Server pServer); + +/**********************************************************************************/ +/******************************* TCP Server 操作方法 *******************************/ + +/* +* 名称:发送小文件 +* 描述:向指定连接发送 4096 KB 以下的小文件 +* +* 参数: dwConnID -- 连接 ID +* lpszFileName -- 文件路径 +* pHead -- 头部附加数据 +* pTail -- 尾部附加数据 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_TcpServer_SendSmallFile(HP_Server pServer, HP_CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail); + +/**********************************************************************************/ +/***************************** TCP Server 属性访问方法 *****************************/ + +/* 设置监听 Socket 的等候队列大小(根据并发连接数量调整设置) */ +HPSOCKET_API void __HP_CALL HP_TcpServer_SetSocketListenQueue(HP_TcpServer pServer, DWORD dwSocketListenQueue); +/* 设置 EPOLL 等待事件的最大数量 */ +HPSOCKET_API void __HP_CALL HP_TcpServer_SetAcceptSocketCount(HP_TcpServer pServer, DWORD dwAcceptSocketCount); +/* 设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为 1024 的倍数) */ +HPSOCKET_API void __HP_CALL HP_TcpServer_SetSocketBufferSize(HP_TcpServer pServer, DWORD dwSocketBufferSize); +/* 设置正常心跳包间隔(毫秒,0 则不发送心跳包,默认:60 * 1000) */ +HPSOCKET_API void __HP_CALL HP_TcpServer_SetKeepAliveTime(HP_TcpServer pServer, DWORD dwKeepAliveTime); +/* 设置异常心跳包间隔(毫秒,0 不发送心跳包,,默认:20 * 1000,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线) */ +HPSOCKET_API void __HP_CALL HP_TcpServer_SetKeepAliveInterval(HP_TcpServer pServer, DWORD dwKeepAliveInterval); +/* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ +HPSOCKET_API void __HP_CALL HP_TcpServer_SetNoDelay(HP_TcpServer pServer, BOOL bNoDelay); + +/* 获取 EPOLL 等待事件的最大数量 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetAcceptSocketCount(HP_TcpServer pServer); +/* 获取通信数据缓冲区大小 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetSocketBufferSize(HP_TcpServer pServer); +/* 获取监听 Socket 的等候队列大小 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetSocketListenQueue(HP_TcpServer pServer); +/* 获取正常心跳包间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetKeepAliveTime(HP_TcpServer pServer); +/* 获取异常心跳包间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpServer_GetKeepAliveInterval(HP_TcpServer pServer); +/* 检查是否开启 nodelay 模式 */ +HPSOCKET_API BOOL __HP_CALL HP_TcpServer_IsNoDelay(HP_TcpServer pServer); + +#ifdef _UDP_SUPPORT + +/**********************************************************************************/ +/***************************** UDP Server 属性访问方法 *****************************/ + +/* 设置数据报文最大长度(建议在局域网环境下不超过 1432 字节,在广域网环境下不超过 548 字节) */ +HPSOCKET_API void __HP_CALL HP_UdpServer_SetMaxDatagramSize(HP_UdpServer pServer, DWORD dwMaxDatagramSize); +/* 获取数据报文最大长度 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetMaxDatagramSize(HP_UdpServer pServer); + +/* 设置 Receive 预投递数量(根据负载调整设置,Receive 预投递数量越大则丢包概率越小) */ +HPSOCKET_API void __HP_CALL HP_UdpServer_SetPostReceiveCount(HP_UdpServer pServer, DWORD dwPostReceiveCount); +/* 获取 Receive 预投递数量 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetPostReceiveCount(HP_UdpServer pServer); + +/* 设置监测包尝试次数(0 则不发送监测跳包,如果超过最大尝试次数则认为已断线) */ +HPSOCKET_API void __HP_CALL HP_UdpServer_SetDetectAttempts(HP_UdpServer pServer, DWORD dwDetectAttempts); +/* 设置监测包发送间隔(毫秒,0 不发送监测包) */ +HPSOCKET_API void __HP_CALL HP_UdpServer_SetDetectInterval(HP_UdpServer pServer, DWORD dwDetectInterval); +/* 获取心跳检查次数 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetDetectAttempts(HP_UdpServer pServer); +/* 获取心跳检查间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpServer_GetDetectInterval(HP_UdpServer pServer); + +/**********************************************************************************/ +/*************************** UDP ARQ Server 属性访问方法 ***************************/ + +/* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetNoDelay(HP_UdpArqServer pServer, BOOL bNoDelay); +/* 设置是否关闭拥塞控制(默认:FALSE,不关闭) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetTurnoffCongestCtrl(HP_UdpArqServer pServer, BOOL bTurnOff); +/* 设置数据刷新间隔(毫秒,默认:60) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetFlushInterval(HP_UdpArqServer pServer, DWORD dwFlushInterval); +/* 设置快速重传 ACK 跨越次数(默认:0,关闭快速重传) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetResendByAcks(HP_UdpArqServer pServer, DWORD dwResendByAcks); +/* 设置发送窗口大小(数据包数量,默认:128) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetSendWndSize(HP_UdpArqServer pServer, DWORD dwSendWndSize); +/* 设置接收窗口大小(数据包数量,默认:512) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetRecvWndSize(HP_UdpArqServer pServer, DWORD dwRecvWndSize); +/* 设置最小重传超时时间(毫秒,默认:30) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMinRto(HP_UdpArqServer pServer, DWORD dwMinRto); +/* 设置快速握手次数限制(默认:5,如果为 0 则不限制) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetFastLimit(HP_UdpArqServer pServer, DWORD dwFastLimit); +/* 设置最大传输单元(默认:0,与 SetMaxDatagramSize() 一致) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMaxTransUnit(HP_UdpArqServer pServer, DWORD dwMaxTransUnit); +/* 设置最大数据包大小(默认:4096) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetMaxMessageSize(HP_UdpArqServer pServer, DWORD dwMaxMessageSize); +/* 设置握手超时时间(毫秒,默认:5000) */ +HPSOCKET_API void __HP_CALL HP_UdpArqServer_SetHandShakeTimeout(HP_UdpArqServer pServer, DWORD dwHandShakeTimeout); + +/* 检测是否开启 nodelay 模式 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_IsNoDelay(HP_UdpArqServer pServer); +/* 检测是否关闭拥塞控制 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_IsTurnoffCongestCtrl(HP_UdpArqServer pServer); +/* 获取数据刷新间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetFlushInterval(HP_UdpArqServer pServer); +/* 获取快速重传 ACK 跨越次数 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetResendByAcks(HP_UdpArqServer pServer); +/* 获取发送窗口大小 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetSendWndSize(HP_UdpArqServer pServer); +/* 获取接收窗口大小 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetRecvWndSize(HP_UdpArqServer pServer); +/* 获取最小重传超时时间 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMinRto(HP_UdpArqServer pServer); +/* 获取快速握手次数限制 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetFastLimit(HP_UdpArqServer pServer); +/* 获取最大传输单元 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMaxTransUnit(HP_UdpArqServer pServer); +/* 获取最大数据包大小 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetMaxMessageSize(HP_UdpArqServer pServer); +/* 获取握手超时时间 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqServer_GetHandShakeTimeout(HP_UdpArqServer pServer); + +/* 获取等待发送包数量 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpArqServer_GetWaitingSendMessageCount(HP_UdpArqServer pServer, HP_CONNID dwConnID, int* piCount); + +#endif + +/**************************************************************************/ +/***************************** Agent 操作方法 *****************************/ + +/* +* 名称:启动通信组件 +* 描述:启动通信代理组件,启动完成后可开始连接远程服务器 +* +* 参数: lpszBindAddress -- 绑定地址(默认:nullptr,绑定任意地址) +* bAsyncConnect -- 是否采用异步 Connect +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 HP_Agent_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_Start(HP_Agent pAgent, LPCTSTR lpszBindAddress, BOOL bAsyncConnect); + +/* +* 名称:关闭通信组件 +* 描述:关闭通信组件,关闭完成后断开所有连接并释放所有资源 +* +* 参数: +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 HP_Agent_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_Stop(HP_Agent pAgent); + +/* +* 名称:等待 +* 描述:等待通信组件停止运行 +* +* 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_Wait(HP_Agent pAgent, DWORD dwMilliseconds); + +/* +* 名称:连接服务器 +* 描述:连接服务器,连接成功后 IAgentListener 会接收到 OnConnect() / OnHandShake() 事件 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* pdwConnID -- 连接 ID(默认:nullptr,不获取连接 ID) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过函数 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_Connect(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID); + +/* +* 名称:连接服务器 +* 描述:连接服务器,连接成功后 IAgentListener 会接收到 OnConnect() / OnHandShake() 事件 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* pdwConnID -- 连接 ID(默认:nullptr,不获取连接 ID) +* pExtra -- 连接附加数据(默认:nullptr) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过函数 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtra(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra); + +/* +* 名称:连接服务器 +* 描述:连接服务器,连接成功后 IAgentListener 会接收到 OnConnect() / OnHandShake() 事件 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* pdwConnID -- 连接 ID(默认:nullptr,不获取连接 ID) +* usLocalPort -- 本地端口(默认:0) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过函数 SYS_GetLastError() 获取 Windows 错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithLocalPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, USHORT usLocalPort); + +/* +* 名称:连接服务器 +* 描述:连接服务器,连接成功后 IAgentListener 会接收到 OnConnect() / OnHandShake() 事件 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* pdwConnID -- 连接 ID(默认:nullptr,不获取连接 ID) +* lpszLocalAddress -- 本地地址(默认:nullptr,使用 Start() 方法中绑定的地址) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过函数 SYS_GetLastError() 获取 Windows 错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithLocalAddress(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, LPCTSTR lpszLocalAddress); + +/* +* 名称:连接服务器 +* 描述:连接服务器,连接成功后 IAgentListener 会接收到 OnConnect() / OnHandShake() 事件 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* pdwConnID -- 连接 ID(默认:nullptr,不获取连接 ID) +* pExtra -- 连接附加数据(默认:nullptr) +* usLocalPort -- 本地端口(默认:0) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过函数 SYS_GetLastError() 获取 Windows 错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtraAndLocalPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra, USHORT usLocalPort); + +/* +* 名称:连接服务器 +* 描述:连接服务器,连接成功后 IAgentListener 会接收到 OnConnect() / OnHandShake() 事件 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* pdwConnID -- 连接 ID(默认:nullptr,不获取连接 ID) +* pExtra -- 连接附加数据(默认:nullptr) +* usLocalPort -- 本地端口(默认:0) +* lpszLocalAddress -- 本地地址(默认:nullptr,使用 Start() 方法中绑定的地址) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过函数 SYS_GetLastError() 获取 Windows 错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_ConnectWithExtraAndLocalAddressPort(HP_Agent pAgent, LPCTSTR lpszRemoteAddress, USHORT usPort, HP_CONNID* pdwConnID, PVOID pExtra, USHORT usLocalPort, LPCTSTR lpszLocalAddress); + +/* +* 名称:发送数据 +* 描述:向指定连接发送数据 +* +* 参数: dwConnID -- 连接 ID +* pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_Send(HP_Agent pAgent, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength); + +/* +* 名称:发送数据 +* 描述:向指定连接发送数据 +* +* 参数: dwConnID -- 连接 ID +* pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* iOffset -- 发送缓冲区指针偏移量 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_SendPart(HP_Agent pAgent, HP_CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset); + +/* +* 名称:发送多组数据 +* 描述:向指定连接发送多组数据 +* TCP - 顺序发送所有数据包 +* UDP - 把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) +* +* 参数: dwConnID -- 连接 ID +* pBuffers -- 发送缓冲区数组 +* iCount -- 发送缓冲区数目 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_SendPackets(HP_Agent pAgent, HP_CONNID dwConnID, const WSABUF pBuffers[], int iCount); + +/* +* 名称:暂停/恢复接收 +* 描述:暂停/恢复某个连接的数据接收工作 +* +* 参数: dwConnID -- 连接 ID +* bPause -- TRUE - 暂停, FALSE - 恢复 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_PauseReceive(HP_Agent pAgent, HP_CONNID dwConnID, BOOL bPause); + +/* +* 名称:断开连接 +* 描述:断开某个连接 +* +* 参数: dwConnID -- 连接 ID +* bForce -- 是否强制断开连接 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_Disconnect(HP_Agent pAgent, HP_CONNID dwConnID, BOOL bForce); + +/* +* 名称:断开超时连接 +* 描述:断开超过指定时长的连接 +* +* 参数: dwPeriod -- 时长(毫秒) +* bForce -- 是否强制断开连接 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_DisconnectLongConnections(HP_Agent pAgent, DWORD dwPeriod, BOOL bForce); + +/* +* 名称:断开静默连接 +* 描述:断开超过指定时长的静默连接 +* +* 参数: dwPeriod -- 时长(毫秒) +* bForce -- 是否强制断开连接 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_DisconnectSilenceConnections(HP_Agent pAgent, DWORD dwPeriod, BOOL bForce); + +/******************************************************************************/ +/***************************** Agent 属性访问方法 *****************************/ + +/* +* 名称:设置连接的附加数据 +* 描述:是否为连接绑定附加数据或者绑定什么样的数据,均由应用程序只身决定 +* +* 参数: dwConnID -- 连接 ID +* pv -- 数据 +* 返回值: TRUE -- 成功 +* FALSE -- 失败(无效的连接 ID) +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_SetConnectionExtra(HP_Agent pAgent, HP_CONNID dwConnID, PVOID pExtra); + +/* +* 名称:获取连接的附加数据 +* 描述:是否为连接绑定附加数据或者绑定什么样的数据,均由应用程序只身决定 +* +* 参数: dwConnID -- 连接 ID +* ppv -- 数据指针 +* 返回值: TRUE -- 成功 +* FALSE -- 失败(无效的连接 ID) +*/ +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetConnectionExtra(HP_Agent pAgent, HP_CONNID dwConnID, PVOID* ppExtra); + +/* 检测是否为安全连接(SSL/HTTPS) */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_IsSecure(HP_Agent pAgent); +/* 检查通信组件是否已启动 */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_HasStarted(HP_Agent pAgent); +/* 查看通信组件当前状态 */ +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_Agent_GetState(HP_Agent pAgent); +/* 获取连接数 */ +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetConnectionCount(HP_Agent pAgent); +/* 获取所有连接的 HP_CONNID */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetAllConnectionIDs(HP_Agent pAgent, HP_CONNID pIDs[], DWORD* pdwCount); +/* 获取某个连接时长(毫秒) */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetConnectPeriod(HP_Agent pAgent, HP_CONNID dwConnID, DWORD* pdwPeriod); +/* 获取某个连接静默时间(毫秒) */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetSilencePeriod(HP_Agent pAgent, HP_CONNID dwConnID, DWORD* pdwPeriod); +/* 获取某个连接的本地地址信息 */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetLocalAddress(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +/* 获取某个连接的远程地址信息 */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetRemoteAddress(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +/* 获取某个连接的远程主机信息 */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetRemoteHost(HP_Agent pAgent, HP_CONNID dwConnID, TCHAR lpszHost[], int* piHostLen, USHORT* pusPort); +/* 获取最近一次失败操作的错误代码 */ +HPSOCKET_API En_HP_SocketError __HP_CALL HP_Agent_GetLastError(HP_Agent pAgent); +/* 获取最近一次失败操作的错误描述 */ +HPSOCKET_API LPCTSTR __HP_CALL HP_Agent_GetLastErrorDesc(HP_Agent pAgent); +/* 获取连接中未发出数据的长度 */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_GetPendingDataLength(HP_Agent pAgent, HP_CONNID dwConnID, int* piPending); +/* 获取连接的数据接收状态 */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_IsPauseReceive(HP_Agent pAgent, HP_CONNID dwConnID, BOOL* pbPaused); +/* 检测是否有效连接 */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_IsConnected(HP_Agent pAgent, HP_CONNID dwConnID); + +/* 设置地址重用选项 */ +HPSOCKET_API void __HP_CALL HP_Agent_SetReuseAddressPolicy(HP_Agent pAgent, En_HP_ReuseAddressPolicy enReusePolicy); +/* 设置数据发送策略(对 Linux 平台组件无效) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetSendPolicy(HP_Agent pAgent, En_HP_SendPolicy enSendPolicy); +/* 设置 OnSend 事件同步策略(对 Linux 平台组件无效) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetOnSendSyncPolicy(HP_Agent pAgent, En_HP_OnSendSyncPolicy enSyncPolicy); +/* 设置 Socket 缓存对象锁定时间(毫秒,在锁定期间该 Socket 缓存对象不能被获取使用) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjLockTime(HP_Agent pAgent, DWORD dwFreeSocketObjLockTime); +/* 设置 Socket 缓存池大小(通常设置为平均并发连接数量的 1/3 - 1/2) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjPool(HP_Agent pAgent, DWORD dwFreeSocketObjPool); +/* 设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeBufferObjPool(HP_Agent pAgent, DWORD dwFreeBufferObjPool); +/* 设置 Socket 缓存池回收阀值(通常设置为 Socket 缓存池大小的 3 倍) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeSocketObjHold(HP_Agent pAgent, DWORD dwFreeSocketObjHold); +/* 设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetFreeBufferObjHold(HP_Agent pAgent, DWORD dwFreeBufferObjHold); +/* 设置最大连接数(组件会根据设置值预分配内存,因此需要根据实际情况设置,不宜过大)*/ +HPSOCKET_API void __HP_CALL HP_Agent_SetMaxConnectionCount(HP_Agent pAgent, DWORD dwMaxConnectionCount); +/* 设置工作线程数量(通常设置为 2 * CPU + 2) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetWorkerThreadCount(HP_Agent pAgent, DWORD dwWorkerThreadCount); +/* 设置是否标记静默时间(设置为 TRUE 时 DisconnectSilenceConnections() 和 GetSilencePeriod() 才有效,默认:TRUE) */ +HPSOCKET_API void __HP_CALL HP_Agent_SetMarkSilence(HP_Agent pAgent, BOOL bMarkSilence); + +/* 获取地址重用选项 */ +HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Agent_GetReuseAddressPolicy(HP_Agent pAgent); +/* 获取数据发送策略(对 Linux 平台组件无效) */ +HPSOCKET_API En_HP_SendPolicy __HP_CALL HP_Agent_GetSendPolicy(HP_Agent pAgent); +/* 获取 OnSend 事件同步策略(对 Linux 平台组件无效) */ +HPSOCKET_API En_HP_OnSendSyncPolicy __HP_CALL HP_Agent_GetOnSendSyncPolicy(HP_Agent pAgent); +/* 获取 Socket 缓存对象锁定时间 */ +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjLockTime(HP_Agent pAgent); +/* 获取 Socket 缓存池大小 */ +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjPool(HP_Agent pAgent); +/* 获取内存块缓存池大小 */ +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeBufferObjPool(HP_Agent pAgent); +/* 获取 Socket 缓存池回收阀值 */ +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeSocketObjHold(HP_Agent pAgent); +/* 获取内存块缓存池回收阀值 */ +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetFreeBufferObjHold(HP_Agent pAgent); +/* 获取最大连接数 */ +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetMaxConnectionCount(HP_Agent pAgent); +/* 获取工作线程数量 */ +HPSOCKET_API DWORD __HP_CALL HP_Agent_GetWorkerThreadCount(HP_Agent pAgent); +/* 检测是否标记静默时间 */ +HPSOCKET_API BOOL __HP_CALL HP_Agent_IsMarkSilence(HP_Agent pAgent); + +/**********************************************************************************/ +/******************************* TCP Agent 操作方法 *******************************/ + +/* +* 名称:发送小文件 +* 描述:向指定连接发送 4096 KB 以下的小文件 +* +* 参数: dwConnID -- 连接 ID +* lpszFileName -- 文件路径 +* pHead -- 头部附加数据 +* pTail -- 尾部附加数据 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_TcpAgent_SendSmallFile(HP_Agent pAgent, HP_CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail); + +/**********************************************************************************/ +/***************************** TCP Agent 属性访问方法 *****************************/ + +/* 设置同步连接超时时间(毫秒) */ +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetSyncConnectTimeout(HP_TcpAgent pAgent, DWORD dwSyncConnectTimeout); +/* 设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为 1024 的倍数) */ +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetSocketBufferSize(HP_TcpAgent pAgent, DWORD dwSocketBufferSize); +/* 设置正常心跳包间隔(毫秒,0 则不发送心跳包,默认:60 * 1000) */ +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetKeepAliveTime(HP_TcpAgent pAgent, DWORD dwKeepAliveTime); +/* 设置异常心跳包间隔(毫秒,0 不发送心跳包,,默认:20 * 1000,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线) */ +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetKeepAliveInterval(HP_TcpAgent pAgent, DWORD dwKeepAliveInterval); +/* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ +HPSOCKET_API void __HP_CALL HP_TcpAgent_SetNoDelay(HP_TcpAgent pAgent, BOOL bNoDelay); + +/* 获取同步连接超时时间 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetSyncConnectTimeout(HP_TcpAgent pAgent); +/* 获取通信数据缓冲区大小 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetSocketBufferSize(HP_TcpAgent pAgent); +/* 获取正常心跳包间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetKeepAliveTime(HP_TcpAgent pAgent); +/* 获取异常心跳包间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpAgent_GetKeepAliveInterval(HP_TcpAgent pAgent); +/* 检查是否开启 nodelay 模式 */ +HPSOCKET_API BOOL __HP_CALL HP_TcpAgent_IsNoDelay(HP_TcpAgent pAgent); + +/******************************************************************************/ +/***************************** Client 组件操作方法 *****************************/ + +/* +* 名称:启动通信组件 +* 描述:启动客户端通信组件并连接服务端,启动完成后可开始收发数据 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* bAsyncConnect -- 是否采用异步 Connect +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 HP_Client_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_Start(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect); + +/* +* 名称:启动通信组件(并指定绑定地址) +* 描述:启动客户端通信组件并连接服务端,启动完成后可开始收发数据 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* bAsyncConnect -- 是否采用异步 Connect +* lpszBindAddress -- 绑定地址(默认:nullptr,TcpClient/UdpClient -> 不执行绑定操作,UdpCast 绑定 -> 任意地址) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 HP_Client_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_StartWithBindAddress(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress); + +/* +* 名称:启动通信组件(并指定绑定地址) +* 描述:启动客户端通信组件并连接服务端,启动完成后可开始收发数据 +* +* 参数: lpszRemoteAddress -- 服务端地址 +* usPort -- 服务端端口 +* bAsyncConnect -- 是否采用异步 Connect +* lpszBindAddress -- 绑定地址(默认:nullptr,TcpClient/UdpClient -> 不执行绑定操作,UdpCast 绑定 -> 任意地址) +* usLocalPort -- 本地端口(默认:0) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 HP_Client_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_StartWithBindAddressAndLocalPort(HP_Client pClient, LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort); + +/* +* 名称:关闭通信组件 +* 描述:关闭客户端通信组件,关闭完成后断开与服务端的连接并释放所有资源 +* +* 参数: +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 HP_Client_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_Stop(HP_Client pClient); + +/* +* 名称:发送数据 +* 描述:向服务端发送数据 +* +* 参数: pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_Send(HP_Client pClient, const BYTE* pBuffer, int iLength); + +/* +* 名称:发送数据 +* 描述:向服务端发送数据 +* +* 参数: pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* iOffset -- 发送缓冲区指针偏移量 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_SendPart(HP_Client pClient, const BYTE* pBuffer, int iLength, int iOffset); + +/* +* 名称:发送多组数据 +* 描述:向服务端发送多组数据 +* TCP - 顺序发送所有数据包 +* UDP - 把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) +* +* 参数: pBuffers -- 发送缓冲区数组 +* iCount -- 发送缓冲区数目 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_SendPackets(HP_Client pClient, const WSABUF pBuffers[], int iCount); + +/* +* 名称:暂停/恢复接收 +* 描述:暂停/恢复某个连接的数据接收工作 +* +* bPause -- TRUE - 暂停, FALSE - 恢复 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_PauseReceive(HP_Client pClient, BOOL bPause); + +/* +* 名称:等待 +* 描述:等待通信组件停止运行 +* +* 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Client_Wait(HP_Client pClient, DWORD dwMilliseconds); + +/******************************************************************************/ +/***************************** Client 属性访问方法 *****************************/ + +/* 设置连接的附加数据 */ +HPSOCKET_API void __HP_CALL HP_Client_SetExtra(HP_Client pClient, PVOID pExtra); +/* 获取连接的附加数据 */ +HPSOCKET_API PVOID __HP_CALL HP_Client_GetExtra(HP_Client pClient); + +/* 检测是否为安全连接(SSL/HTTPS) */ +HPSOCKET_API BOOL __HP_CALL HP_Client_IsSecure(HP_Client pClient); +/* 检查通信组件是否已启动 */ +HPSOCKET_API BOOL __HP_CALL HP_Client_HasStarted(HP_Client pClient); +/* 查看通信组件当前状态 */ +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_Client_GetState(HP_Client pClient); +/* 获取最近一次失败操作的错误代码 */ +HPSOCKET_API En_HP_SocketError __HP_CALL HP_Client_GetLastError(HP_Client pClient); +/* 获取最近一次失败操作的错误描述 */ +HPSOCKET_API LPCTSTR __HP_CALL HP_Client_GetLastErrorDesc(HP_Client pClient); +/* 获取该组件对象的连接 ID */ +HPSOCKET_API HP_CONNID __HP_CALL HP_Client_GetConnectionID(HP_Client pClient); +/* 获取 Client Socket 的地址信息 */ +HPSOCKET_API BOOL __HP_CALL HP_Client_GetLocalAddress(HP_Client pClient, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +/* 获取连接的远程主机信息 */ +HPSOCKET_API BOOL __HP_CALL HP_Client_GetRemoteHost(HP_Client pClient, TCHAR lpszHost[], int* piHostLen, USHORT* pusPort); +/* 获取连接中未发出数据的长度 */ +HPSOCKET_API BOOL __HP_CALL HP_Client_GetPendingDataLength(HP_Client pClient, int* piPending); +/* 获取连接的数据接收状态 */ +HPSOCKET_API BOOL __HP_CALL HP_Client_IsPauseReceive(HP_Client pClient, BOOL* pbPaused); +/* 检测是否有效连接 */ +HPSOCKET_API BOOL __HP_CALL HP_Client_IsConnected(HP_Client pClient); + +/* 设置地址重用选项 */ +HPSOCKET_API void __HP_CALL HP_Client_SetReuseAddressPolicy(HP_Client pClient, En_HP_ReuseAddressPolicy enReusePolicy); +/* 设置内存块缓存池大小(通常设置为 -> PUSH 模型:5 - 10;PULL 模型:10 - 20 ) */ +HPSOCKET_API void __HP_CALL HP_Client_SetFreeBufferPoolSize(HP_Client pClient, DWORD dwFreeBufferPoolSize); +/* 设置内存块缓存池回收阀值(通常设置为内存块缓存池大小的 3 倍) */ +HPSOCKET_API void __HP_CALL HP_Client_SetFreeBufferPoolHold(HP_Client pClient, DWORD dwFreeBufferPoolHold); + +/* 获取地址重用选项 */ +HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_Client_GetReuseAddressPolicy(HP_Client pClient); +/* 获取内存块缓存池大小 */ +HPSOCKET_API DWORD __HP_CALL HP_Client_GetFreeBufferPoolSize(HP_Client pClient); +/* 获取内存块缓存池回收阀值 */ +HPSOCKET_API DWORD __HP_CALL HP_Client_GetFreeBufferPoolHold(HP_Client pClient); + +/**********************************************************************************/ +/******************************* TCP Client 操作方法 *******************************/ + +/* +* 名称:发送小文件 +* 描述:向服务端发送 4096 KB 以下的小文件 +* +* 参数: lpszFileName -- 文件路径 +* pHead -- 头部附加数据 +* pTail -- 尾部附加数据 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_TcpClient_SendSmallFile(HP_Client pClient, LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail); + +/**********************************************************************************/ +/***************************** TCP Client 属性访问方法 *****************************/ + +/* 设置同步连接超时时间(毫秒) */ +HPSOCKET_API void __HP_CALL HP_TcpClient_SetSyncConnectTimeout(HP_TcpClient pClient, DWORD dwSyncConnectTimeout); +/* 设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为:(N * 1024) - sizeof(TBufferObj)) */ +HPSOCKET_API void __HP_CALL HP_TcpClient_SetSocketBufferSize(HP_TcpClient pClient, DWORD dwSocketBufferSize); +/* 设置正常心跳包间隔(毫秒,0 则不发送心跳包,默认:60 * 1000) */ +HPSOCKET_API void __HP_CALL HP_TcpClient_SetKeepAliveTime(HP_TcpClient pClient, DWORD dwKeepAliveTime); +/* 设置异常心跳包间隔(毫秒,0 不发送心跳包,,默认:20 * 1000,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线) */ +HPSOCKET_API void __HP_CALL HP_TcpClient_SetKeepAliveInterval(HP_TcpClient pClient, DWORD dwKeepAliveInterval); +/* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ +HPSOCKET_API void __HP_CALL HP_TcpClient_SetNoDelay(HP_TcpClient pClient, BOOL bNoDelay); + +/* 获取同步连接超时时间 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetSyncConnectTimeout(HP_TcpClient pClient); +/* 获取通信数据缓冲区大小 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetSocketBufferSize(HP_TcpClient pClient); +/* 获取正常心跳包间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetKeepAliveTime(HP_TcpClient pClient); +/* 获取异常心跳包间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpClient_GetKeepAliveInterval(HP_TcpClient pClient); +/* 检查是否开启 nodelay 模式 */ +HPSOCKET_API BOOL __HP_CALL HP_TcpClient_IsNoDelay(HP_TcpClient pClient); + +#ifdef _UDP_SUPPORT + +/**********************************************************************************/ +/***************************** UDP Client 属性访问方法 *****************************/ + +/* 设置数据报文最大长度(建议在局域网环境下不超过 1432 字节,在广域网环境下不超过 548 字节) */ +HPSOCKET_API void __HP_CALL HP_UdpClient_SetMaxDatagramSize(HP_UdpClient pClient, DWORD dwMaxDatagramSize); +/* 获取数据报文最大长度 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetMaxDatagramSize(HP_UdpClient pClient); + +/* 设置监测包尝试次数(0 则不发送监测跳包,如果超过最大尝试次数则认为已断线) */ +HPSOCKET_API void __HP_CALL HP_UdpClient_SetDetectAttempts(HP_UdpClient pClient, DWORD dwDetectAttempts); +/* 设置监测包发送间隔(毫秒,0 不发送监测包) */ +HPSOCKET_API void __HP_CALL HP_UdpClient_SetDetectInterval(HP_UdpClient pClient, DWORD dwDetectInterval); +/* 获取心跳检查次数 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetDetectAttempts(HP_UdpClient pClient); +/* 获取心跳检查间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpClient_GetDetectInterval(HP_UdpClient pClient); + +/**********************************************************************************/ +/*************************** UDP ARQ Client 属性访问方法 ***************************/ + +/* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetNoDelay(HP_UdpArqClient pClient, BOOL bNoDelay); +/* 设置是否关闭拥塞控制(默认:FALSE,不关闭) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetTurnoffCongestCtrl(HP_UdpArqClient pClient, BOOL bTurnOff); +/* 设置数据刷新间隔(毫秒,默认:60) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetFlushInterval(HP_UdpArqClient pClient, DWORD dwFlushInterval); +/* 设置快速重传 ACK 跨越次数(默认:0,关闭快速重传) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetResendByAcks(HP_UdpArqClient pClient, DWORD dwResendByAcks); +/* 设置发送窗口大小(数据包数量,默认:128) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetSendWndSize(HP_UdpArqClient pClient, DWORD dwSendWndSize); +/* 设置接收窗口大小(数据包数量,默认:512) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetRecvWndSize(HP_UdpArqClient pClient, DWORD dwRecvWndSize); +/* 设置最小重传超时时间(毫秒,默认:30) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMinRto(HP_UdpArqClient pClient, DWORD dwMinRto); +/* 设置快速握手次数限制(默认:5,如果为 0 则不限制) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetFastLimit(HP_UdpArqClient pClient, DWORD dwFastLimit); +/* 设置最大传输单元(默认:0,与 SetMaxDatagramSize() 一致) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMaxTransUnit(HP_UdpArqClient pClient, DWORD dwMaxTransUnit); +/* 设置最大数据包大小(默认:4096) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetMaxMessageSize(HP_UdpArqClient pClient, DWORD dwMaxMessageSize); +/* 设置握手超时时间(毫秒,默认:5000) */ +HPSOCKET_API void __HP_CALL HP_UdpArqClient_SetHandShakeTimeout(HP_UdpArqClient pClient, DWORD dwHandShakeTimeout); + +/* 检测是否开启 nodelay 模式 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_IsNoDelay(HP_UdpArqClient pClient); +/* 检测是否关闭拥塞控制 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_IsTurnoffCongestCtrl(HP_UdpArqClient pClient); +/* 获取数据刷新间隔 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetFlushInterval(HP_UdpArqClient pClient); +/* 获取快速重传 ACK 跨越次数 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetResendByAcks(HP_UdpArqClient pClient); +/* 获取发送窗口大小 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetSendWndSize(HP_UdpArqClient pClient); +/* 获取接收窗口大小 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetRecvWndSize(HP_UdpArqClient pClient); +/* 获取最小重传超时时间 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMinRto(HP_UdpArqClient pClient); +/* 获取快速握手次数限制 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetFastLimit(HP_UdpArqClient pClient); +/* 获取最大传输单元 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMaxTransUnit(HP_UdpArqClient pClient); +/* 获取最大数据包大小 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetMaxMessageSize(HP_UdpArqClient pClient); +/* 获取握手超时时间 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpArqClient_GetHandShakeTimeout(HP_UdpArqClient pClient); + +/* 获取等待发送包数量 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpArqClient_GetWaitingSendMessageCount(HP_UdpArqClient pClient, int* piCount); + +/**********************************************************************************/ +/****************************** UDP Cast 属性访问方法 ******************************/ + +/* 设置数据报文最大长度(建议在局域网环境下不超过 1432 字节,在广域网环境下不超过 548 字节) */ +HPSOCKET_API void __HP_CALL HP_UdpCast_SetMaxDatagramSize(HP_UdpCast pCast, DWORD dwMaxDatagramSize); +/* 获取数据报文最大长度 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpCast_GetMaxDatagramSize(HP_UdpCast pCast); +/* 获取当前数据报的远程地址信息(通常在 OnReceive 事件中调用) */ +HPSOCKET_API BOOL __HP_CALL HP_UdpCast_GetRemoteAddress(HP_UdpCast pCast, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +/* 设置传播模式(组播或广播) */ +HPSOCKET_API void __HP_CALL HP_UdpCast_SetCastMode(HP_UdpCast pCast, En_HP_CastMode enCastMode); +/* 获取传播模式 */ +HPSOCKET_API En_HP_CastMode __HP_CALL HP_UdpCast_GetCastMode(HP_UdpCast pCast); +/* 设置组播报文的 TTL(0 - 255) */ +HPSOCKET_API void __HP_CALL HP_UdpCast_SetMultiCastTtl(HP_UdpCast pCast, int iMCTtl); +/* 获取组播报文的 TTL */ +HPSOCKET_API int __HP_CALL HP_UdpCast_GetMultiCastTtl(HP_UdpCast pCast); +/* 设置是否启用组播环路(TRUE or FALSE) */ +HPSOCKET_API void __HP_CALL HP_UdpCast_SetMultiCastLoop(HP_UdpCast pCast, BOOL bMCLoop); +/* 检测是否启用组播环路 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpCast_IsMultiCastLoop(HP_UdpCast pCast); + +/**********************************************************************************/ +/****************************** UDP Node 组件操作方法 ******************************/ + +/* +* 名称:启动通信组件 +* 描述:启动 UDP 节点通信组件,启动完成后可开始收发数据 +* +* 参数: lpszBindAddress -- 绑定地址(默认:nullptr,绑定任意地址) +* usPort -- 本地端口(默认:0) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Start(HP_UdpNode pNode, LPCTSTR lpszBindAddress, USHORT usPort); + +/* +* 名称:启动通信组件 +* 描述:启动 UDP 节点通信组件,启动完成后可开始收发数据 +* +* 参数: lpszBindAddress -- 绑定地址(默认:nullptr,绑定任意地址) +* usPort -- 本地端口(默认:0) +* enCastMode -- 传播模式(默认:CM_UNICAST) +* lpszCastAddress -- 传播地址(默认:nullptr,当 enCaseMode 为 CM_MULTICAST 或 CM_BROADCAST 时有效) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_StartWithCast(HP_UdpNode pNode, LPCTSTR lpszBindAddress, USHORT usPort, En_HP_CastMode enCastMode, LPCTSTR lpszCastAddress); + +/* +* 名称:关闭通信组件 +* 描述:关闭 UDP 节点通信组件,关闭完成后释放所有资源 +* +* 参数: +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Stop(HP_UdpNode pNode); + +/* +* 名称:等待 +* 描述:等待通信组件停止运行 +* +* 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Wait(HP_UdpNode pNode, DWORD dwMilliseconds); + +/* +* 名称:发送数据 +* 描述:向指定地址发送数据 +* +* 参数: lpszRemoteAddress -- 远程地址 +* usRemotePort -- 远程端口 +* pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_Send(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength); + +/* +* 名称:发送数据 +* 描述:向指定地址发送数据 +* +* 参数: lpszRemoteAddress -- 远程地址 +* usRemotePort -- 远程端口 +* pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* iOffset -- 发送缓冲区指针偏移量 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendPart(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength, int iOffset); + +/* +* 名称:发送多组数据 +* 描述:向指定地址发送多组数据,把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) +* +* 参数: lpszRemoteAddress -- 远程地址 +* usRemotePort -- 远程端口 +* pBuffers -- 发送缓冲区数组 +* iCount -- 发送缓冲区数目 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendPackets(HP_UdpNode pNode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const WSABUF pBuffers[], int iCount); + +/* +* 名称:发送数据 +* 描述:向传播地址发送数据 +* +* 参数: pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCast(HP_UdpNode pNode, const BYTE* pBuffer, int iLength); + +/* +* 名称:发送数据 +* 描述:向传播地址发送数据 +* +* 参数: pBuffer -- 发送缓冲区 +* iLength -- 发送缓冲区长度 +* iOffset -- 发送缓冲区指针偏移量 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCastPart(HP_UdpNode pNode, const BYTE* pBuffer, int iLength, int iOffset); + +/* +* 名称:发送多组数据 +* 描述:向传播地址发送多组数据,把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) +* +* 参数: pBuffers -- 发送缓冲区数组 +* iCount -- 发送缓冲区数目 +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_SendCastPackets(HP_UdpNode pNode, const WSABUF pBuffers[], int iCount); + +/**********************************************************************************/ +/****************************** UDP Node 属性访问方法 ******************************/ + +/* 设置附加数据 */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetExtra(HP_UdpNode pNode, PVOID pExtra); + +/* 获取附加数据 */ +HPSOCKET_API PVOID __HP_CALL HP_UdpNode_GetExtra(HP_UdpNode pNode); + +/* 检查通信组件是否已启动 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_HasStarted(HP_UdpNode pNode); +/* 查看通信组件当前状态 */ +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_UdpNode_GetState(HP_UdpNode pNode); +/* 获取最近一次失败操作的错误代码 */ +HPSOCKET_API En_HP_SocketError __HP_CALL HP_UdpNode_GetLastError(HP_UdpNode pNode); +/* 获取最近一次失败操作的错误描述 */ +HPSOCKET_API LPCTSTR __HP_CALL HP_UdpNode_GetLastErrorDesc(HP_UdpNode pNode); +/* 获取本节点地址 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetLocalAddress(HP_UdpNode pNode, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +/* 获取本节点传播地址 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetCastAddress(HP_UdpNode pNode, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +/* 获取传播模式 */ +HPSOCKET_API En_HP_CastMode __HP_CALL HP_UdpNode_GetCastMode(HP_UdpNode pNode); +/* 获取未发出数据的长度 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_GetPendingDataLength(HP_UdpNode pNode, int* piPending); + +/* 设置数据报文最大长度(建议在局域网环境下不超过 1432 字节,在广域网环境下不超过 548 字节) */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetMaxDatagramSize(HP_UdpNode pNode, DWORD dwMaxDatagramSize); +/* 获取数据报文最大长度 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetMaxDatagramSize(HP_UdpNode pNode); + +/* 设置组播报文的 TTL(0 - 255) */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetMultiCastTtl(HP_UdpNode pNode, int iMCTtl); +/* 获取组播报文的 TTL */ +HPSOCKET_API int __HP_CALL HP_UdpNode_GetMultiCastTtl(HP_UdpNode pNode); + +/* 设置是否启用组播环路(TRUE or FALSE) */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetMultiCastLoop(HP_UdpNode pNode, BOOL bMCLoop); +/* 检测是否启用组播环路 */ +HPSOCKET_API BOOL __HP_CALL HP_UdpNode_IsMultiCastLoop(HP_UdpNode pNode); + +/* 设置地址重用选项 */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetReuseAddressPolicy(HP_UdpNode pNode, En_HP_ReuseAddressPolicy enReusePolicy); +/* 设置工作线程数量(通常设置为 2 * CPU + 2) */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetWorkerThreadCount(HP_UdpNode pNode, DWORD dwWorkerThreadCount); +/* 设置 Receive 预投递数量(根据负载调整设置,Receive 预投递数量越大则丢包概率越小) */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetPostReceiveCount(HP_UdpNode pNode, DWORD dwPostReceiveCount); +/* 设置内存块缓存池大小 */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetFreeBufferPoolSize(HP_UdpNode pNode, DWORD dwFreeBufferPoolSize); +/* 设置内存块缓存池回收阀值 */ +HPSOCKET_API void __HP_CALL HP_UdpNode_SetFreeBufferPoolHold(HP_UdpNode pNode, DWORD dwFreeBufferPoolHold); + +/* 获取地址重用选项 */ +HPSOCKET_API En_HP_ReuseAddressPolicy __HP_CALL HP_UdpNode_GetReuseAddressPolicy(HP_UdpNode pNode); +/* 获取工作线程数量 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetWorkerThreadCount(HP_UdpNode pNode); +/* 获取 Receive 预投递数量 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetPostReceiveCount(HP_UdpNode pNode); +/* 获取内存块缓存池大小 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetFreeBufferPoolSize(HP_UdpNode pNode); +/* 获取内存块缓存池回收阀值 */ +HPSOCKET_API DWORD __HP_CALL HP_UdpNode_GetFreeBufferPoolHold(HP_UdpNode pNode); + +#endif + +/***************************************************************************************/ +/***************************** TCP Pull Server 组件操作方法 *****************************/ + +/* +* 名称:抓取数据 +* 描述:用户通过该方法从 Socket 组件中抓取数据 +* +* 参数: dwConnID -- 连接 ID +* pData -- 抓取缓冲区 +* iLength -- 抓取数据长度 +* 返回值: En_HP_FetchResult +*/ +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullServer_Fetch(HP_TcpPullServer pServer, HP_CONNID dwConnID, BYTE* pData, int iLength); + +/* +* 名称:窥探数据(不会移除缓冲区数据) +* 描述:用户通过该方法从 Socket 组件中窥探数据 +* +* 参数: dwConnID -- 连接 ID +* pData -- 窥探缓冲区 +* iLength -- 窥探数据长度 +* 返回值: En_HP_FetchResult +*/ +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullServer_Peek(HP_TcpPullServer pServer, HP_CONNID dwConnID, BYTE* pData, int iLength); + +/***************************************************************************************/ +/***************************** TCP Pull Server 属性访问方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pull Agent 组件操作方法 *****************************/ + +/* +* 名称:抓取数据 +* 描述:用户通过该方法从 Socket 组件中抓取数据 +* +* 参数: dwConnID -- 连接 ID +* pData -- 抓取缓冲区 +* iLength -- 抓取数据长度 +* 返回值: En_HP_FetchResult +*/ +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullAgent_Fetch(HP_TcpPullAgent pAgent, HP_CONNID dwConnID, BYTE* pData, int iLength); + +/* +* 名称:窥探数据(不会移除缓冲区数据) +* 描述:用户通过该方法从 Socket 组件中窥探数据 +* +* 参数: dwConnID -- 连接 ID +* pData -- 窥探缓冲区 +* iLength -- 窥探数据长度 +* 返回值: En_HP_FetchResult +*/ +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullAgent_Peek(HP_TcpPullAgent pAgent, HP_CONNID dwConnID, BYTE* pData, int iLength); + +/***************************************************************************************/ +/***************************** TCP Pull Agent 属性访问方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pull Client 组件操作方法 *****************************/ + +/* +* 名称:抓取数据 +* 描述:用户通过该方法从 Socket 组件中抓取数据 +* +* 参数: dwConnID -- 连接 ID +* pData -- 抓取缓冲区 +* iLength -- 抓取数据长度 +* 返回值: En_HP_FetchResult +*/ +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullClient_Fetch(HP_TcpPullClient pClient, BYTE* pData, int iLength); + +/* +* 名称:窥探数据(不会移除缓冲区数据) +* 描述:用户通过该方法从 Socket 组件中窥探数据 +* +* 参数: dwConnID -- 连接 ID +* pData -- 窥探缓冲区 +* iLength -- 窥探数据长度 +* 返回值: En_HP_FetchResult +*/ +HPSOCKET_API En_HP_FetchResult __HP_CALL HP_TcpPullClient_Peek(HP_TcpPullClient pClient, BYTE* pData, int iLength); + +/***************************************************************************************/ +/***************************** TCP Pull Client 属性访问方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pack Server 组件操作方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pack Server 属性访问方法 *****************************/ + +/* 设置数据包最大长度(有效数据包最大长度不能超过 4194303/0x3FFFFF 字节,默认:262144/0x40000) */ +HPSOCKET_API void __HP_CALL HP_TcpPackServer_SetMaxPackSize(HP_TcpPackServer pServer, DWORD dwMaxPackSize); +/* 设置包头标识(有效包头标识取值范围 0 ~ 1023/0x3FF,当包头标识为 0 时不校验包头,默认:0) */ +HPSOCKET_API void __HP_CALL HP_TcpPackServer_SetPackHeaderFlag(HP_TcpPackServer pServer, USHORT usPackHeaderFlag); + +/* 获取数据包最大长度 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpPackServer_GetMaxPackSize(HP_TcpPackServer pServer); +/* 获取包头标识 */ +HPSOCKET_API USHORT __HP_CALL HP_TcpPackServer_GetPackHeaderFlag(HP_TcpPackServer pServer); + +/***************************************************************************************/ +/***************************** TCP Pack Agent 组件操作方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pack Agent 属性访问方法 *****************************/ + +/* 设置数据包最大长度(有效数据包最大长度不能超过 4194303/0x3FFFFF 字节,默认:262144/0x40000) */ +HPSOCKET_API void __HP_CALL HP_TcpPackAgent_SetMaxPackSize(HP_TcpPackAgent pAgent, DWORD dwMaxPackSize); +/* 设置包头标识(有效包头标识取值范围 0 ~ 1023/0x3FF,当包头标识为 0 时不校验包头,默认:0) */ +HPSOCKET_API void __HP_CALL HP_TcpPackAgent_SetPackHeaderFlag(HP_TcpPackAgent pAgent, USHORT usPackHeaderFlag); + +/* 获取数据包最大长度 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpPackAgent_GetMaxPackSize(HP_TcpPackAgent pAgent); +/* 获取包头标识 */ +HPSOCKET_API USHORT __HP_CALL HP_TcpPackAgent_GetPackHeaderFlag(HP_TcpPackAgent pAgent); + +/***************************************************************************************/ +/***************************** TCP Pack Client 组件操作方法 *****************************/ + +/***************************************************************************************/ +/***************************** TCP Pack Client 属性访问方法 *****************************/ + +/* 设置数据包最大长度(有效数据包最大长度不能超过 4194303/0x3FFFFF 字节,默认:262144/0x40000) */ +HPSOCKET_API void __HP_CALL HP_TcpPackClient_SetMaxPackSize(HP_TcpPackClient pClient, DWORD dwMaxPackSize); +/* 设置包头标识(有效包头标识取值范围 0 ~ 1023/0x3FF,当包头标识为 0 时不校验包头,默认:0) */ +HPSOCKET_API void __HP_CALL HP_TcpPackClient_SetPackHeaderFlag(HP_TcpPackClient pClient, USHORT usPackHeaderFlag); + +/* 获取数据包最大长度 */ +HPSOCKET_API DWORD __HP_CALL HP_TcpPackClient_GetMaxPackSize(HP_TcpPackClient pClient); +/* 获取包头标识 */ +HPSOCKET_API USHORT __HP_CALL HP_TcpPackClient_GetPackHeaderFlag(HP_TcpPackClient pClient); + +/*****************************************************************************************************************************************************/ +/*************************************************************** Global Function Exports *************************************************************/ +/*****************************************************************************************************************************************************/ + +// 获取 HPSocket 版本号(4 个字节分别为:主版本号,子版本号,修正版本号,构建编号) +HPSOCKET_API DWORD __HP_CALL HP_GetHPSocketVersion(); + +/* 获取错误描述文本 */ +HPSOCKET_API LPCTSTR __HP_CALL HP_GetSocketErrorDesc(En_HP_SocketError enCode); +// 调用系统的 errno 方法获取系统错误代码 +HPSOCKET_API DWORD __HP_CALL SYS_GetLastError(); +// 调用系统的 strerror() 方法获取系统错误代码描述 +HPSOCKET_API LPCSTR __HP_CALL SYS_GetLastErrorStr(); +// 调用系统的 setsockopt() +HPSOCKET_API int __HP_CALL SYS_SetSocketOption(SOCKET sock, int level, int name, LPVOID val, int len); +// 调用系统的 getsockopt() +HPSOCKET_API int __HP_CALL SYS_GetSocketOption(SOCKET sock, int level, int name, LPVOID val, int* len); +// 调用系统的 ioctlsocket() +HPSOCKET_API int __HP_CALL SYS_IoctlSocket(SOCKET sock, long cmd, ULONG* arg); + +// 调用系统的 fcntl() 设置 F_SETFL 属性 +HPSOCKET_API BOOL __HP_CALL SYS_fcntl_SETFL(FD fd, INT fl, BOOL bSet); + +// 设置 FD 选项:O_NONBLOCK +HPSOCKET_API int __HP_CALL SYS_SSO_NoBlock(SOCKET sock, BOOL bNoBlock); +// 设置 socket 选项:IPPROTO_TCP -> TCP_NODELAY +HPSOCKET_API int __HP_CALL SYS_SSO_NoDelay(SOCKET sock, BOOL bNoDelay); +// 设置 socket 选项:SOL_SOCKET -> SO_DONTLINGER +HPSOCKET_API int __HP_CALL SYS_SSO_DontLinger(SOCKET sock, BOOL bDont); +// 设置 socket 选项:SOL_SOCKET -> SO_LINGER +HPSOCKET_API int __HP_CALL SYS_SSO_Linger(SOCKET sock, USHORT l_onoff, USHORT l_linger); +// 设置 socket 选项:SOL_SOCKET -> SO_RCVBUF +HPSOCKET_API int __HP_CALL SYS_SSO_RecvBuffSize(SOCKET sock, int size); +// 设置 socket 选项:SOL_SOCKET -> SO_SNDBUF +HPSOCKET_API int __HP_CALL SYS_SSO_SendBuffSize(SOCKET sock, int size); +// 设置 socket 选项:SOL_SOCKET -> SO_RCVTIMEO +HPSOCKET_API int __HP_CALL SYS_SSO_RecvTimeOut(SOCKET sock, int ms); +// 设置 socket 选项:SOL_SOCKET -> SO_SNDTIMEO +HPSOCKET_API int __HP_CALL SYS_SSO_SendTimeOut(SOCKET sock, int ms); +// 设置 socket 选项:SOL_SOCKET -> SO_REUSEADDR / SO_REUSEPORT +HPSOCKET_API int __HP_CALL SYS_SSO_ReuseAddress(SOCKET sock, En_HP_ReuseAddressPolicy opt); + +// 获取 SOCKET 本地地址信息 +HPSOCKET_API BOOL __HP_CALL SYS_GetSocketLocalAddress(SOCKET socket, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); +// 获取 SOCKET 远程地址信息 +HPSOCKET_API BOOL __HP_CALL SYS_GetSocketRemoteAddress(SOCKET socket, TCHAR lpszAddress[], int* piAddressLen, USHORT* pusPort); + +/* 枚举主机 IP 地址 */ +HPSOCKET_API BOOL __HP_CALL SYS_EnumHostIPAddresses(LPCTSTR lpszHost, En_HP_IPAddrType enType, HP_LPTIPAddr** lpppIPAddr, int* piIPAddrCount); +/* 释放 HP_LPTIPAddr* */ +HPSOCKET_API BOOL __HP_CALL SYS_FreeHostIPAddresses(HP_LPTIPAddr* lppIPAddr); +/* 检查字符串是否符合 IP 地址格式 */ +HPSOCKET_API BOOL __HP_CALL SYS_IsIPAddress(LPCTSTR lpszAddress, En_HP_IPAddrType* penType); +/* 通过主机名获取 IP 地址 */ +HPSOCKET_API BOOL __HP_CALL SYS_GetIPAddress(LPCTSTR lpszHost, TCHAR lpszIP[], int* piIPLenth, En_HP_IPAddrType* penType); + +/* 64 位网络字节序转主机字节序 */ +HPSOCKET_API ULONGLONG __HP_CALL SYS_NToH64(ULONGLONG value); +/* 64 位主机字节序转网络字节序 */ +HPSOCKET_API ULONGLONG __HP_CALL SYS_HToN64(ULONGLONG value); +/* 短整型高低字节交换 */ +HPSOCKET_API USHORT __HP_CALL SYS_SwapEndian16(USHORT value); +/* 长整型高低字节交换 */ +HPSOCKET_API DWORD __HP_CALL SYS_SwapEndian32(DWORD value); +/* 检查是否小端字节序 */ +HPSOCKET_API BOOL __HP_CALL SYS_IsLittleEndian(); + +/* 分配内存 */ +HPSOCKET_API LPBYTE __HP_CALL SYS_Malloc(int size); +/* 重新分配内存 */ +HPSOCKET_API LPBYTE __HP_CALL SYS_Realloc(LPBYTE p, int size); +/* 释放内存 */ +HPSOCKET_API VOID __HP_CALL SYS_Free(LPBYTE p); +/* 分配内存块 */ +HPSOCKET_API LPVOID __HP_CALL SYS_Calloc(int number, int size); + +// 计算 Base64 编码后长度 +HPSOCKET_API DWORD __HP_CALL SYS_GuessBase64EncodeBound(DWORD dwSrcLen); +// 计算 Base64 解码后长度 +HPSOCKET_API DWORD __HP_CALL SYS_GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen); +// Base64 编码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); +// Base64 解码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); + +// 计算 URL 编码后长度 +HPSOCKET_API DWORD __HP_CALL SYS_GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen); +// 计算 URL 解码后长度 +HPSOCKET_API DWORD __HP_CALL SYS_GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen); +// URL 编码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); +// URL 解码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); + +#ifdef _ZLIB_SUPPORT + +// 普通压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +//(默认参数:iLevel -> -1,iMethod -> 8,iWindowBits -> 15,iMemLevel -> 8,iStrategy -> 0) +HPSOCKET_API int __HP_CALL SYS_Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); +// 高级压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iLevel /*= -1*/, int iMethod /*= 8*/, int iWindowBits /*= 15*/, int iMemLevel /*= 8*/, int iStrategy /*= 0*/); +// 普通解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +// (默认参数:iWindowBits -> 15) +HPSOCKET_API int __HP_CALL SYS_Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); +// 高级解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iWindowBits /*= 15*/); +// 推测压缩结果长度 +HPSOCKET_API DWORD __HP_CALL SYS_GuessCompressBound(DWORD dwSrcLen, BOOL bGZip); + +// Gzip 压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); +// Gzip 解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); +// 推测 Gzip 解压结果长度(如果返回 0 或不合理值则说明输入内容并非有效的 Gzip 格式) +HPSOCKET_API DWORD __HP_CALL SYS_GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen); + +#endif + +#ifdef _BROTLI_SUPPORT + +// Brotli 压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +//(默认参数:iQuality -> 11,iWindow -> 22,iMode -> 0) +HPSOCKET_API int __HP_CALL SYS_BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); +// Brotli 高级压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen, int iQuality /*= 11*/, int iWindow /*= 22*/, int iMode /*= 0*/); +// Brotli 解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足) +HPSOCKET_API int __HP_CALL SYS_BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD* pdwDestLen); +// Brotli 推测压缩结果长度 +HPSOCKET_API DWORD __HP_CALL SYS_BrotliGuessCompressBound(DWORD dwSrcLen); + +#endif + +#ifdef _ICONV_SUPPORT + +// Charset A -> Charset B +HPSOCKET_API BOOL __HP_CALL SYS_CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int* piOutBufLen); + +// GBK -> UNICODE +HPSOCKET_API BOOL __HP_CALL SYS_GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int* piDestLength); +// UNICODE -> GBK +HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int* piDestLength); +// UTF8 -> UNICODE +HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int* piDestLength); +// UNICODE -> UTF8 +HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int* piDestLength); +// GBK -> UTF8 +HPSOCKET_API BOOL __HP_CALL SYS_GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int* piDestLength); +// UTF8 -> GBK +HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int* piDestLength); + +// GBK -> UNICODE +HPSOCKET_API BOOL __HP_CALL SYS_GbkToUnicode(const char szSrc[], WCHAR szDest[], int* piDestLength); +// UNICODE -> GBK +HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToGbk(const WCHAR szSrc[], char szDest[], int* piDestLength); +// UTF8 -> UNICODE +HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int* piDestLength); +// UNICODE -> UTF8 +HPSOCKET_API BOOL __HP_CALL SYS_UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int* piDestLength); +// GBK -> UTF8 +HPSOCKET_API BOOL __HP_CALL SYS_GbkToUtf8(const char szSrc[], char szDest[], int* piDestLength); +// UTF8 -> GBK +HPSOCKET_API BOOL __HP_CALL SYS_Utf8ToGbk(const char szSrc[], char szDest[], int* piDestLength); + +#endif + +/*****************************************************************************************************************************************************/ +/******************************************************************** HTTP Exports *******************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _HTTP_SUPPORT + +/****************************************************/ +/******************* HTTP 回调函数 *******************/ + +/* HTTP 回调函数 */ +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnMessageBegin) (HP_Http pSender, HP_CONNID dwConnID); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnRequestLine) (HP_Http pSender, HP_CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnStatusLine) (HP_Http pSender, HP_CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnHeader) (HP_Http pSender, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnHeadersComplete) (HP_Http pSender, HP_CONNID dwConnID); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnBody) (HP_Http pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnChunkHeader) (HP_Http pSender, HP_CONNID dwConnID, int iLength); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnChunkComplete) (HP_Http pSender, HP_CONNID dwConnID); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnMessageComplete) (HP_Http pSender, HP_CONNID dwConnID); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnUpgrade) (HP_Http pSender, HP_CONNID dwConnID, En_HP_HttpUpgradeType enUpgradeType); +typedef En_HP_HttpParseResult (__HP_CALL *HP_FN_Http_OnParseError) (HP_Http pSender, HP_CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc); + +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Http_OnWSMessageHeader) (HP_Http pSender, HP_CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Http_OnWSMessageBody) (HP_Http pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +typedef En_HP_HandleResult (__HP_CALL *HP_FN_Http_OnWSMessageComplete)(HP_Http pSender, HP_CONNID dwConnID); + +/* HTTP Server 回调函数 */ +typedef HP_FN_Http_OnMessageBegin HP_FN_HttpServer_OnMessageBegin; +typedef HP_FN_Http_OnRequestLine HP_FN_HttpServer_OnRequestLine; +typedef HP_FN_Http_OnHeader HP_FN_HttpServer_OnHeader; +typedef HP_FN_Http_OnHeadersComplete HP_FN_HttpServer_OnHeadersComplete; +typedef HP_FN_Http_OnBody HP_FN_HttpServer_OnBody; +typedef HP_FN_Http_OnChunkHeader HP_FN_HttpServer_OnChunkHeader; +typedef HP_FN_Http_OnChunkComplete HP_FN_HttpServer_OnChunkComplete; +typedef HP_FN_Http_OnMessageComplete HP_FN_HttpServer_OnMessageComplete; +typedef HP_FN_Http_OnUpgrade HP_FN_HttpServer_OnUpgrade; +typedef HP_FN_Http_OnParseError HP_FN_HttpServer_OnParseError; + +typedef HP_FN_Http_OnWSMessageHeader HP_FN_HttpServer_OnWSMessageHeader; +typedef HP_FN_Http_OnWSMessageBody HP_FN_HttpServer_OnWSMessageBody; +typedef HP_FN_Http_OnWSMessageComplete HP_FN_HttpServer_OnWSMessageComplete; + +typedef HP_FN_Server_OnPrepareListen HP_FN_HttpServer_OnPrepareListen; +typedef HP_FN_Server_OnAccept HP_FN_HttpServer_OnAccept; +typedef HP_FN_Server_OnHandShake HP_FN_HttpServer_OnHandShake; +typedef HP_FN_Server_OnReceive HP_FN_HttpServer_OnReceive; +typedef HP_FN_Server_OnSend HP_FN_HttpServer_OnSend; +typedef HP_FN_Server_OnClose HP_FN_HttpServer_OnClose; +typedef HP_FN_Server_OnShutdown HP_FN_HttpServer_OnShutdown; + +/* HTTP Agent 回调函数 */ +typedef HP_FN_Http_OnMessageBegin HP_FN_HttpAgent_OnMessageBegin; +typedef HP_FN_Http_OnStatusLine HP_FN_HttpAgent_OnStatusLine; +typedef HP_FN_Http_OnHeader HP_FN_HttpAgent_OnHeader; +typedef HP_FN_Http_OnHeadersComplete HP_FN_HttpAgent_OnHeadersComplete; +typedef HP_FN_Http_OnBody HP_FN_HttpAgent_OnBody; +typedef HP_FN_Http_OnChunkHeader HP_FN_HttpAgent_OnChunkHeader; +typedef HP_FN_Http_OnChunkComplete HP_FN_HttpAgent_OnChunkComplete; +typedef HP_FN_Http_OnMessageComplete HP_FN_HttpAgent_OnMessageComplete; +typedef HP_FN_Http_OnUpgrade HP_FN_HttpAgent_OnUpgrade; +typedef HP_FN_Http_OnParseError HP_FN_HttpAgent_OnParseError; + +typedef HP_FN_Http_OnWSMessageHeader HP_FN_HttpAgent_OnWSMessageHeader; +typedef HP_FN_Http_OnWSMessageBody HP_FN_HttpAgent_OnWSMessageBody; +typedef HP_FN_Http_OnWSMessageComplete HP_FN_HttpAgent_OnWSMessageComplete; + +typedef HP_FN_Agent_OnPrepareConnect HP_FN_HttpAgent_OnPrepareConnect; +typedef HP_FN_Agent_OnConnect HP_FN_HttpAgent_OnConnect; +typedef HP_FN_Agent_OnHandShake HP_FN_HttpAgent_OnHandShake; +typedef HP_FN_Agent_OnReceive HP_FN_HttpAgent_OnReceive; +typedef HP_FN_Agent_OnSend HP_FN_HttpAgent_OnSend; +typedef HP_FN_Agent_OnClose HP_FN_HttpAgent_OnClose; +typedef HP_FN_Agent_OnShutdown HP_FN_HttpAgent_OnShutdown; + +/* HTTP Client 回调函数 */ +typedef HP_FN_Http_OnMessageBegin HP_FN_HttpClient_OnMessageBegin; +typedef HP_FN_Http_OnStatusLine HP_FN_HttpClient_OnStatusLine; +typedef HP_FN_Http_OnHeader HP_FN_HttpClient_OnHeader; +typedef HP_FN_Http_OnHeadersComplete HP_FN_HttpClient_OnHeadersComplete; +typedef HP_FN_Http_OnBody HP_FN_HttpClient_OnBody; +typedef HP_FN_Http_OnChunkHeader HP_FN_HttpClient_OnChunkHeader; +typedef HP_FN_Http_OnChunkComplete HP_FN_HttpClient_OnChunkComplete; +typedef HP_FN_Http_OnMessageComplete HP_FN_HttpClient_OnMessageComplete; +typedef HP_FN_Http_OnUpgrade HP_FN_HttpClient_OnUpgrade; +typedef HP_FN_Http_OnParseError HP_FN_HttpClient_OnParseError; + +typedef HP_FN_Http_OnWSMessageHeader HP_FN_HttpClient_OnWSMessageHeader; +typedef HP_FN_Http_OnWSMessageBody HP_FN_HttpClient_OnWSMessageBody; +typedef HP_FN_Http_OnWSMessageComplete HP_FN_HttpClient_OnWSMessageComplete; + +typedef HP_FN_Client_OnPrepareConnect HP_FN_HttpClient_OnPrepareConnect; +typedef HP_FN_Client_OnConnect HP_FN_HttpClient_OnConnect; +typedef HP_FN_Client_OnHandShake HP_FN_HttpClient_OnHandShake; +typedef HP_FN_Client_OnReceive HP_FN_HttpClient_OnReceive; +typedef HP_FN_Client_OnSend HP_FN_HttpClient_OnSend; +typedef HP_FN_Client_OnClose HP_FN_HttpClient_OnClose; + +/****************************************************/ +/***************** HTTP 对象创建函数 *****************/ + +// 创建 HP_HttpServer 对象 +HPSOCKET_API HP_HttpServer __HP_CALL Create_HP_HttpServer(HP_HttpServerListener pListener); +// 创建 HP_HttpAgent 对象 +HPSOCKET_API HP_HttpAgent __HP_CALL Create_HP_HttpAgent(HP_HttpAgentListener pListener); +// 创建 HP_HttpClient 对象 +HPSOCKET_API HP_HttpClient __HP_CALL Create_HP_HttpClient(HP_HttpClientListener pListener); +// 创建 HP_HttpSyncClient 对象(pListener 参数可传入 nullptr) +HPSOCKET_API HP_HttpSyncClient __HP_CALL Create_HP_HttpSyncClient(HP_HttpClientListener pListener /*= nullptr*/); + +// 销毁 HP_HttpServer 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpServer(HP_HttpServer pServer); +// 销毁 HP_HttpAgent 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpAgent(HP_HttpAgent pAgent); +// 销毁 HP_HttpClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpClient(HP_HttpClient pClient); +// 销毁 HP_HttpSyncClient 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpSyncClient(HP_HttpSyncClient pClient); + +// 创建 HP_HttpServerListener 对象 +HPSOCKET_API HP_HttpServerListener __HP_CALL Create_HP_HttpServerListener(); +// 创建 HP_HttpAgentListener 对象 +HPSOCKET_API HP_HttpAgentListener __HP_CALL Create_HP_HttpAgentListener(); +// 创建 HP_HttpClientListener 对象 +HPSOCKET_API HP_HttpClientListener __HP_CALL Create_HP_HttpClientListener(); + +// 销毁 HP_HttpServerListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpServerListener(HP_HttpServerListener pListener); +// 销毁 HP_HttpAgentListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpAgentListener(HP_HttpAgentListener pListener); +// 销毁 HP_HttpClientListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_HttpClientListener(HP_HttpClientListener pListener); + +/**********************************************************************************/ +/*************************** HTTP Server 回调函数设置方法 **************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnMessageBegin(HP_HttpServerListener pListener , HP_FN_HttpServer_OnMessageBegin fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnRequestLine(HP_HttpServerListener pListener , HP_FN_HttpServer_OnRequestLine fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHeader(HP_HttpServerListener pListener , HP_FN_HttpServer_OnHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHeadersComplete(HP_HttpServerListener pListener , HP_FN_HttpServer_OnHeadersComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnBody(HP_HttpServerListener pListener , HP_FN_HttpServer_OnBody fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnChunkHeader(HP_HttpServerListener pListener , HP_FN_HttpServer_OnChunkHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnChunkComplete(HP_HttpServerListener pListener , HP_FN_HttpServer_OnChunkComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnMessageComplete(HP_HttpServerListener pListener , HP_FN_HttpServer_OnMessageComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnUpgrade(HP_HttpServerListener pListener , HP_FN_HttpServer_OnUpgrade fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnParseError(HP_HttpServerListener pListener , HP_FN_HttpServer_OnParseError fn); + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageHeader(HP_HttpServerListener pListener , HP_FN_HttpServer_OnWSMessageHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageBody(HP_HttpServerListener pListener , HP_FN_HttpServer_OnWSMessageBody fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnWSMessageComplete(HP_HttpServerListener pListener, HP_FN_HttpServer_OnWSMessageComplete fn); + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnPrepareListen(HP_HttpServerListener pListener , HP_FN_HttpServer_OnPrepareListen fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnAccept(HP_HttpServerListener pListener , HP_FN_HttpServer_OnAccept fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnHandShake(HP_HttpServerListener pListener , HP_FN_HttpServer_OnHandShake fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnReceive(HP_HttpServerListener pListener , HP_FN_HttpServer_OnReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnSend(HP_HttpServerListener pListener , HP_FN_HttpServer_OnSend fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnClose(HP_HttpServerListener pListener , HP_FN_HttpServer_OnClose fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpServer_OnShutdown(HP_HttpServerListener pListener , HP_FN_HttpServer_OnShutdown fn); + +/**********************************************************************************/ +/**************************** HTTP Agent 回调函数设置方法 **************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnMessageBegin(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnMessageBegin fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnStatusLine(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnStatusLine fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHeader(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHeadersComplete(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnHeadersComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnBody(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnBody fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnChunkHeader(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnChunkHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnChunkComplete(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnChunkComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnMessageComplete(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnMessageComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnUpgrade(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnUpgrade fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnParseError(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnParseError fn); + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageHeader(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnWSMessageHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageBody(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnWSMessageBody fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnWSMessageComplete(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnWSMessageComplete fn); + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnPrepareConnect(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnPrepareConnect fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnConnect(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnConnect fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnHandShake(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnHandShake fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnReceive(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnSend(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnSend fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnClose(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnClose fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpAgent_OnShutdown(HP_HttpAgentListener pListener , HP_FN_HttpAgent_OnShutdown fn); + +/**********************************************************************************/ +/*************************** HTTP Client 回调函数设置方法 **************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnMessageBegin(HP_HttpClientListener pListener , HP_FN_HttpClient_OnMessageBegin fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnStatusLine(HP_HttpClientListener pListener , HP_FN_HttpClient_OnStatusLine fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHeader(HP_HttpClientListener pListener , HP_FN_HttpClient_OnHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHeadersComplete(HP_HttpClientListener pListener , HP_FN_HttpClient_OnHeadersComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnBody(HP_HttpClientListener pListener , HP_FN_HttpClient_OnBody fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnChunkHeader(HP_HttpClientListener pListener , HP_FN_HttpClient_OnChunkHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnChunkComplete(HP_HttpClientListener pListener , HP_FN_HttpClient_OnChunkComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnMessageComplete(HP_HttpClientListener pListener , HP_FN_HttpClient_OnMessageComplete fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnUpgrade(HP_HttpClientListener pListener , HP_FN_HttpClient_OnUpgrade fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnParseError(HP_HttpClientListener pListener , HP_FN_HttpClient_OnParseError fn); + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageHeader(HP_HttpClientListener pListener , HP_FN_HttpClient_OnWSMessageHeader fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageBody(HP_HttpClientListener pListener , HP_FN_HttpClient_OnWSMessageBody fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnWSMessageComplete(HP_HttpClientListener pListener, HP_FN_HttpClient_OnWSMessageComplete fn); + +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnPrepareConnect(HP_HttpClientListener pListener , HP_FN_HttpClient_OnPrepareConnect fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnConnect(HP_HttpClientListener pListener , HP_FN_HttpClient_OnConnect fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnHandShake(HP_HttpClientListener pListener , HP_FN_HttpClient_OnHandShake fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnReceive(HP_HttpClientListener pListener , HP_FN_HttpClient_OnReceive fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnSend(HP_HttpClientListener pListener , HP_FN_HttpClient_OnSend fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_HttpClient_OnClose(HP_HttpClientListener pListener , HP_FN_HttpClient_OnClose fn); + +/**************************************************************************/ +/*************************** HTTP Server 操作方法 **************************/ + +/* +* 名称:回复请求 +* 描述:向客户端回复 HTTP 请求 +* +* 参数: dwConnID -- 连接 ID +* usStatusCode -- HTTP 状态码 +* lpszDesc -- HTTP 状态描述 +* lpHeaders -- 回复请求头 +* iHeaderCount -- 回复请求头数量 +* pData -- 回复请求体 +* iLength -- 回复请求体长度 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendResponse(HP_HttpServer pServer, HP_CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pData, int iLength); + +/* +* 名称:发送本地文件 +* 描述:向指定连接发送 4096 KB 以下的小文件 +* +* 参数: dwConnID -- 连接 ID +* lpszFileName -- 文件路径 +* usStatusCode -- HTTP 状态码 +* lpszDesc -- HTTP 状态描述 +* lpHeaders -- 回复请求头 +* iHeaderCount -- 回复请求头数量 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendLocalFile(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszFileName, USHORT usStatusCode, LPCSTR lpszDesc, const HP_THeader lpHeaders[], int iHeaderCount); + +/* +* 名称:发送 Chunked 数据分片 +* 描述:向对端发送 Chunked 数据分片 +* +* 参数: dwConnID -- 连接 ID +* pData -- Chunked 数据分片 +* iLength -- 数据分片长度(为 0 表示结束分片) +* lpszExtensions -- 扩展属性(默认:nullptr) +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendChunkData(HP_HttpServer pServer, HP_CONNID dwConnID, const BYTE* pData /*= nullptr*/, int iLength /*= 0*/, LPCSTR lpszExtensions /*= nullptr*/); + +/* +* 名称:发送 WebSocket 消息 +* 描述:向对端端发送 WebSocket 消息 +* +* 参数: dwConnID -- 连接 ID +* bFinal -- 是否结束帧 +* iReserved -- RSV1/RSV2/RSV3 各 1 位 +* iOperationCode -- 操作码:0x0 - 0xF +* pData -- 消息体数据缓冲区 +* iLength -- 消息体数据长度 +* ullBodyLen -- 消息总长度 +* ullBodyLen = 0 -> 消息总长度为 iLength +* ullBodyLen = iLength -> 消息总长度为 ullBodyLen +* ullBodyLen > iLength -> 消息总长度为 ullBodyLen,后续消息体长度为 ullBOdyLen - iLength,后续消息体通过底层方法 Send() / SendPackets() 发送 +* ullBodyLen < iLength -> 错误参数,发送失败 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_SendWSMessage(HP_HttpServer pServer, HP_CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE* pData, int iLength, ULONGLONG ullBodyLen); + +/* +* 名称:释放连接 +* 描述:把连接放入释放队列,等待某个时间(通过 SetReleaseDelay() 设置)关闭连接 +* +* 参数: dwConnID -- 连接 ID +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_Release(HP_HttpServer pServer, HP_CONNID dwConnID); + +/* +* 名称:启动 HTTP 通信 +* 描述:当通信组件设置为非自动启动 HTTP 通信时,需要调用本方法启动 HTTP 通信 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_StartHttp(HP_HttpServer pServer, HP_CONNID dwConnID); + +/******************************************************************************/ +/*************************** HTTP Server 属性访问方法 **************************/ + +/* 设置连接释放延时(默认:3000 毫秒) */ +HPSOCKET_API void __HP_CALL HP_HttpServer_SetReleaseDelay(HP_HttpServer pServer, DWORD dwReleaseDelay); +/* 获取连接释放延时 */ +HPSOCKET_API DWORD __HP_CALL HP_HttpServer_GetReleaseDelay(HP_HttpServer pServer); +/* 获取请求行 URL 域掩码(URL 域参考:EnHttpUrlField) */ +HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetUrlFieldSet(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取某个 URL 域值 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetUrlField(HP_HttpServer pServer, HP_CONNID dwConnID, En_HP_HttpUrlField enField); +/* 获取请求方法 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetMethod(HP_HttpServer pServer, HP_CONNID dwConnID); + +/* 设置本地协议版本 */ +HPSOCKET_API void __HP_CALL HP_HttpServer_SetLocalVersion(HP_HttpServer pServer, En_HP_HttpVersion usVersion); +/* 获取本地协议版本 */ +HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpServer_GetLocalVersion(HP_HttpServer pServer); + +/* 检查是否升级协议 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsUpgrade(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 检查是否有 Keep-Alive 标识 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsKeepAlive(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取协议版本 */ +HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetVersion(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取主机 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetHost(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取内容长度 */ +HPSOCKET_API ULONGLONG __HP_CALL HP_HttpServer_GetContentLength(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取内容类型 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetContentType(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取内容编码 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetContentEncoding(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取传输编码 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpServer_GetTransferEncoding(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取协议升级类型 */ +HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpServer_GetUpgradeType(HP_HttpServer pServer, HP_CONNID dwConnID); +/* 获取解析错误代码 */ +HPSOCKET_API USHORT __HP_CALL HP_HttpServer_GetParseErrorCode(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR* lpszErrorDesc); + +/* 获取某个请求头(单值) */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetHeader(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue); +/* 获取某个请求头(多值) */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetHeaders(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount); +/* 获取所有请求头 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllHeaders(HP_HttpServer pServer, HP_CONNID dwConnID, HP_THeader lpHeaders[], DWORD* pdwCount); +/* 获取所有请求头名称 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllHeaderNames(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName[], DWORD* pdwCount); + +/* 获取 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetCookie(HP_HttpServer pServer, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue); +/* 获取所有 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetAllCookies(HP_HttpServer pServer, HP_CONNID dwConnID, HP_TCookie lpCookies[], DWORD* pdwCount); + +/* 获取当前 WebSocket 消息状态,传入 nullptr 则不获取相应字段 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_GetWSMessageState(HP_HttpServer pServer, HP_CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain); + +/* 设置 HTTP 启动方式(默认:TRUE,自动启动) */ +HPSOCKET_API void __HP_CALL HP_HttpServer_SetHttpAutoStart(HP_HttpServer pServer, BOOL bAutoStart); +/* 获取 HTTP 启动方式 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpServer_IsHttpAutoStart(HP_HttpServer pServer); + +/**************************************************************************/ +/*************************** HTTP Agent 操作方法 ***************************/ + +/* +* 名称:发送请求 +* 描述:向服务端发送 HTTP 请求 +* +* 参数: dwConnID -- 连接 ID +* lpszMethod -- 请求方法 +* lpszPath -- 请求路径 +* lpHeaders -- 请求头 +* iHeaderCount -- 请求头数量 +* pBody -- 请求体 +* iLength -- 请求体长度 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendRequest(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pData, int iLength); + +/* +* 名称:发送本地文件 +* 描述:向指定连接发送 4096 KB 以下的小文件 +* +* 参数: dwConnID -- 连接 ID +* lpszFileName -- 文件路径 +* lpszMethod -- 请求方法 +* lpszPath -- 请求路径 +* lpHeaders -- 请求头 +* iHeaderCount -- 请求头数量 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendLocalFile(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); + +/* +* 名称:发送 Chunked 数据分片 +* 描述:向对端发送 Chunked 数据分片 +* +* 参数: dwConnID -- 连接 ID +* pData -- Chunked 数据分片 +* iLength -- 数据分片长度(为 0 表示结束分片) +* lpszExtensions -- 扩展属性(默认:nullptr) +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendChunkData(HP_HttpAgent pAgent, HP_CONNID dwConnID, const BYTE* pData /*= nullptr*/, int iLength /*= 0*/, LPCSTR lpszExtensions /*= nullptr*/); + +/* 发送 POST 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPost(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength); +/* 发送 PUT 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPut(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength); +/* 发送 PATCH 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendPatch(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength); +/* 发送 GET 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendGet(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 DELETE 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendDelete(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 HEAD 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendHead(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 TRACE 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendTrace(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 OPTIONS 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendOptions(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 CONNECT 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendConnect(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszHost, const HP_THeader lpHeaders[], int iHeaderCount); + +/* +* 名称:发送 WebSocket 消息 +* 描述:向对端端发送 WebSocket 消息 +* +* 参数: dwConnID -- 连接 ID +* bFinal -- 是否结束帧 +* iReserved -- RSV1/RSV2/RSV3 各 1 位 +* iOperationCode -- 操作码:0x0 - 0xF +* lpszMask -- 掩码(nullptr 或 4 字节掩码,如果为 nullptr 则没有掩码) +* pData -- 消息体数据缓冲区 +* iLength -- 消息体数据长度 +* ullBodyLen -- 消息总长度 +* ullBodyLen = 0 -> 消息总长度为 iLength +* ullBodyLen = iLength -> 消息总长度为 ullBodyLen +* ullBodyLen > iLength -> 消息总长度为 ullBodyLen,后续消息体长度为 ullBOdyLen - iLength,后续消息体通过底层方法 Send() / SendPackets() 发送 +* ullBodyLen < iLength -> 错误参数,发送失败 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_SendWSMessage(HP_HttpAgent pAgent, HP_CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen); + +/* +* 名称:启动 HTTP 通信 +* 描述:当通信组件设置为非自动启动 HTTP 通信时,需要调用本方法启动 HTTP 通信 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_StartHttp(HP_HttpAgent pAgent, HP_CONNID dwConnID); + +/******************************************************************************/ +/*************************** HTTP Agent 属性访问方法 ***************************/ + +/* 获取 HTTP 状态码 */ +HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetStatusCode(HP_HttpAgent pAgent, HP_CONNID dwConnID); + +/* 设置本地协议版本 */ +HPSOCKET_API void __HP_CALL HP_HttpAgent_SetLocalVersion(HP_HttpAgent pAgent, En_HP_HttpVersion usVersion); +/* 获取本地协议版本 */ +HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpAgent_GetLocalVersion(HP_HttpAgent pAgent); + +/* 检查是否升级协议 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsUpgrade(HP_HttpAgent pAgent, HP_CONNID dwConnID); +/* 检查是否有 Keep-Alive 标识 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsKeepAlive(HP_HttpAgent pAgent, HP_CONNID dwConnID); +/* 获取协议版本 */ +HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetVersion(HP_HttpAgent pAgent, HP_CONNID dwConnID); +/* 获取内容长度 */ +HPSOCKET_API ULONGLONG __HP_CALL HP_HttpAgent_GetContentLength(HP_HttpAgent pAgent, HP_CONNID dwConnID); +/* 获取内容类型 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetContentType(HP_HttpAgent pAgent, HP_CONNID dwConnID); +/* 获取内容编码 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetContentEncoding(HP_HttpAgent pAgent, HP_CONNID dwConnID); +/* 获取传输编码 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpAgent_GetTransferEncoding(HP_HttpAgent pAgent, HP_CONNID dwConnID); +/* 获取协议升级类型 */ +HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpAgent_GetUpgradeType(HP_HttpAgent pAgent, HP_CONNID dwConnID); +/* 获取解析错误代码 */ +HPSOCKET_API USHORT __HP_CALL HP_HttpAgent_GetParseErrorCode(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR* lpszErrorDesc); + +/* 获取某个请求头(单值) */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetHeader(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue); +/* 获取某个请求头(多值) */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetHeaders(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount); +/* 获取所有请求头 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllHeaders(HP_HttpAgent pAgent, HP_CONNID dwConnID, HP_THeader lpHeaders[], DWORD* pdwCount); +/* 获取所有请求头名称 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllHeaderNames(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName[], DWORD* pdwCount); + +/* 设置是否使用 Cookie */ +HPSOCKET_API void __HP_CALL HP_HttpAgent_SetUseCookie(HP_HttpAgent pAgent, BOOL bUseCookie); +/* 检查是否使用 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsUseCookie(HP_HttpAgent pAgent); +/* 获取 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetCookie(HP_HttpAgent pAgent, HP_CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue); +/* 获取所有 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetAllCookies(HP_HttpAgent pAgent, HP_CONNID dwConnID, HP_TCookie lpCookies[], DWORD* pdwCount); + +/* 获取当前 WebSocket 消息状态,传入 nullptr 则不获取相应字段 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_GetWSMessageState(HP_HttpAgent pAgent, HP_CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain); + +/* 设置 HTTP 启动方式(默认:TRUE,自动启动) */ +HPSOCKET_API void __HP_CALL HP_HttpAgent_SetHttpAutoStart(HP_HttpAgent pAgent, BOOL bAutoStart); +/* 获取 HTTP 启动方式 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpAgent_IsHttpAutoStart(HP_HttpAgent pAgent); + +/**************************************************************************/ +/*************************** HTTP Client 操作方法 **************************/ + +/* +* 名称:发送请求 +* 描述:向服务端发送 HTTP 请求 +* +* 参数: lpszMethod -- 请求方法 +* lpszPath -- 请求路径 +* lpHeaders -- 请求头 +* iHeaderCount -- 请求头数量 +* pBody -- 请求体 +* iLength -- 请求体长度 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendRequest(HP_HttpClient pClient, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength); + +/* +* 名称:发送本地文件 +* 描述:向指定连接发送 4096 KB 以下的小文件 +* +* 参数: dwConnID -- 连接 ID +* lpszFileName -- 文件路径 +* lpszMethod -- 请求方法 +* lpszPath -- 请求路径 +* lpHeaders -- 请求头 +* iHeaderCount -- 请求头数量 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendLocalFile(HP_HttpClient pClient, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); + +/* +* 名称:发送 Chunked 数据分片 +* 描述:向对端发送 Chunked 数据分片 +* +* 参数: pData -- Chunked 数据分片 +* iLength -- 数据分片长度(为 0 表示结束分片) +* lpszExtensions -- 扩展属性(默认:nullptr) +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendChunkData(HP_HttpClient pClient, const BYTE* pData /*= nullptr*/, int iLength /*= 0*/, LPCSTR lpszExtensions /*= nullptr*/); + +/* 发送 POST 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPost(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength); +/* 发送 PUT 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPut(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength); +/* 发送 PATCH 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendPatch(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength); +/* 发送 GET 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendGet(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 DELETE 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendDelete(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 HEAD 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendHead(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 TRACE 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendTrace(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 OPTIONS 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendOptions(HP_HttpClient pClient, LPCSTR lpszPath, const HP_THeader lpHeaders[], int iHeaderCount); +/* 发送 CONNECT 请求 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendConnect(HP_HttpClient pClient, LPCSTR lpszHost, const HP_THeader lpHeaders[], int iHeaderCount); + +/* +* 名称:发送 WebSocket 消息 +* 描述:向对端端发送 WebSocket 消息 +* +* 参数: bFinal -- 是否结束帧 +* iReserved -- RSV1/RSV2/RSV3 各 1 位 +* iOperationCode -- 操作码:0x0 - 0xF +* lpszMask -- 掩码(nullptr 或 4 字节掩码,如果为 nullptr 则没有掩码) +* pData -- 消息体数据缓冲区 +* iLength -- 消息体数据长度 +* ullBodyLen -- 消息总长度 +* ullBodyLen = 0 -> 消息总长度为 iLength +* ullBodyLen = iLength -> 消息总长度为 ullBodyLen +* ullBodyLen > iLength -> 消息总长度为 ullBodyLen,后续消息体长度为 ullBOdyLen - iLength,后续消息体通过底层方法 Send() / SendPackets() 发送 +* ullBodyLen < iLength -> 错误参数,发送失败 +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_SendWSMessage(HP_HttpClient pClient, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen); + +/* +* 名称:启动 HTTP 通信 +* 描述:当通信组件设置为非自动启动 HTTP 通信时,需要调用本方法启动 HTTP 通信 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_StartHttp(HP_HttpClient pClient); + +/******************************************************************************/ +/*************************** HTTP Client 属性访问方法 **************************/ + +/* 获取 HTTP 状态码 */ +HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetStatusCode(HP_HttpClient pClient); + +/* 设置本地协议版本 */ +HPSOCKET_API void __HP_CALL HP_HttpClient_SetLocalVersion(HP_HttpClient pClient, En_HP_HttpVersion usVersion); +/* 获取本地协议版本 */ +HPSOCKET_API En_HP_HttpVersion __HP_CALL HP_HttpClient_GetLocalVersion(HP_HttpClient pClient); + +/* 检查是否升级协议 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsUpgrade(HP_HttpClient pClient); +/* 检查是否有 Keep-Alive 标识 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsKeepAlive(HP_HttpClient pClient); +/* 获取协议版本 */ +HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetVersion(HP_HttpClient pClient); +/* 获取内容长度 */ +HPSOCKET_API ULONGLONG __HP_CALL HP_HttpClient_GetContentLength(HP_HttpClient pClient); +/* 获取内容类型 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetContentType(HP_HttpClient pClient); +/* 获取内容编码 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetContentEncoding(HP_HttpClient pClient); +/* 获取传输编码 */ +HPSOCKET_API LPCSTR __HP_CALL HP_HttpClient_GetTransferEncoding(HP_HttpClient pClient); +/* 获取协议升级类型 */ +HPSOCKET_API En_HP_HttpUpgradeType __HP_CALL HP_HttpClient_GetUpgradeType(HP_HttpClient pClient); +/* 获取解析错误代码 */ +HPSOCKET_API USHORT __HP_CALL HP_HttpClient_GetParseErrorCode(HP_HttpClient pClient, LPCSTR* lpszErrorDesc); + +/* 获取某个请求头(单值) */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetHeader(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR* lpszValue); +/* 获取某个请求头(多值) */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetHeaders(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR lpszValue[], DWORD* pdwCount); +/* 获取所有请求头 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllHeaders(HP_HttpClient pClient, HP_THeader lpHeaders[], DWORD* pdwCount); +/* 获取所有请求头名称 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllHeaderNames(HP_HttpClient pClient, LPCSTR lpszName[], DWORD* pdwCount); + +/* 设置是否使用 Cookie */ +HPSOCKET_API void __HP_CALL HP_HttpClient_SetUseCookie(HP_HttpClient pClient, BOOL bUseCookie); +/* 检查是否使用 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsUseCookie(HP_HttpClient pClient); +/* 获取 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetCookie(HP_HttpClient pClient, LPCSTR lpszName, LPCSTR* lpszValue); +/* 获取所有 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetAllCookies(HP_HttpClient pClient, HP_TCookie lpCookies[], DWORD* pdwCount); + +/* 获取当前 WebSocket 消息状态,传入 nullptr 则不获取相应字段 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_GetWSMessageState(HP_HttpClient pClient, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain); + +/* 设置 HTTP 启动方式(默认:TRUE,自动启动) */ +HPSOCKET_API void __HP_CALL HP_HttpClient_SetHttpAutoStart(HP_HttpClient pClient, BOOL bAutoStart); +/* 获取 HTTP 启动方式 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpClient_IsHttpAutoStart(HP_HttpClient pClient); + +/**************************************************************************/ +/************************ HTTP Sync Client 操作方法 ************************/ + +/* +* 名称:发送 URL 请求 +* 描述:向服务端发送 HTTP URL 请求 +* +* 参数: lpszMethod -- 请求方法 +* lpszUrl -- 请求 URL +* lpHeaders -- 请求头 +* iHeaderCount -- 请求头数量 +* pBody -- 请求体 +* iLength -- 请求体长度 +* bForceReconnect -- 强制重新连接(默认:FALSE,当请求 URL 的主机和端口与现有连接一致时,重用现有连接) +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_OpenUrl(HP_HttpSyncClient pClient, LPCSTR lpszMethod, LPCSTR lpszUrl, const HP_THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength, BOOL bForceReconnect); + +/* +* 名称:清除请求结果 +* 描述:清除上一次请求的响应头和响应体等结果信息(该方法会在每次发送请求前自动调用) +* +* 参数: +* 返回值: TRUE -- 成功 +* FALSE -- 失败 +*/ +HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_CleanupRequestResult(HP_HttpSyncClient pClient); + +/******************************************************************************/ +/************************ HTTP Sync Client 属性访问方法 ************************/ + +/* 设置连接超时(毫秒,0:系统默认超时,默认:5000) */ +HPSOCKET_API void __HP_CALL HP_HttpSyncClient_SetConnectTimeout(HP_HttpSyncClient pClient, DWORD dwConnectTimeout); +/* 设置请求超时(毫秒,0:无限等待,默认:10000) */ +HPSOCKET_API void __HP_CALL HP_HttpSyncClient_SetRequestTimeout(HP_HttpSyncClient pClient, DWORD dwRequestTimeout); + +/* 获取连接超时 */ +HPSOCKET_API DWORD __HP_CALL HP_HttpSyncClient_GetConnectTimeout(HP_HttpSyncClient pClient); +/* 获取请求超时 */ +HPSOCKET_API DWORD __HP_CALL HP_HttpSyncClient_GetRequestTimeout(HP_HttpSyncClient pClient); + +/* 获取响应体 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpSyncClient_GetResponseBody(HP_HttpSyncClient pClient, LPCBYTE* lpszBody, int* piLength); + +/**************************************************************************/ +/*************************** HTTP Cookie 管理方法 **************************/ + +/* 从文件加载 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists /*= TRUE*/); +/* 保存 Cookie 到文件 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_SaveToFile(LPCSTR lpszFile, BOOL bKeepExists /*= TRUE*/); +/* 清理 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_ClearCookies(LPCSTR lpszDomain /*= nullptr*/, LPCSTR lpszPath /*= nullptr*/); +/* 清理过期 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_RemoveExpiredCookies(LPCSTR lpszDomain /*= nullptr*/, LPCSTR lpszPath /*= nullptr*/); +/* 设置 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge /*= -1*/, BOOL bHttpOnly /*= FALSE*/, BOOL bSecure /*= FALSE*/, int enSameSite /*= 0*/, BOOL bOnlyUpdateValueIfExists /*= TRUE*/); +/* 删除 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName); +/* 设置是否允许第三方 Cookie */ +HPSOCKET_API void __HP_CALL HP_HttpCookie_MGR_SetEnableThirdPartyCookie(BOOL bEnableThirdPartyCookie /*= TRUE*/); +/* 检查是否允许第三方 Cookie */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_MGR_IsEnableThirdPartyCookie(); + +/* Cookie expires 字符串转换为整数 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_ParseExpires(LPCSTR lpszExpires, __time64_t* ptmExpires); +/* 整数转换为 Cookie expires 字符串 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_MakeExpiresStr(char lpszBuff[], int* piBuffLen, __time64_t tmExpires); +/* 生成 Cookie 字符串 */ +HPSOCKET_API BOOL __HP_CALL HP_HttpCookie_HLP_ToString(char lpszBuff[], int* piBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge /*= -1*/, BOOL bHttpOnly /*= FALSE*/, BOOL bSecure /*= FALSE*/, int enSameSite /*= 0*/); +/* 获取当前 UTC 时间 */ +HPSOCKET_API __time64_t __HP_CALL HP_HttpCookie_HLP_CurrentUTCTime(); +/* Max-Age -> expires */ +HPSOCKET_API __time64_t __HP_CALL HP_HttpCookie_HLP_MaxAgeToExpires(int iMaxAge); +/* expires -> Max-Age */ +HPSOCKET_API int __HP_CALL HP_HttpCookie_HLP_ExpiresToMaxAge(__time64_t tmExpires); + +/*****************************************************************************************************************************************************/ +/************************************************************* HTTP Global Function Exports **********************************************************/ +/*****************************************************************************************************************************************************/ + +#endif + +/*****************************************************************************************************************************************************/ +/**************************************************************** Thread Pool Exports ****************************************************************/ +/*****************************************************************************************************************************************************/ + +/* Thread Pool 回调函数 */ +typedef void (__HP_CALL *HP_FN_ThreadPool_OnStartup) (HP_ThreadPool pThreadPool); +typedef void (__HP_CALL *HP_FN_ThreadPool_OnShutdown) (HP_ThreadPool pThreadPool); +typedef void (__HP_CALL *HP_FN_ThreadPool_OnWorkerThreadStart) (HP_ThreadPool pThreadPool, THR_ID dwThreadID); +typedef void (__HP_CALL *HP_FN_ThreadPool_OnWorkerThreadEnd) (HP_ThreadPool pThreadPool, THR_ID dwThreadID); + +/**********************************************************************************/ +/************************** Thread Pool 回调函数设置方法 ***************************/ + +HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnStartup(HP_ThreadPoolListener pListener , HP_FN_ThreadPool_OnStartup fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnShutdown(HP_ThreadPoolListener pListener , HP_FN_ThreadPool_OnShutdown fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnWorkerThreadStart(HP_ThreadPoolListener pListener , HP_FN_ThreadPool_OnWorkerThreadStart fn); +HPSOCKET_API void __HP_CALL HP_Set_FN_ThreadPool_OnWorkerThreadEnd(HP_ThreadPoolListener pListener , HP_FN_ThreadPool_OnWorkerThreadEnd fn); + +/****************************************************/ +/******************* 对象创建函数 ********************/ + +// 创建 IHPThreadPoolListener 对象 +HPSOCKET_API HP_ThreadPoolListener __HP_CALL Create_HP_ThreadPoolListener(); +// 销毁 IHPThreadPoolListener 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_ThreadPoolListener(HP_ThreadPoolListener pListener); + +// 创建 IHPThreadPool 对象(pListener 参数可传入 nullptr) +HPSOCKET_API HP_ThreadPool __HP_CALL Create_HP_ThreadPool(HP_ThreadPoolListener pListener /*= nullptr*/); +// 销毁 IHPThreadPool 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_ThreadPool(HP_ThreadPool pThreadPool); + +/* +* 名称:创建 TSocketTask 对象 +* 描述:创建任务对象,该对象最终需由 HP_Destroy_SocketTaskObj() 销毁 +* +* 参数: fnTaskProc -- 任务处理函数 +* pSender -- 发起对象 +* dwConnID -- 连接 ID +* pBuffer -- 数据缓冲区 +* iBuffLen -- 数据缓冲区长度 +* enBuffType -- 数据缓冲区类型(默认:TBT_COPY) +* TBT_COPY :(深拷贝)pBuffer 复制到 TSocketTask 对象。此后 TSocketTask 对象与 pBuffer 不再有任何关联 +* -> 适用于 pBuffer 不大或 pBuffer 生命周期不受控的场景 +* TBT_REFER :(浅拷贝)pBuffer 不复制到 TSocketTask 对象,需确保 TSocketTask 对象生命周期内 pBuffer 必须有效 +* -> 适用于 pBuffer 较大或 pBuffer 可重用,并且 pBuffer 生命周期受控的场景 +* TBT_ATTACH :(附属)执行浅拷贝,但 TSocketTask 对象会获得 pBuffer 的所有权,并负责释放 pBuffer,避免多次缓冲区拷贝 +* -> 注意:pBuffer 必须由 SYS_Malloc()/SYS_Calloc() 函数分配才能使用本类型,否则可能会发生内存访问错误 +* wParam -- 自定义参数 +* lParam -- 自定义参数 +* 返回值: HP_LPTSocketTask +*/ +HPSOCKET_API HP_LPTSocketTask __HP_CALL Create_HP_SocketTaskObj(Fn_SocketTaskProc fnTaskProc, PVOID pSender, HP_CONNID dwConnID, LPCBYTE pBuffer, INT iBuffLen, En_HP_TaskBufferType enBuffType /*= TBT_COPY*/, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/); + +// 销毁 TSocketTask 对象 +HPSOCKET_API void __HP_CALL Destroy_HP_SocketTaskObj(HP_LPTSocketTask pTask); + +/***********************************************************************/ +/***************************** 组件操作方法 *****************************/ + +/* +* 名称:启动线程池组件 +* 描述: +* +* 参数: dwThreadCount -- 线程数量,(默认:0) +* >0 -> dwThreadCount +* =0 -> (CPU核数 * 2 + 2) +* <0 -> (CPU核数 * (-dwThreadCount)) +* dwMaxQueueSize -- 任务队列最大容量(默认:0,不限制) +* enRejectedPolicy -- 任务拒绝处理策略 +* TRP_CALL_FAIL(默认) :立刻返回失败 +* TRP_WAIT_FOR :等待(直到成功、超时或线程池关闭等原因导致失败) +* TRP_CALLER_RUN :调用者线程直接执行 +* dwStackSize -- 线程堆栈空间大小(默认:0 -> 操作系统默认) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Start(HP_ThreadPool pThreadPool, DWORD dwThreadCount /*= 0*/, DWORD dwMaxQueueSize /*= 0*/, En_HP_RejectedPolicy enRejectedPolicy /*= TRP_CALL_FAIL*/, DWORD dwStackSize /*= 0*/); + +/* +* 名称:关闭线程池组件 +* 描述:在规定时间内关闭线程池组件,如果工作线程在最大等待时间内未能正常关闭,会尝试强制关闭,这种情况下很可能会造成系统资源泄漏 +* +* 参数: dwMaxWait -- 最大等待时间(毫秒,默认:INFINITE,一直等待) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Stop(HP_ThreadPool pThreadPool, DWORD dwMaxWait /*= INFINITE*/); + +/* +* 名称:提交任务 +* 描述:向线程池提交异步任务 +* +* 参数: fnTaskProc -- 任务处理函数 +* pvArg -- 任务参数 +* dwMaxWait -- 任务提交最大等待时间(仅对 TRP_WAIT_FOR 类型线程池生效,默认:INFINITE,一直等待) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +* 其中,错误码 ERROR_DESTINATION_ELEMENT_FULL 表示任务队列已满 +*/ +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Submit(HP_ThreadPool pThreadPool, HP_Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait /*= INFINITE*/); + +/* +* 名称:提交 Socket 任务 +* 描述:向线程池提交异步 Socket 任务 +* +* 参数: pTask -- 任务参数 +* dwMaxWait -- 任务提交最大等待时间(仅对 TRP_WAIT_FOR 类型线程池生效,默认:INFINITE,一直等待) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +* 其中,错误码 ERROR_DESTINATION_ELEMENT_FULL 表示任务队列已满 +* 注意:如果提交失败,需要手工调用 Destroy_HP_SocketTaskObj() 销毁 TSocketTask 对象 +*/ +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Submit_Task(HP_ThreadPool pThreadPool, HP_LPTSocketTask pTask, DWORD dwMaxWait /*= INFINITE*/); + +/* +* 名称:调整线程池大小 +* 描述:增加或减少线程池的工作线程数量 +* +* 参数: dwNewThreadCount -- 线程数量 +* >0 -> dwNewThreadCount +* =0 -> (CPU核数 * 2 + 2) +* <0 -> (CPU核数 * (-dwNewThreadCount)) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取系统错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_AdjustThreadCount(HP_ThreadPool pThreadPool, DWORD dwNewThreadCount); + +/* +* 名称:等待 +* 描述:等待线程池组件停止运行 +* +* 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_Wait(HP_ThreadPool pThreadPool, DWORD dwMilliseconds); + +/***********************************************************************/ +/***************************** 属性访问方法 *****************************/ + +/* 检查线程池组件是否已启动 */ +HPSOCKET_API BOOL __HP_CALL HP_ThreadPool_HasStarted(HP_ThreadPool pThreadPool); +/* 查看线程池组件当前状态 */ +HPSOCKET_API En_HP_ServiceState __HP_CALL HP_ThreadPool_GetState(HP_ThreadPool pThreadPool); +/* 获取当前任务等待队列大小 */ +HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetQueueSize(HP_ThreadPool pThreadPool); +/* 获取当前正在执行的任务数量 */ +HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetTaskCount(HP_ThreadPool pThreadPool); +/* 获取工作线程数量 */ +HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetThreadCount(HP_ThreadPool pThreadPool); +/* 获取任务队列最大容量 */ +HPSOCKET_API DWORD __HP_CALL HP_ThreadPool_GetMaxQueueSize(HP_ThreadPool pThreadPool); +/* 获取任务拒绝处理策略 */ +HPSOCKET_API En_HP_RejectedPolicy __HP_CALL HP_ThreadPool_GetRejectedPolicy(HP_ThreadPool pThreadPool); + +/*****************************************************************************************************************************************************/ +/********************************************************* Compressor / Decompressor Exports *********************************************************/ +/*****************************************************************************************************************************************************/ + +/****************************************************/ +/******************* 对象创建函数 ********************/ + +/* 销毁压缩器对象 */ +HPSOCKET_API void __HP_CALL Destroy_HP_Compressor(HP_Compressor pCompressor); +/* 销毁解压器对象 */ +HPSOCKET_API void __HP_CALL Destroy_HP_Decompressor(HP_Decompressor pDecompressor); + +#ifdef _ZLIB_SUPPORT + +/* 创建 ZLib 压缩器对象 */ +// (默认参数:iWindowBits = 15, iLevel = -1, iMethod = 8, iMemLevel = 8, iStrategy = 0, dwBuffSize = 16 * 1024) +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_ZLibCompressor(HP_Fn_CompressDataCallback fnCallback); +/* 创建 ZLib 压缩器对象 */ +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_ZLibCompressorEx(HP_Fn_CompressDataCallback fnCallback, int iWindowBits /*= 15*/, int iLevel /*= -1*/, int iMethod /*= 8*/, int iMemLevel /*= 8*/, int iStrategy /*= 0*/, DWORD dwBuffSize /*= 16 * 1024*/); +/* 创建 GZip 压缩器对象 */ +// (默认参数:iLevel = -1, iMethod = 8, iMemLevel = 8, iStrategy = 0, dwBuffSize = 16 * 1024) +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_GZipCompressor(HP_Fn_CompressDataCallback fnCallback); +/* 创建 GZip 压缩器对象 */ +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_GZipCompressorEx(HP_Fn_CompressDataCallback fnCallback, int iLevel /*= -1*/, int iMethod /*= 8*/, int iMemLevel /*= 8*/, int iStrategy /*= 0*/, DWORD dwBuffSize /*= 16 * 1024*/); +/* 创建 ZLib 解压器对象 */ +// (默认参数:iWindowBits = 15, dwBuffSize = 16 * 1024) +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_ZLibDecompressor(HP_Fn_DecompressDataCallback fnCallback); +/* 创建 ZLib 解压器对象 */ +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_ZLibDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, int iWindowBits /*= 15*/, DWORD dwBuffSize /*= 16 * 1024*/); +/* 创建 GZip 解压器对象 */ +// (默认参数:dwBuffSize = 16 * 1024) +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_GZipDecompressor(HP_Fn_DecompressDataCallback fnCallback); +/* 创建 GZip 解压器对象 */ +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_GZipDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize /*= 16 * 1024*/); + +#endif + +#ifdef _BROTLI_SUPPORT + +/* 创建 Brotli 压缩器对象 */ +//(默认参数:iQuality = 11,iWindow = 22,iMode = 0, dwBuffSize = 16 * 1024) +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_BrotliCompressor(HP_Fn_CompressDataCallback fnCallback); +/* 创建 Brotli 压缩器对象 */ +HPSOCKET_API HP_Compressor __HP_CALL Create_HP_BrotliCompressorEx(HP_Fn_CompressDataCallback fnCallback, int iQuality /*= 11*/, int iWindow /*= 22*/, int iMode /*= 0*/, DWORD dwBuffSize /*= 16 * 1024*/); +/* 创建 Brotli 解压器对象 */ +// (默认参数:dwBuffSize = 16 * 1024) +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_BrotliDecompressor(HP_Fn_DecompressDataCallback fnCallback); +/* 创建 Brotli 解压器对象 */ +HPSOCKET_API HP_Decompressor __HP_CALL Create_HP_BrotliDecompressorEx(HP_Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize /*= 16 * 1024*/); + +#endif + +/***********************************************************************/ +/***************************** 组件操作方法 *****************************/ + +/* +* 名称:执行压缩 +* 描述:可循环调用以压缩流式或分段数据 +* +* 参数: pData -- 待压缩数据缓冲区 +* iLength -- 待压缩数据长度 +* bLast -- 是否最后一段待压缩数据 +* pContext -- 压缩回调函数 Fn_CompressDataCallback 的上下文参数 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Compressor_Process(HP_Compressor pCompressor, const BYTE* pData, int iLength, BOOL bLast, PVOID pContext /*= nullptr*/); + +/* +* 名称:执行压缩 +* 描述:可循环调用以压缩流式或分段数据 +* +* 参数: pData -- 待压缩数据缓冲区 +* iLength -- 待压缩数据长度 +* bLast -- 是否最后一段待压缩数据 +* bFlush -- 是否强制刷新(强制刷新会降低压缩效率,但可对数据进行分段压缩) +* pContext -- 压缩回调函数 Fn_CompressDataCallback 的上下文参数 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Compressor_ProcessEx(HP_Compressor pCompressor, const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush /*= FALSE*/, PVOID pContext /*= nullptr*/); + +/* 重置压缩器 */ +HPSOCKET_API BOOL __HP_CALL HP_Compressor_Reset(HP_Compressor pCompressor); + +/* +* 名称:执行解压 +* 描述:可循环调用以解压流式或分段数据 +* +* 参数: pData -- 待解压数据缓冲区 +* iLength -- 待解压数据长度 +* pContext -- 解压回调函数 Fn_DecompressDataCallback 的上下文参数 +* +* 返回值: TRUE -- 成功 +* FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 +*/ +HPSOCKET_API BOOL __HP_CALL HP_Decompressor_Process(HP_Decompressor pDecompressor, const BYTE* pData, int iLength, PVOID pContext /*= nullptr*/); + +/* 重置解压器 */ +HPSOCKET_API BOOL __HP_CALL HP_Decompressor_Reset(HP_Decompressor pDecompressor); + +/***********************************************************************/ +/***************************** 属性访问方法 *****************************/ + +/* 检测压缩器是否可用 */ +HPSOCKET_API BOOL __HP_CALL HP_Compressor_IsValid(HP_Compressor pCompressor); + +/* 检测解压器是否可用 */ +HPSOCKET_API BOOL __HP_CALL HP_Decompressor_IsValid(HP_Decompressor pDecompressor); diff --git a/hpsocket/HPTypeDef.h b/hpsocket/HPTypeDef.h new file mode 100644 index 0000000..1f84c93 --- /dev/null +++ b/hpsocket/HPTypeDef.h @@ -0,0 +1,600 @@ +/* + * 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 "GlobalDef.h" + +/* HP-Socket 版本号 */ +#define HP_VERSION_MAJOR 6 // 主版本号 +#define HP_VERSION_MINOR 0 // 子版本号 +#define HP_VERSION_REVISE 3 // 修正版本号 +#define HP_VERSION_BUILD 1 // 构建编号 + +//#define _UDP_DISABLED // 禁用 UDP +//#define _SSL_DISABLED // 禁用 SSL +//#define _HTTP_DISABLED // 禁用 HTTP +//#define _ZLIB_DISABLED // 禁用 ZLIB +//#define _ICONV_DISABLED // 禁用 ICONV +//#define _BROTLI_DISABLED // 禁用 BROTLI + +/* 是否启用 UDP,如果定义了 _UDP_DISABLED 则禁用(默认:启用) */ +#if !defined(_UDP_DISABLED) + #ifndef _UDP_SUPPORT + #define _UDP_SUPPORT + #endif +#endif + +/* 是否启用 SSL,如果定义了 _SSL_DISABLED 则禁用(默认:启用) */ +#if !defined(_SSL_DISABLED) + #ifndef _SSL_SUPPORT + #define _SSL_SUPPORT + #endif +#endif + +/* 是否启用 HTTP,如果定义了 _HTTP_DISABLED 则禁用(默认:启用) */ +#if !defined(_HTTP_DISABLED) + #ifndef _HTTP_SUPPORT + #define _HTTP_SUPPORT + #endif +#endif + +/* 是否启用 ZLIB,如果定义了 _ZLIB_DISABLED 则禁用(默认:启用) */ +#if !defined(_ZLIB_DISABLED) + #ifndef _ZLIB_SUPPORT + #define _ZLIB_SUPPORT + #endif +#endif + +/* 是否启用 BROTLI,如果定义了 _BROTLI_DISABLED 则禁用(默认:启用) */ +#if !defined(_BROTLI_DISABLED) + #ifndef _BROTLI_SUPPORT + #define _BROTLI_SUPPORT + #endif +#endif + +/* 是否启用 ICONV,如果定义了 _ICONV_DISABLED 则禁用(默认:启用) */ +#if !defined(_ICONV_DISABLED) + #ifndef _ICONV_SUPPORT + #define _ICONV_SUPPORT + #endif +#endif + +#define HPSOCKET_API EXTERN_C __attribute__ ((__visibility__("default"))) + +#define __HP_CALL + +/*****************************************************************************************************************************************************/ +/**************************************************************** Base Type Definitions **************************************************************/ +/*****************************************************************************************************************************************************/ + +/************************************************************************ +名称:连接 ID 数据类型 +描述:应用程序可以把 CONNID 定义为自身需要的类型(如:ULONG / ULONGLONG) +************************************************************************/ +typedef ULID CONNID, HP_CONNID; + +/************************************************************************ +名称:通信组件服务状态 +描述:应用程序可以通过通信组件的 GetState() 方法获取组件当前服务状态 +************************************************************************/ +typedef enum EnServiceState +{ + SS_STARTING = 0, // 正在启动 + SS_STARTED = 1, // 已经启动 + SS_STOPPING = 2, // 正在停止 + SS_STOPPED = 3, // 已经停止 +} En_HP_ServiceState; + +/************************************************************************ +名称:Socket 操作类型 +描述:应用程序的 OnClose() 事件中通过该参数标识是哪种操作导致的错误 +************************************************************************/ +typedef enum EnSocketOperation +{ + SO_UNKNOWN = 0, // Unknown + SO_ACCEPT = 1, // Acccept + SO_CONNECT = 2, // Connect + SO_SEND = 3, // Send + SO_RECEIVE = 4, // Receive + SO_CLOSE = 5, // Close +} En_HP_SocketOperation; + +/************************************************************************ +名称:事件处理结果 +描述:事件的返回值,不同的返回值会影响通信组件的后续行为 +************************************************************************/ +typedef enum EnHandleResult +{ + HR_OK = 0, // 成功 + HR_IGNORE = 1, // 忽略 + HR_ERROR = 2, // 错误 +} En_HP_HandleResult; + +/************************************************************************ +名称:数据抓取结果 +描述:数据抓取操作的返回值 +************************************************************************/ +typedef enum EnFetchResult +{ + FR_OK = 0, // 成功 + FR_LENGTH_TOO_LONG = 1, // 抓取长度过大 + FR_DATA_NOT_FOUND = 2, // 找不到 ConnID 对应的数据 +} En_HP_FetchResult; + +/************************************************************************ +名称:数据发送策略 +描述:Server 组件和 Agent 组件的数据发送策略 + +* 打包发送策略(默认) :尽量把多个发送操作的数据组合在一起发送,增加传输效率 +* 安全发送策略 :尽量把多个发送操作的数据组合在一起发送,并控制传输速度,避免缓冲区溢出 +* 直接发送策略 :对每一个发送操作都直接投递,适用于负载不高但要求实时性较高的场合 +************************************************************************/ +typedef enum EnSendPolicy +{ + SP_PACK = 0, // 打包模式(默认) + SP_SAFE = 1, // 安全模式 + SP_DIRECT = 2, // 直接模式 +} En_HP_SendPolicy; + +/************************************************************************ +名称:OnSend 事件同步策略 +描述:Server 组件和 Agent 组件的 OnSend 事件同步策略 + +* 不同步(默认) :不同步 OnSend 事件,可能同时触发 OnReceive 和 OnClose 事件 +* 同步 OnClose :只同步 OnClose 事件,可能同时触发 OnReceive 事件 +* 同步 OnReceive :(只用于 TCP 组件)同步 OnReceive 和 OnClose 事件,不可能同时触发 OnReceive 或 OnClose 事件 +************************************************************************/ +typedef enum EnOnSendSyncPolicy +{ + OSSP_NONE = 0, // 不同步(默认) + OSSP_CLOSE = 1, // 同步 OnClose + OSSP_RECEIVE = 2, // 同步 OnReceive(只用于 TCP 组件) +} En_HP_OnSendSyncPolicy; + +/************************************************************************ +名称:地址重用选项 +描述:通信组件底层 socket 的地址重用选项 +************************************************************************/ +typedef enum EnReuseAddressPolicy +{ + RAP_NONE = 0, // 不重用 + RAP_ADDR_ONLY = 1, // 仅重用地址 + RAP_ADDR_AND_PORT = 2, // 重用地址和端口 +} En_HP_ReuseAddressPolicy; + +/************************************************************************ +名称:操作结果代码 +描述:组件 Start() / Stop() 方法执行失败时,可通过 GetLastError() 获取错误代码 +************************************************************************/ +typedef enum EnSocketError +{ + SE_OK = NO_ERROR, // 成功 + SE_ILLEGAL_STATE = 1, // 当前状态不允许操作 + SE_INVALID_PARAM = 2, // 非法参数 + SE_SOCKET_CREATE = 3, // 创建 SOCKET 失败 + SE_SOCKET_BIND = 4, // 绑定 SOCKET 失败 + SE_SOCKET_PREPARE = 5, // 设置 SOCKET 失败 + SE_SOCKET_LISTEN = 6, // 监听 SOCKET 失败 + SE_CP_CREATE = 7, // 创建完成端口失败 + SE_WORKER_THREAD_CREATE = 8, // 创建工作线程失败 + SE_DETECT_THREAD_CREATE = 9, // 创建监测线程失败 + SE_SOCKE_ATTACH_TO_CP = 10, // 绑定完成端口失败 + SE_CONNECT_SERVER = 11, // 连接服务器失败 + SE_NETWORK = 12, // 网络错误 + SE_DATA_PROC = 13, // 数据处理错误 + SE_DATA_SEND = 14, // 数据发送失败 + SE_GC_START = 15, // 垃圾回收启动失败 + + /***** SSL Socket 扩展操作结果代码 *****/ + SE_SSL_ENV_NOT_READY = 101, // SSL 环境未就绪 +} En_HP_SocketError; + +/************************************************************************ +名称:播送模式 +描述:UDP 组件的播送模式(组播或广播) +************************************************************************/ +typedef enum EnCastMode +{ + CM_UNICAST = -1, // 单播 + CM_MULTICAST = 0, // 组播 + CM_BROADCAST = 1, // 广播 +} En_HP_CastMode; + +/************************************************************************ +名称:IP 地址类型 +描述:IP 地址类型枚举值 +************************************************************************/ +typedef enum EnIPAddrType +{ + IPT_ALL = 0, // 所有 + IPT_IPV4 = 1, // IPv4 + IPT_IPV6 = 2, // IPv6 +} En_HP_IPAddrType; + +/************************************************************************ +名称:IP 地址条目结构体 +描述:IP 地址的地址簇/地址值结构体 +************************************************************************/ +typedef struct TIPAddr +{ + En_HP_IPAddrType type; + LPCTSTR address; +} *LPTIPAddr, HP_TIPAddr, *HP_LPTIPAddr; + +/************************************************************************ +名称:缓冲区结构体 +描述:数据缓冲区 +************************************************************************/ +typedef struct _WSABUF +{ + UINT len; + LPBYTE buf; +} WSABUF, *PWSABUF, *LPWSABUF; + +/************************************************************************ +名称:拒绝策略 +描述:调用被拒绝后的处理策略 +************************************************************************/ +typedef enum EnRejectedPolicy +{ + TRP_CALL_FAIL = 0, // 立刻返回失败 + TRP_WAIT_FOR = 1, // 等待(直到成功、超时或线程池关闭等原因导致失败) + TRP_CALLER_RUN = 2, // 调用者线程直接执行 +} En_HP_RejectedPolicy; + +/************************************************************************ +名称:任务缓冲区类型 +描述:TSockeTask 对象创建和销毁时,根据不同类型的缓冲区类型作不同的处理 +************************************************************************/ +typedef enum EnTaskBufferType +{ + TBT_COPY = 0, // 深拷贝 + TBT_REFER = 1, // 浅拷贝 + TBT_ATTACH = 2, // 附属(不负责创建,但负责销毁) +} En_HP_TaskBufferType; + +/************************************************************************ +名称:任务处理函数 +描述:任务处理入口函数 +参数:pvArg -- 自定义参数 +返回值:(无) +************************************************************************/ +typedef VOID (__HP_CALL *Fn_TaskProc)(PVOID pvArg); +typedef Fn_TaskProc HP_Fn_TaskProc; + +struct TSocketTask; + +/************************************************************************ +名称:Socket 任务处理函数 +描述:Socket 任务处理入口函数 +参数:pTask -- Socket 任务结构体指针 +返回值:(无) +************************************************************************/ +typedef VOID (__HP_CALL *Fn_SocketTaskProc)(struct TSocketTask* pTask); +typedef Fn_SocketTaskProc HP_Fn_SocketTaskProc; + +/************************************************************************ +名称:Socket 任务结构体 +描述:封装 Socket 任务相关数据结构 +************************************************************************/ +typedef struct TSocketTask +{ + HP_Fn_SocketTaskProc fn; // 任务处理函数 + PVOID sender; // 发起对象 + CONNID connID; // 连接 ID + LPCBYTE buf; // 数据缓冲区 + INT bufLen; // 数据缓冲区长度 + En_HP_TaskBufferType bufType; // 缓冲区类型 + WPARAM wparam; // 自定义参数 + LPARAM lparam; // 自定义参数 +} *LPTSocketTask, HP_TSocketTask, *HP_LPTSocketTask; + +/************************************************************************ +名称:获取 HPSocket 版本号 +描述:版本号(4 个字节分别为:主版本号,子版本号,修正版本号,构建编号) +************************************************************************/ +inline DWORD GetHPSocketVersion() +{ + return (HP_VERSION_MAJOR << 24) | (HP_VERSION_MINOR << 16) | (HP_VERSION_REVISE << 8) | HP_VERSION_BUILD; +} + +/*****************************************************************************************************************************************************/ +/**************************************************************** SSL Type Definitions ***************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _SSL_SUPPORT + +/************************************************************************ +名称:SSL 工作模式 +描述:标识 SSL 的工作模式,客户端模式或服务端模式 +************************************************************************/ +typedef enum EnSSLSessionMode +{ + SSL_SM_CLIENT = 0, // 客户端模式 + SSL_SM_SERVER = 1, // 服务端模式 +} En_HP_SSLSessionMode; + +/************************************************************************ +名称:SSL 验证模式 +描述:SSL 验证模式选项,SSL_VM_PEER 可以和后面两个选项组合一起 +************************************************************************/ +typedef enum EnSSLVerifyMode +{ + SSL_VM_NONE = 0x00, // SSL_VERIFY_NONE + SSL_VM_PEER = 0x01, // SSL_VERIFY_PEER + SSL_VM_FAIL_IF_NO_PEER_CERT = 0x02, // SSL_VERIFY_FAIL_IF_NO_PEER_CERT + SSL_VM_CLIENT_ONCE = 0x04, // SSL_VERIFY_CLIENT_ONCE +} En_HP_SSLVerifyMode; + +/************************************************************************ +名称:SSL Session 信息类型 +描述:用于 GetSSLSessionInfo(),标识输出的 Session 信息类型 +************************************************************************/ +typedef enum EnSSLSessionInfo +{ + SSL_SSI_MIN = 0, // + SSL_SSI_CTX = 0, // SSL CTX (输出类型:SSL_CTX*) + SSL_SSI_CTX_METHOD = 1, // SSL CTX Mehtod (输出类型:SSL_METHOD*) + SSL_SSI_CTX_CIPHERS = 2, // SSL CTX Ciphers (输出类型:STACK_OF(SSL_CIPHER)*) + SSL_SSI_CTX_CERT_STORE = 3, // SSL CTX Cert Store (输出类型:X509_STORE*) + SSL_SSI_SERVER_NAME_TYPE = 4, // Server Name Type (输出类型:int) + SSL_SSI_SERVER_NAME = 5, // Server Name (输出类型:LPCSTR) + SSL_SSI_VERSION = 6, // SSL Version (输出类型:LPCSTR) + SSL_SSI_METHOD = 7, // SSL Method (输出类型:SSL_METHOD*) + SSL_SSI_CERT = 8, // SSL Cert (输出类型:X509*) + SSL_SSI_PKEY = 9, // SSL Private Key (输出类型:EVP_PKEY*) + SSL_SSI_CURRENT_CIPHER = 10, // SSL Current Cipher (输出类型:SSL_CIPHER*) + SSL_SSI_CIPHERS = 11, // SSL Available Ciphers(输出类型:STACK_OF(SSL_CIPHER)*) + SSL_SSI_CLIENT_CIPHERS = 12, // SSL Client Ciphers (输出类型:STACK_OF(SSL_CIPHER)*) + SSL_SSI_PEER_CERT = 13, // SSL Peer Cert (输出类型:X509*) + SSL_SSI_PEER_CERT_CHAIN = 14, // SSL Peer Cert Chain (输出类型:STACK_OF(X509)*) + SSL_SSI_VERIFIED_CHAIN = 15, // SSL Verified Chain (输出类型:STACK_OF(X509)*) + SSL_SSI_MAX = 15, // +} En_HP_SSLSessionInfo; + +/************************************************************************ +名称:SNI 服务名称回调函数 +描述:根据服务器名称选择 SSL 证书 +参数: + lpszServerName -- 服务器名称(域名) + +返回值: + 0 -- 成功,使用默认 SSL 证书索引 + 正数 -- 成功,使用返回值对应的 SNI 主机证书索引 + 负数 -- 失败,中断 SSL 握手 + +************************************************************************/ +typedef int (__HP_CALL *Fn_SNI_ServerNameCallback)(LPCTSTR lpszServerName, PVOID pContext); +typedef Fn_SNI_ServerNameCallback HP_Fn_SNI_ServerNameCallback; + +#endif + +/*****************************************************************************************************************************************************/ +/**************************************************************** HTTP Type Definitions **************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _HTTP_SUPPORT + +/************************************************************************ +名称:HTTP 版本 +描述:低字节:主版本号,高字节:次版本号 +************************************************************************/ + +typedef enum EnHttpVersion +{ + HV_1_0 = MAKEWORD(1, 0), // HTTP/1.0 + HV_1_1 = MAKEWORD(1, 1) // HTTP/1.1 +} En_HP_HttpVersion; + +/************************************************************************ +名称:URL 域 +描述:HTTP 请求行中 URL 段位的域定义 +************************************************************************/ +typedef enum EnHttpUrlField +{ + HUF_SCHEMA = 0, // Schema + HUF_HOST = 1, // Host + HUF_PORT = 2, // Port + HUF_PATH = 3, // Path + HUF_QUERY = 4, // Query String + HUF_FRAGMENT = 5, // Fragment + HUF_USERINFO = 6, // User Info + HUF_MAX = 7, // (Field Count) +} En_HP_HttpUrlField; + +/************************************************************************ +名称:HTTP 解析结果标识 +描述:指示 HTTP 解析器是否继续执行解析操作 +************************************************************************/ +typedef enum EnHttpParseResult +{ + HPR_OK = 0, // 解析成功 + HPR_SKIP_BODY = 1, // 跳过当前请求 BODY(仅用于 OnHeadersComplete 事件) + HPR_UPGRADE = 2, // 升级协议(仅用于 OnHeadersComplete 事件) + HPR_ERROR = -1, // 解析错误,终止解析,断开连接 +} En_HP_HttpParseResult; + +/************************************************************************ +名称:HTTP 协议升级类型 +描述:标识 HTTP 升级为哪种协议 +************************************************************************/ +typedef enum EnHttpUpgradeType +{ + HUT_NONE = 0, // 没有升级 + HUT_WEB_SOCKET = 1, // WebSocket + HUT_HTTP_TUNNEL = 2, // HTTP 隧道 + HUT_UNKNOWN = -1, // 未知类型 +} En_HP_HttpUpgradeType; + +/************************************************************************ +名称:HTTP 状态码 +描述:HTTP 标准状态码 +************************************************************************/ +typedef enum EnHttpStatusCode +{ + HSC_CONTINUE = 100, + HSC_SWITCHING_PROTOCOLS = 101, + HSC_PROCESSING = 102, + HSC_EARLY_HINTS = 103, + HSC_RESPONSE_IS_STALE = 110, + HSC_REVALIDATION_FAILED = 111, + HSC_DISCONNECTED_OPERATION = 112, + HSC_HEURISTIC_EXPIRATION = 113, + HSC_MISCELLANEOUS_WARNING = 199, + + HSC_OK = 200, + HSC_CREATED = 201, + HSC_ACCEPTED = 202, + HSC_NON_AUTHORITATIVE_INFORMATION = 203, + HSC_NO_CONTENT = 204, + HSC_RESET_CONTENT = 205, + HSC_PARTIAL_CONTENT = 206, + HSC_MULTI_STATUS = 207, + HSC_ALREADY_REPORTED = 208, + HSC_TRANSFORMATION_APPLIED = 214, + HSC_IM_USED = 226, + HSC_MISCELLANEOUS_PERSISTENT_WARNING = 299, + + HSC_MULTIPLE_CHOICES = 300, + HSC_MOVED_PERMANENTLY = 301, + HSC_MOVED_TEMPORARILY = 302, + HSC_SEE_OTHER = 303, + HSC_NOT_MODIFIED = 304, + HSC_USE_PROXY = 305, + HSC_SWITCH_PROXY = 306, + HSC_TEMPORARY_REDIRECT = 307, + HSC_PERMANENT_REDIRECT = 308, + + HSC_BAD_REQUEST = 400, + HSC_UNAUTHORIZED = 401, + HSC_PAYMENT_REQUIRED = 402, + HSC_FORBIDDEN = 403, + HSC_NOT_FOUND = 404, + HSC_METHOD_NOT_ALLOWED = 405, + HSC_NOT_ACCEPTABLE = 406, + HSC_PROXY_AUTHENTICATION_REQUIRED = 407, + HSC_REQUEST_TIMEOUT = 408, + HSC_CONFLICT = 409, + HSC_GONE = 410, + HSC_LENGTH_REQUIRED = 411, + HSC_PRECONDITION_FAILED = 412, + HSC_REQUEST_ENTITY_TOO_LARGE = 413, + HSC_REQUEST_URI_TOO_LONG = 414, + HSC_UNSUPPORTED_MEDIA_TYPE = 415, + HSC_REQUESTED_RANGE_NOT_SATISFIABLE = 416, + HSC_EXPECTATION_FAILED = 417, + HSC_IM_A_TEAPOT = 418, + HSC_PAGE_EXPIRED = 419, + HSC_ENHANCE_YOUR_CALM = 420, + HSC_MISDIRECTED_REQUEST = 421, + HSC_UNPROCESSABLE_ENTITY = 422, + HSC_LOCKED = 423, + HSC_FAILED_DEPENDENCY = 424, + HSC_UNORDERED_COLLECTION = 425, + HSC_UPGRADE_REQUIRED = 426, + HSC_PRECONDITION_REQUIRED = 428, + HSC_TOO_MANY_REQUESTS = 429, + HSC_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430, + HSC_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + HSC_LOGIN_TIMEOUT = 440, + HSC_NO_RESPONSE = 444, + HSC_RETRY_WITH = 449, + HSC_BLOCKED_BY_PARENTAL_CONTROL = 450, + HSC_UNAVAILABLE_FOR_LEGAL_REASONS = 451, + HSC_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460, + HSC_INVALID_X_FORWARDED_FOR = 463, + HSC_REQUEST_HEADER_TOO_LARGE = 494, + HSC_SSL_CERTIFICATE_ERROR = 495, + HSC_SSL_CERTIFICATE_REQUIRED = 496, + HSC_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497, + HSC_INVALID_TOKEN = 498, + HSC_CLIENT_CLOSED_REQUEST = 499, + + HSC_INTERNAL_SERVER_ERROR = 500, + HSC_NOT_IMPLEMENTED = 501, + HSC_BAD_GATEWAY = 502, + HSC_SERVICE_UNAVAILABLE = 503, + HSC_GATEWAY_TIMEOUT = 504, + HSC_HTTP_VERSION_NOT_SUPPORTED = 505, + HSC_VARIANT_ALSO_NEGOTIATES = 506, + HSC_INSUFFICIENT_STORAGE = 507, + HSC_LOOP_DETECTED = 508, + HSC_BANDWIDTH_LIMIT_EXCEEDED = 509, + HSC_NOT_EXTENDED = 510, + HSC_NETWORK_AUTHENTICATION_REQUIRED = 511, + HSC_WEB_SERVER_UNKNOWN_ERROR = 520, + HSC_WEB_SERVER_IS_DOWN = 521, + HSC_CONNECTION_TIMEOUT = 522, + HSC_ORIGIN_IS_UNREACHABLE = 523, + HSC_TIMEOUT_OCCURED = 524, + HSC_SSL_HANDSHAKE_FAILED = 525, + HSC_INVALID_SSL_CERTIFICATE = 526, + HSC_RAILGUN_ERROR = 527, + HSC_SITE_IS_OVERLOADED = 529, + HSC_SITE_IS_FROZEN = 530, + HSC_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561, + HSC_NETWORK_READ_TIMEOUT = 598, + HSC_NETWORK_CONNECT_TIMEOUT = 599, + + HSC_UNPARSEABLE_RESPONSE_HEADERS = 600 +} En_HP_HttpStatusCode; + +/************************************************************************ +名称:Name/Value 结构体 +描述:字符串名值对结构体 +************************************************************************/ +typedef struct TNVPair +{ + LPCSTR name; + LPCSTR value; +} HP_TNVPair, +TParam, HP_TParam, *LPPARAM, *HP_LPPARAM, +THeader, HP_THeader, *LPHEADER, *HP_LPHEADER, +TCookie, HP_TCookie, *LPCOOKIE, *HP_LPCOOKIE; + +#endif + +/*****************************************************************************************************************************************************/ +/********************************************************** Compress / Decompress Definitions ********************************************************/ +/*****************************************************************************************************************************************************/ + +/************************************************************************ +名称:数据回调函数 +描述:回调处理过程中产生的数据输出 +参数: + pData -- 数据缓冲区 + iLength -- 数据长度 + pContext -- 回调上下文 + +返回值: + TRUE -- 成功 + FALSE -- 失败 + +************************************************************************/ +typedef BOOL (__HP_CALL *Fn_DataCallback)(const BYTE* pData, int iLength, PVOID pContext); +typedef Fn_DataCallback Fn_CompressDataCallback; +typedef Fn_DataCallback Fn_DecompressDataCallback; +typedef Fn_DataCallback HP_Fn_DataCallback; +typedef Fn_DataCallback HP_Fn_CompressDataCallback; +typedef Fn_DataCallback HP_Fn_DecompressDataCallback; diff --git a/hpsocket/SocketInterface.h b/hpsocket/SocketInterface.h new file mode 100644 index 0000000..c3cc68b --- /dev/null +++ b/hpsocket/SocketInterface.h @@ -0,0 +1,3114 @@ +/* + * 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 "HPTypeDef.h" + +/*****************************************************************************************************************************************************/ +/***************************************************************** TCP/UDP Interfaces ****************************************************************/ +/*****************************************************************************************************************************************************/ + +/************************************************************************ +名称:双接口模版类 +描述:定义双接口转换方法 +************************************************************************/ + +#if defined(__arm__) && defined(__GNUC__) && !(defined(__clang__) || defined(__llvm__)) + +#define __DUAL_VPTR_GAP__ sizeof(PVOID) + +class __IFakeDualInterface__ +{ +public: + virtual ~__IFakeDualInterface__() {} +}; + +template class DualInterface : public F, private __IFakeDualInterface__, public S + +#else + +#define __DUAL_VPTR_GAP__ 0 + +template class DualInterface : public F, public S + +#endif + +{ +public: + + /* this 转换为 F* */ + inline static F* ToF(DualInterface* pThis) + { + return (F*)(pThis); + } + + /* F* 转换为 this */ + inline static DualInterface* FromF(F* pF) + { + return (DualInterface*)(pF); + } + + /* this 转换为 S* */ + inline static S* ToS(DualInterface* pThis) + { + return (S*)(F2S(ToF(pThis))); + } + + /* S* 转换为 this */ + inline static DualInterface* FromS(S* pS) + { + return FromF(S2F(pS)); + } + + /* S* 转换为 F* */ + inline static F* S2F(S* pS) + { + return (F*)((char*)pS - (sizeof(F) + __DUAL_VPTR_GAP__)); + } + + /* F* 转换为 S* */ + inline static S* F2S(F* pF) + { + return (S*)((char*)pF + (sizeof(F) + __DUAL_VPTR_GAP__)); + } + +public: + virtual ~DualInterface() = default; +}; + +/************************************************************************ +名称:复合 Socket 组件接口 +描述:定义复合 Socket 组件的所有操作方法和属性访问方法,复合 Socket 组件同时管理多个 Socket 连接 +************************************************************************/ +class IComplexSocket +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:关闭通信组件 + * 描述:关闭通信组件,关闭完成后断开所有连接并释放所有资源 + * + * 参数: + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 GetLastError() 获取错误代码 + */ + virtual BOOL Stop () = 0; + + /* + * 名称:发送数据 + * 描述:向指定连接发送数据 + * + * 参数: dwConnID -- 连接 ID + * pBuffer -- 发送缓冲区 + * iLength -- 发送缓冲区长度 + * iOffset -- 发送缓冲区指针偏移量 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Send (CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0) = 0; + + /* + * 名称:发送多组数据 + * 描述:向指定连接发送多组数据 + * TCP - 顺序发送所有数据包 + * UDP - 把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) + * + * 参数: dwConnID -- 连接 ID + * pBuffers -- 发送缓冲区数组 + * iCount -- 发送缓冲区数目 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount) = 0; + + /* + * 名称:暂停/恢复接收 + * 描述:暂停/恢复某个连接的数据接收工作 + * + * 参数: dwConnID -- 连接 ID + * bPause -- TRUE - 暂停, FALSE - 恢复 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL PauseReceive(CONNID dwConnID, BOOL bPause = TRUE) = 0; + + /* + * 名称:断开连接 + * 描述:断开某个连接 + * + * 参数: dwConnID -- 连接 ID + * bForce -- 是否强制断开连接 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL Disconnect(CONNID dwConnID, BOOL bForce = TRUE) = 0; + + /* + * 名称:断开超时连接 + * 描述:断开超过指定时长的连接 + * + * 参数: dwPeriod -- 时长(毫秒) + * bForce -- 是否强制断开连接 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL DisconnectLongConnections(DWORD dwPeriod, BOOL bForce = TRUE) = 0; + + /* + * 名称:断开静默连接 + * 描述:断开超过指定时长的静默连接 + * + * 参数: dwPeriod -- 时长(毫秒) + * bForce -- 是否强制断开连接 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce = TRUE) = 0; + + /* + * 名称:等待 + * 描述:等待通信组件停止运行 + * + * 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Wait(DWORD dwMilliseconds = INFINITE) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* + * 名称:设置连接的附加数据 + * 描述:是否为连接绑定附加数据或者绑定什么样的数据,均由应用程序自身决定 + * + * 参数: dwConnID -- 连接 ID + * pv -- 数据 + * 返回值: TRUE -- 成功 + * FALSE -- 失败(无效的连接 ID) + */ + virtual BOOL SetConnectionExtra (CONNID dwConnID, PVOID pExtra) = 0; + + /* + * 名称:获取连接的附加数据 + * 描述:是否为连接绑定附加数据或者绑定什么样的数据,均由应用程序自身决定 + * + * 参数: dwConnID -- 连接 ID + * ppv -- 数据指针 + * 返回值: TRUE -- 成功 + * FALSE -- 失败(无效的连接 ID) + */ + virtual BOOL GetConnectionExtra (CONNID dwConnID, PVOID* ppExtra) = 0; + + /* 检测是否为安全连接(SSL/HTTPS) */ + virtual BOOL IsSecure () = 0; + /* 检查通信组件是否已启动 */ + virtual BOOL HasStarted () = 0; + /* 查看通信组件当前状态 */ + virtual EnServiceState GetState () = 0; + /* 获取连接数 */ + virtual DWORD GetConnectionCount () = 0; + /* 获取所有连接的 CONNID */ + virtual BOOL GetAllConnectionIDs (CONNID pIDs[], DWORD& dwCount) = 0; + /* 获取某个连接时长(毫秒) */ + virtual BOOL GetConnectPeriod (CONNID dwConnID, DWORD& dwPeriod) = 0; + /* 获取某个连接静默时间(毫秒) */ + virtual BOOL GetSilencePeriod (CONNID dwConnID, DWORD& dwPeriod) = 0; + /* 获取某个连接的本地地址信息 */ + virtual BOOL GetLocalAddress (CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) = 0; + /* 获取某个连接的远程地址信息 */ + virtual BOOL GetRemoteAddress (CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) = 0; + /* 获取最近一次失败操作的错误代码 */ + virtual EnSocketError GetLastError () = 0; + /* 获取最近一次失败操作的错误描述 */ + virtual LPCTSTR GetLastErrorDesc () = 0; + /* 获取连接中未发出数据的长度 */ + virtual BOOL GetPendingDataLength (CONNID dwConnID, int& iPending) = 0; + /* 获取连接的数据接收状态 */ + virtual BOOL IsPauseReceive (CONNID dwConnID, BOOL& bPaused) = 0; + /* 检测是否有效连接 */ + virtual BOOL IsConnected (CONNID dwConnID) = 0; + + /* 设置地址重用选项 */ + virtual void SetReuseAddressPolicy(EnReuseAddressPolicy enReusePolicy) = 0; + /* 设置数据发送策略(对 Linux 平台组件无效) */ + virtual void SetSendPolicy (EnSendPolicy enSendPolicy) = 0; + /* 设置 OnSend 事件同步策略(对 Linux 平台组件无效) */ + virtual void SetOnSendSyncPolicy (EnOnSendSyncPolicy enSyncPolicy) = 0; + /* 设置最大连接数(组件会根据设置值预分配内存,因此需要根据实际情况设置,不宜过大)*/ + virtual void SetMaxConnectionCount (DWORD dwMaxConnectionCount) = 0; + /* 设置 Socket 缓存对象锁定时间(毫秒,在锁定期间该 Socket 缓存对象不能被获取使用) */ + virtual void SetFreeSocketObjLockTime (DWORD dwFreeSocketObjLockTime) = 0; + /* 设置 Socket 缓存池大小(通常设置为平均并发连接数的 1/3 - 1/2) */ + virtual void SetFreeSocketObjPool (DWORD dwFreeSocketObjPool) = 0; + /* 设置内存块缓存池大小(通常设置为 Socket 缓存池大小的 2 - 3 倍) */ + virtual void SetFreeBufferObjPool (DWORD dwFreeBufferObjPool) = 0; + /* 设置 Socket 缓存池回收阀值(通常设置为 Socket 缓存池大小的 3 倍) */ + virtual void SetFreeSocketObjHold (DWORD dwFreeSocketObjHold) = 0; + /* 设置内存块缓存池回收阀值 */ + virtual void SetFreeBufferObjHold (DWORD dwFreeBufferObjHold) = 0; + /* 设置工作线程数量(通常设置为 2 * CPU + 2) */ + virtual void SetWorkerThreadCount (DWORD dwWorkerThreadCount) = 0; + /* 设置是否标记静默时间(设置为 TRUE 时 DisconnectSilenceConnections() 和 GetSilencePeriod() 才有效,默认:TRUE) */ + virtual void SetMarkSilence (BOOL bMarkSilence) = 0; + + /* 获取地址重用选项 */ + virtual EnReuseAddressPolicy GetReuseAddressPolicy () = 0; + /* 获取数据发送策略(对 Linux 平台组件无效) */ + virtual EnSendPolicy GetSendPolicy () = 0; + /* 获取 OnSend 事件同步策略(对 Linux 平台组件无效) */ + virtual EnOnSendSyncPolicy GetOnSendSyncPolicy () = 0; + /* 获取最大连接数 */ + virtual DWORD GetMaxConnectionCount () = 0; + /* 获取 Socket 缓存对象锁定时间 */ + virtual DWORD GetFreeSocketObjLockTime () = 0; + /* 获取 Socket 缓存池大小 */ + virtual DWORD GetFreeSocketObjPool () = 0; + /* 获取内存块缓存池大小 */ + virtual DWORD GetFreeBufferObjPool () = 0; + /* 获取 Socket 缓存池回收阀值 */ + virtual DWORD GetFreeSocketObjHold () = 0; + /* 获取内存块缓存池回收阀值 */ + virtual DWORD GetFreeBufferObjHold () = 0; + /* 获取工作线程数量 */ + virtual DWORD GetWorkerThreadCount () = 0; + /* 检测是否标记静默时间 */ + virtual BOOL IsMarkSilence () = 0; + +public: + virtual ~IComplexSocket() = default; +}; + +/************************************************************************ +名称:通信服务端组件接口 +描述:定义通信服务端组件的所有操作方法和属性访问方法 +************************************************************************/ +class IServer : public IComplexSocket +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:启动通信组件 + * 描述:启动服务端通信组件,启动完成后可开始接收客户端连接并收发数据 + * + * 参数: lpszBindAddress -- 监听地址 + * usPort -- 监听端口 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 GetLastError() 获取错误代码 + */ + virtual BOOL Start (LPCTSTR lpszBindAddress, USHORT usPort) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 获取监听 Socket 的地址信息 */ + virtual BOOL GetListenAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) = 0; +}; + +/************************************************************************ +名称:TCP 通信服务端组件接口 +描述:定义 TCP 通信服务端组件的所有操作方法和属性访问方法 +************************************************************************/ +class ITcpServer : public IServer +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:发送小文件 + * 描述:向指定连接发送 4096 KB 以下的小文件 + * + * 参数: dwConnID -- 连接 ID + * lpszFileName -- 文件路径 + * pHead -- 头部附加数据 + * pTail -- 尾部附加数据 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL SendSmallFile(CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr) = 0; + +#ifdef _SSL_SUPPORT + /* + * 名称:初始化通信组件 SSL 环境参数 + * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpszPemCertFile -- 证书文件 + * lpszPemKeyFile -- 私钥文件 + * lpszKeyPassword -- 私钥密码(没有密码则为空) + * lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证或客户端可选) + * fnServerNameCallback -- SNI 回调函数指针(可选,如果为 nullptr 则使用 SNI 默认回调函数) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + 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) = 0; + + /* + * 名称:初始化通信组件 SSL 环境参数(通过内存加载证书) + * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpszPemCert -- 证书内容 + * lpszPemKey -- 私钥内容 + * lpszKeyPassword -- 私钥密码(没有密码则为空) + * lpszCAPemCert -- CA 证书内容(单向验证或客户端可选) + * fnServerNameCallback -- SNI 回调函数指针(可选,如果为 nullptr 则使用 SNI 默认回调函数) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + 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) = 0; + + /* + * 名称:增加 SNI 主机证书 + * 描述:SSL 服务端在 SetupSSLContext() 成功后可以调用本方法增加多个 SNI 主机证书 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpszPemCertFile -- 证书文件 + * lpszPemKeyFile -- 私钥文件 + * lpszKeyPassword -- 私钥密码(没有密码则为空) + * lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证可选) + * + * 返回值: 正数 -- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机 + * 负数 -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual int AddSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr) = 0; + + /* + * 名称:增加 SNI 主机证书(通过内存加载证书) + * 描述:SSL 服务端在 SetupSSLContext() 成功后可以调用本方法增加多个 SNI 主机证书 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpszPemCert -- 证书内容 + * lpszPemKey -- 私钥内容 + * lpszKeyPassword -- 私钥密码(没有密码则为空) + * lpszCAPemCert -- CA 证书内容(单向验证可选) + * + * 返回值: 正数 -- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机 + * 负数 -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual int AddSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr) = 0; + + /* + * 名称:绑定 SNI 主机域名 + * 描述:SSL 服务端在 AddSSLContext() 成功后可以调用本方法绑定主机域名到 SNI 主机证书 + * + * 参数: lpszServerName -- 主机域名 + * iContextIndex -- SNI 主机证书对应的索引 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL BindSSLServerName(LPCTSTR lpszServerName, int iContextIndex) = 0; + + /* + * 名称:清理通信组件 SSL 运行环境 + * 描述:清理通信组件 SSL 运行环境,回收 SSL 相关内存 + * 1、通信组件析构时会自动调用本方法 + * 2、当要重新设置通信组件 SSL 环境参数时,需要先调用本方法清理原先的环境参数 + * + * 参数: 无 + * + * 返回值:无 + */ + virtual void CleanupSSLContext() = 0; + + /* + * 名称:启动 SSL 握手 + * 描述:当通信组件设置为非自动握手时,需要调用本方法启动 SSL 握手 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL StartSSLHandShake(CONNID dwConnID) = 0; + +#endif + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置 EPOLL 等待事件的最大数量 */ + virtual void SetAcceptSocketCount (DWORD dwAcceptSocketCount) = 0; + /* 设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为 1024 的倍数) */ + virtual void SetSocketBufferSize (DWORD dwSocketBufferSize) = 0; + /* 设置监听 Socket 的等候队列大小(根据并发连接数量调整设置) */ + virtual void SetSocketListenQueue (DWORD dwSocketListenQueue) = 0; + /* 设置正常心跳包间隔(毫秒,0 则不发送心跳包,默认:60 * 1000) */ + virtual void SetKeepAliveTime (DWORD dwKeepAliveTime) = 0; + /* 设置异常心跳包间隔(毫秒,0 不发送心跳包,,默认:20 * 1000,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线) */ + virtual void SetKeepAliveInterval (DWORD dwKeepAliveInterval) = 0; + /* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ + virtual void SetNoDelay (BOOL bNoDelay) = 0; + + /* 获取 EPOLL 等待事件的最大数量 */ + virtual DWORD GetAcceptSocketCount () = 0; + /* 获取通信数据缓冲区大小 */ + virtual DWORD GetSocketBufferSize () = 0; + /* 获取监听 Socket 的等候队列大小 */ + virtual DWORD GetSocketListenQueue () = 0; + /* 获取正常心跳包间隔 */ + virtual DWORD GetKeepAliveTime () = 0; + /* 获取异常心跳包间隔 */ + virtual DWORD GetKeepAliveInterval () = 0; + /* 检查是否开启 nodelay 模式 */ + virtual BOOL IsNoDelay () = 0; + +#ifdef _SSL_SUPPORT + /* 设置通信组件握手方式(默认:TRUE,自动握手) */ + virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) = 0; + /* 获取通信组件握手方式 */ + virtual BOOL IsSSLAutoHandShake() = 0; + + /* 设置 SSL 加密算法列表 */ + virtual void SetSSLCipherList(LPCTSTR lpszCipherList) = 0; + /* 获取 SSL 加密算法列表 */ + virtual LPCTSTR GetSSLCipherList() = 0; + + /* + * 名称:获取 SSL Session 信息 + * 描述:获取指定类型的 SSL Session 信息(输出类型参考:EnSSLSessionInfo) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo) = 0; +#endif + +}; + +#ifdef _UDP_SUPPORT + +/************************************************************************ +名称:UDP 通信服务端组件接口 +描述:定义 UDP 通信服务端组件的所有操作方法和属性访问方法 +************************************************************************/ +class IUdpServer : public IServer +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置数据报文最大长度(建议在局域网环境下不超过 1432 字节,在广域网环境下不超过 548 字节) */ + virtual void SetMaxDatagramSize (DWORD dwMaxDatagramSize) = 0; + /* 获取数据报文最大长度 */ + virtual DWORD GetMaxDatagramSize () = 0; + + /* 设置 Receive 预投递数量(根据负载调整设置,Receive 预投递数量越大则丢包概率越小) */ + virtual void SetPostReceiveCount (DWORD dwPostReceiveCount) = 0; + /* 获取 Receive 预投递数量 */ + virtual DWORD GetPostReceiveCount () = 0; + + /* 设置监测包尝试次数(0 则不发送监测跳包,如果超过最大尝试次数则认为已断线) */ + virtual void SetDetectAttempts (DWORD dwDetectAttempts) = 0; + /* 设置监测包发送间隔(毫秒,0 不发送监测包) */ + virtual void SetDetectInterval (DWORD dwDetectInterval) = 0; + /* 获取心跳检查次数 */ + virtual DWORD GetDetectAttempts () = 0; + /* 获取心跳检查间隔 */ + virtual DWORD GetDetectInterval () = 0; +}; + +/************************************************************************ +名称:Server/Agent ARQ 模型组件接口 +描述:定义 Server/Agent 组件的 ARQ 模型组件的所有操作方法 +************************************************************************/ +class IArqSocket +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ + virtual void SetNoDelay (BOOL bNoDelay) = 0; + /* 设置是否关闭拥塞控制(默认:FALSE,不关闭) */ + virtual void SetTurnoffCongestCtrl (BOOL bTurnOff) = 0; + /* 设置数据刷新间隔(毫秒,默认:60) */ + virtual void SetFlushInterval (DWORD dwFlushInterval) = 0; + /* 设置快速重传 ACK 跨越次数(默认:0,关闭快速重传) */ + virtual void SetResendByAcks (DWORD dwResendByAcks) = 0; + /* 设置发送窗口大小(数据包数量,默认:128) */ + virtual void SetSendWndSize (DWORD dwSendWndSize) = 0; + /* 设置接收窗口大小(数据包数量,默认:512) */ + virtual void SetRecvWndSize (DWORD dwRecvWndSize) = 0; + /* 设置最小重传超时时间(毫秒,默认:30) */ + virtual void SetMinRto (DWORD dwMinRto) = 0; + /* 设置快速握手次数限制(默认:5,如果为 0 则不限制) */ + virtual void SetFastLimit (DWORD dwFastLimit) = 0; + /* 设置最大传输单元(默认:0,与 SetMaxDatagramSize() 一致) */ + virtual void SetMaxTransUnit (DWORD dwMaxTransUnit) = 0; + /* 设置最大数据包大小(默认:4096) */ + virtual void SetMaxMessageSize (DWORD dwMaxMessageSize) = 0; + /* 设置握手超时时间(毫秒,默认:5000) */ + virtual void SetHandShakeTimeout (DWORD dwHandShakeTimeout) = 0; + + /* 检测是否开启 nodelay 模式 */ + virtual BOOL IsNoDelay () = 0; + /* 检测是否关闭拥塞控制 */ + virtual BOOL IsTurnoffCongestCtrl () = 0; + /* 获取数据刷新间隔 */ + virtual DWORD GetFlushInterval () = 0; + /* 获取快速重传 ACK 跨越次数 */ + virtual DWORD GetResendByAcks () = 0; + /* 获取发送窗口大小 */ + virtual DWORD GetSendWndSize () = 0; + /* 获取接收窗口大小 */ + virtual DWORD GetRecvWndSize () = 0; + /* 获取最小重传超时时间 */ + virtual DWORD GetMinRto () = 0; + /* 获取快速握手次数限制 */ + virtual DWORD GetFastLimit () = 0; + /* 获取最大传输单元 */ + virtual DWORD GetMaxTransUnit () = 0; + /* 获取最大数据包大小 */ + virtual DWORD GetMaxMessageSize () = 0; + /* 获取握手超时时间 */ + virtual DWORD GetHandShakeTimeout () = 0; + + /* 获取等待发送包数量 */ + virtual BOOL GetWaitingSendMessageCount (CONNID dwConnID, int& iCount) = 0; + +public: + virtual ~IArqSocket() = default; +}; + +/************************************************************************ +名称:UDP ARQ 通信服务端组件接口 +描述:继承了 ARQ 和 Server 接口 +************************************************************************/ +typedef DualInterface IUdpArqServer; + +#endif + +/************************************************************************ +名称:通信代理组件接口 +描述:定义通信代理组件的所有操作方法和属性访问方法,代理组件本质是一个同时连接多个服务器的客户端组件 +************************************************************************/ +class IAgent : public IComplexSocket +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:启动通信组件 + * 描述:启动通信代理组件,启动完成后可开始连接远程服务器 + * + * 参数: lpszBindAddress -- 绑定地址(默认:nullptr,绑定任意地址) + * bAsyncConnect -- 是否采用异步 Connect + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 GetLastError() 获取错误代码 + */ + virtual BOOL Start (LPCTSTR lpszBindAddress = nullptr, BOOL bAsyncConnect = TRUE) = 0; + + /* + * 名称:连接服务器 + * 描述:连接服务器,连接成功后 IAgentListener 会接收到 OnConnect() / OnHandShake() 事件 + * + * 参数: lpszRemoteAddress -- 服务端地址 + * usPort -- 服务端端口 + * pdwConnID -- 连接 ID(默认:nullptr,不获取连接 ID) + * pExtra -- 连接附加数据(默认:nullptr) + * usLocalPort -- 本地端口(默认:0) + * lpszLocalAddress -- 本地地址(默认:nullptr,使用 Start() 方法中绑定的地址) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Connect(LPCTSTR lpszRemoteAddress, USHORT usPort, CONNID* pdwConnID = nullptr, PVOID pExtra = nullptr, USHORT usLocalPort = 0, LPCTSTR lpszLocalAddress = nullptr) = 0; + + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 获取某个连接的远程主机信息 */ + virtual BOOL GetRemoteHost (CONNID dwConnID, TCHAR lpszHost[], int& iHostLen, USHORT& usPort) = 0; + +}; + +/************************************************************************ +名称:TCP 通信代理组件接口 +描述:定义 TCP 通信代理组件的所有操作方法和属性访问方法 +************************************************************************/ +class ITcpAgent : public IAgent +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:发送小文件 + * 描述:向指定连接发送 4096 KB 以下的小文件 + * + * 参数: dwConnID -- 连接 ID + * lpszFileName -- 文件路径 + * pHead -- 头部附加数据 + * pTail -- 尾部附加数据 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL SendSmallFile(CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr) = 0; + +#ifdef _SSL_SUPPORT + /* + * 名称:初始化通信组件 SSL 环境参数 + * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpszPemCertFile -- 证书文件(客户端可选) + * lpszPemKeyFile -- 私钥文件(客户端可选) + * lpszKeyPassword -- 私钥密码(没有密码则为空) + * lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证或客户端可选) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL SetupSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr) = 0; + + /* + * 名称:初始化通信组件 SSL 环境参数(通过内存加载证书) + * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpszPemCert -- 证书内容 + * lpszPemKey -- 私钥内容 + * lpszKeyPassword -- 私钥密码(没有密码则为空) + * lpszCAPemCert -- CA 证书内容(单向验证或客户端可选) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr) = 0; + + /* + * 名称:清理通信组件 SSL 运行环境 + * 描述:清理通信组件 SSL 运行环境,回收 SSL 相关内存 + * 1、通信组件析构时会自动调用本方法 + * 2、当要重新设置通信组件 SSL 环境参数时,需要先调用本方法清理原先的环境参数 + * + * 参数: 无 + * + * 返回值:无 + */ + virtual void CleanupSSLContext() = 0; + + /* + * 名称:启动 SSL 握手 + * 描述:当通信组件设置为非自动握手时,需要调用本方法启动 SSL 握手 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL StartSSLHandShake(CONNID dwConnID) = 0; + +#endif + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置同步连接超时时间(毫秒) */ + virtual void SetSyncConnectTimeout (DWORD dwSyncConnectTimeout) = 0; + /* 设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为 1024 的倍数) */ + virtual void SetSocketBufferSize (DWORD dwSocketBufferSize) = 0; + /* 设置正常心跳包间隔(毫秒,0 则不发送心跳包,默认:60 * 1000) */ + virtual void SetKeepAliveTime (DWORD dwKeepAliveTime) = 0; + /* 设置异常心跳包间隔(毫秒,0 不发送心跳包,,默认:20 * 1000,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线) */ + virtual void SetKeepAliveInterval (DWORD dwKeepAliveInterval) = 0; + /* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ + virtual void SetNoDelay (BOOL bNoDelay) = 0; + + /* 获取同步连接超时时间 */ + virtual DWORD GetSyncConnectTimeout () = 0; + /* 获取通信数据缓冲区大小 */ + virtual DWORD GetSocketBufferSize () = 0; + /* 获取正常心跳包间隔 */ + virtual DWORD GetKeepAliveTime () = 0; + /* 获取异常心跳包间隔 */ + virtual DWORD GetKeepAliveInterval () = 0; + /* 检查是否开启 nodelay 模式 */ + virtual BOOL IsNoDelay () = 0; + +#ifdef _SSL_SUPPORT + /* 设置通信组件握手方式(默认:TRUE,自动握手) */ + virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) = 0; + /* 获取通信组件握手方式 */ + virtual BOOL IsSSLAutoHandShake() = 0; + + /* 设置 SSL 加密算法列表 */ + virtual void SetSSLCipherList(LPCTSTR lpszCipherList) = 0; + /* 获取 SSL 加密算法列表 */ + virtual LPCTSTR GetSSLCipherList() = 0; + + /* + * 名称:获取 SSL Session 信息 + * 描述:获取指定类型的 SSL Session 信息(输出类型参考:EnSSLSessionInfo) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo) = 0; +#endif + +}; + +/************************************************************************ +名称:通信客户端组件接口 +描述:定义通信客户端组件的所有操作方法和属性访问方法 +************************************************************************/ +class IClient +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:启动通信组件 + * 描述:启动客户端通信组件并连接服务端,启动完成后可开始收发数据 + * + * 参数: lpszRemoteAddress -- 服务端地址 + * usPort -- 服务端端口 + * bAsyncConnect -- 是否采用异步 Connect + * lpszBindAddress -- 绑定地址(默认:nullptr,TcpClient/UdpClient -> 不执行绑定操作,UdpCast 绑定 -> 任意地址) + * usLocalPort -- 本地端口(默认:0) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 GetLastError() 获取错误代码 + */ + virtual BOOL Start (LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0) = 0; + + /* + * 名称:关闭通信组件 + * 描述:关闭客户端通信组件,关闭完成后断开与服务端的连接并释放所有资源 + * + * 参数: + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 GetLastError() 获取错误代码 + */ + virtual BOOL Stop () = 0; + + /* + * 名称:发送数据 + * 描述:向服务端发送数据 + * + * 参数: pBuffer -- 发送缓冲区 + * iLength -- 发送缓冲区长度 + * iOffset -- 发送缓冲区指针偏移量 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Send (const BYTE* pBuffer, int iLength, int iOffset = 0) = 0; + + /* + * 名称:发送多组数据 + * 描述:向服务端发送多组数据 + * TCP - 顺序发送所有数据包 + * UDP - 把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) + * + * 参数: pBuffers -- 发送缓冲区数组 + * iCount -- 发送缓冲区数目 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL SendPackets(const WSABUF pBuffers[], int iCount) = 0; + + /* + * 名称:暂停/恢复接收 + * 描述:暂停/恢复某个连接的数据接收工作 + * + * bPause -- TRUE - 暂停, FALSE - 恢复 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL PauseReceive(BOOL bPause = TRUE) = 0; + + /* + * 名称:等待 + * 描述:等待通信组件停止运行 + * + * 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Wait(DWORD dwMilliseconds = INFINITE) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置连接的附加数据 */ + virtual void SetExtra (PVOID pExtra) = 0; + + /* 获取连接的附加数据 */ + virtual PVOID GetExtra () = 0; + + /* 检测是否为安全连接(SSL/HTTPS) */ + virtual BOOL IsSecure () = 0; + /* 检查通信组件是否已启动 */ + virtual BOOL HasStarted () = 0; + /* 查看通信组件当前状态 */ + virtual EnServiceState GetState () = 0; + /* 获取最近一次失败操作的错误代码 */ + virtual EnSocketError GetLastError () = 0; + /* 获取最近一次失败操作的错误描述 */ + virtual LPCTSTR GetLastErrorDesc() = 0; + /* 获取该组件对象的连接 ID */ + virtual CONNID GetConnectionID () = 0; + /* 获取 Client Socket 的地址信息 */ + virtual BOOL GetLocalAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) = 0; + /* 获取连接的远程主机信息 */ + virtual BOOL GetRemoteHost (TCHAR lpszHost[], int& iHostLen, USHORT& usPort) = 0; + /* 获取连接中未发出数据的长度 */ + virtual BOOL GetPendingDataLength (int& iPending) = 0; + /* 获取连接的数据接收状态 */ + virtual BOOL IsPauseReceive (BOOL& bPaused) = 0; + /* 检测是否有效连接 */ + virtual BOOL IsConnected () = 0; + + /* 设置地址重用选项 */ + virtual void SetReuseAddressPolicy(EnReuseAddressPolicy enReusePolicy) = 0; + /* 设置内存块缓存池大小 */ + virtual void SetFreeBufferPoolSize (DWORD dwFreeBufferPoolSize) = 0; + /* 设置内存块缓存池回收阀值 */ + virtual void SetFreeBufferPoolHold (DWORD dwFreeBufferPoolHold) = 0; + + /* 获取地址重用选项 */ + virtual EnReuseAddressPolicy GetReuseAddressPolicy () = 0; + /* 获取内存块缓存池大小 */ + virtual DWORD GetFreeBufferPoolSize () = 0; + /* 获取内存块缓存池回收阀值 */ + virtual DWORD GetFreeBufferPoolHold () = 0; + +public: + virtual ~IClient() = default; +}; + +/************************************************************************ +名称:TCP 通信客户端组件接口 +描述:定义 TCP 通信客户端组件的所有操作方法和属性访问方法 +************************************************************************/ +class ITcpClient : public IClient +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:发送小文件 + * 描述:向服务端发送 4096 KB 以下的小文件 + * + * 参数: lpszFileName -- 文件路径 + * pHead -- 头部附加数据 + * pTail -- 尾部附加数据 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL SendSmallFile(LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr) = 0; + +#ifdef _SSL_SUPPORT + /* + * 名称:初始化通信组件 SSL 环境参数 + * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpszPemCertFile -- 证书文件(客户端可选) + * lpszPemKeyFile -- 私钥文件(客户端可选) + * lpszKeyPassword -- 私钥密码(没有密码则为空) + * lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证或客户端可选) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL SetupSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr) = 0; + + /* + * 名称:初始化通信组件 SSL 环境参数(通过内存加载证书) + * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败 + * + * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode) + * lpszPemCert -- 证书内容 + * lpszPemKey -- 私钥内容 + * lpszKeyPassword -- 私钥密码(没有密码则为空) + * lpszCAPemCert -- CA 证书内容(单向验证或客户端可选) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr) = 0; + + /* + * 名称:清理通信组件 SSL 运行环境 + * 描述:清理通信组件 SSL 运行环境,回收 SSL 相关内存 + * 1、通信组件析构时会自动调用本方法 + * 2、当要重新设置通信组件 SSL 环境参数时,需要先调用本方法清理原先的环境参数 + * + * 参数: 无 + * + * 返回值:无 + */ + virtual void CleanupSSLContext() = 0; + + /* + * 名称:启动 SSL 握手 + * 描述:当通信组件设置为非自动握手时,需要调用本方法启动 SSL 握手 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL StartSSLHandShake() = 0; + +#endif + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置同步连接超时时间(毫秒) */ + virtual void SetSyncConnectTimeout (DWORD dwSyncConnectTimeout) = 0; + /* 设置通信数据缓冲区大小(根据平均通信数据包大小调整设置,通常设置为:(N * 1024) - sizeof(TBufferObj)) */ + virtual void SetSocketBufferSize (DWORD dwSocketBufferSize) = 0; + /* 设置正常心跳包间隔(毫秒,0 则不发送心跳包,默认:60 * 1000) */ + virtual void SetKeepAliveTime (DWORD dwKeepAliveTime) = 0; + /* 设置异常心跳包间隔(毫秒,0 不发送心跳包,,默认:20 * 1000,如果超过若干次 [默认:WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线) */ + virtual void SetKeepAliveInterval (DWORD dwKeepAliveInterval) = 0; + /* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ + virtual void SetNoDelay (BOOL bNoDelay) = 0; + + /* 获取同步连接超时时间 */ + virtual DWORD GetSyncConnectTimeout () = 0; + /* 获取通信数据缓冲区大小 */ + virtual DWORD GetSocketBufferSize () = 0; + /* 获取正常心跳包间隔 */ + virtual DWORD GetKeepAliveTime () = 0; + /* 获取异常心跳包间隔 */ + virtual DWORD GetKeepAliveInterval () = 0; + /* 检查是否开启 nodelay 模式 */ + virtual BOOL IsNoDelay () = 0; + +#ifdef _SSL_SUPPORT + /* 设置通信组件握手方式(默认:TRUE,自动握手) */ + virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) = 0; + /* 获取通信组件握手方式 */ + virtual BOOL IsSSLAutoHandShake() = 0; + + /* 设置 SSL 加密算法列表 */ + virtual void SetSSLCipherList(LPCTSTR lpszCipherList) = 0; + /* 获取 SSL 加密算法列表 */ + virtual LPCTSTR GetSSLCipherList() = 0; + + /* + * 名称:获取 SSL Session 信息 + * 描述:获取指定类型的 SSL Session 信息(输出类型参考:EnSSLSessionInfo) + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL GetSSLSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo) = 0; +#endif + +}; + +#ifdef _UDP_SUPPORT + +/************************************************************************ +名称:UDP 通信客户端组件接口 +描述:定义 UDP 通信客户端组件的所有操作方法和属性访问方法 +************************************************************************/ +class IUdpClient : public IClient +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置数据报文最大长度(建议在局域网环境下不超过 1432 字节,在广域网环境下不超过 548 字节) */ + virtual void SetMaxDatagramSize (DWORD dwMaxDatagramSize) = 0; + /* 获取数据报文最大长度 */ + virtual DWORD GetMaxDatagramSize() = 0; + + /* 设置监测包尝试次数(0 则不发送监测跳包,如果超过最大尝试次数则认为已断线) */ + virtual void SetDetectAttempts (DWORD dwDetectAttempts) = 0; + /* 设置监测包发送间隔(毫秒,0 不发送监测包) */ + virtual void SetDetectInterval (DWORD dwDetectInterval) = 0; + /* 获取心跳检查次数 */ + virtual DWORD GetDetectAttempts () = 0; + /* 获取心跳检查间隔 */ + virtual DWORD GetDetectInterval () = 0; +}; + +/************************************************************************ +名称:UDP 传播组件接口 +描述:定义 UDP 传播(组播或广播)组件的所有操作方法和属性访问方法 +************************************************************************/ +class IUdpCast : public IClient +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置数据报文最大长度(建议在局域网环境下不超过 1432 字节,在广域网环境下不超过 548 字节) */ + virtual void SetMaxDatagramSize (DWORD dwMaxDatagramSize) = 0; + /* 获取数据报文最大长度 */ + virtual DWORD GetMaxDatagramSize() = 0; + + /* 设置传播模式(组播或广播) */ + virtual void SetCastMode (EnCastMode enCastMode) = 0; + /* 获取传播模式 */ + virtual EnCastMode GetCastMode () = 0; + + /* 设置组播报文的 TTL(0 - 255) */ + virtual void SetMultiCastTtl (int iMCTtl) = 0; + /* 获取组播报文的 TTL */ + virtual int GetMultiCastTtl () = 0; + + /* 设置是否启用组播环路(TRUE or FALSE) */ + virtual void SetMultiCastLoop (BOOL bMCLoop) = 0; + /* 检测是否启用组播环路 */ + virtual BOOL IsMultiCastLoop () = 0; + + /* 获取当前数据报的远程地址信息(通常在 OnReceive 事件中调用) */ + virtual BOOL GetRemoteAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) = 0; +}; + +/************************************************************************ +名称:UDP 节点组件接口 +描述:定义 UDP 节点组件的所有操作方法和属性访问方法 +************************************************************************/ +class IUdpNode +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:启动通信组件 + * 描述:启动 UDP 节点通信组件,启动完成后可开始收发数据 + * + * 参数: lpszBindAddress -- 绑定地址(默认:nullptr,绑定任意地址) + * usPort -- 本地端口(默认:0) + * enCastMode -- 传播模式(默认:CM_UNICAST) + * lpszCastAddress -- 传播地址(默认:nullptr,当 enCaseMode 为 CM_MULTICAST 或 CM_BROADCAST 时有效) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 GetLastError() 获取错误代码 + */ + virtual BOOL Start(LPCTSTR lpszBindAddress = nullptr, USHORT usPort = 0, EnCastMode enCastMode = CM_UNICAST, LPCTSTR lpszCastAddress = nullptr) = 0; + + /* + * 名称:关闭通信组件 + * 描述:关闭 UDP 节点通信组件,关闭完成后释放所有资源 + * + * 参数: + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 GetLastError() 获取错误代码 + */ + virtual BOOL Stop() = 0; + + /* + * 名称:发送数据 + * 描述:向指定地址发送数据 + * + * 参数: lpszRemoteAddress -- 远程地址 + * usRemotePort -- 远程端口 + * pBuffer -- 发送缓冲区 + * iLength -- 发送缓冲区长度 + * iOffset -- 发送缓冲区指针偏移量 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Send(LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength, int iOffset = 0) = 0; + + /* + * 名称:发送多组数据 + * 描述:向指定地址发送多组数据,把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) + * + * 参数: lpszRemoteAddress -- 远程地址 + * usRemotePort -- 远程端口 + * pBuffers -- 发送缓冲区数组 + * iCount -- 发送缓冲区数目 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL SendPackets(LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const WSABUF pBuffers[], int iCount) = 0; + + /* + * 名称:发送数据 + * 描述:向传播地址发送数据 + * + * 参数: pBuffer -- 发送缓冲区 + * iLength -- 发送缓冲区长度 + * iOffset -- 发送缓冲区指针偏移量 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL SendCast(const BYTE* pBuffer, int iLength, int iOffset = 0) = 0; + + /* + * 名称:发送多组数据 + * 描述:向传播地址发送多组数据,把所有数据包组合成一个数据包发送(数据包的总长度不能大于设置的 UDP 包最大长度) + * + * 参数: pBuffers -- 发送缓冲区数组 + * iCount -- 发送缓冲区数目 + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL SendCastPackets(const WSABUF pBuffers[], int iCount) = 0; + + /* + * 名称:等待 + * 描述:等待通信组件停止运行 + * + * 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Wait(DWORD dwMilliseconds = INFINITE) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置附加数据 */ + virtual void SetExtra (PVOID pExtra) = 0; + + /* 获取附加数据 */ + virtual PVOID GetExtra () = 0; + + /* 检查通信组件是否已启动 */ + virtual BOOL HasStarted () = 0; + /* 查看通信组件当前状态 */ + virtual EnServiceState GetState () = 0; + /* 获取最近一次失败操作的错误代码 */ + virtual EnSocketError GetLastError () = 0; + /* 获取最近一次失败操作的错误描述 */ + virtual LPCTSTR GetLastErrorDesc () = 0; + /* 获取本节点地址 */ + virtual BOOL GetLocalAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) = 0; + /* 获取本节点传播地址 */ + virtual BOOL GetCastAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort) = 0; + /* 获取传播模式 */ + virtual EnCastMode GetCastMode () = 0; + /* 获取未发出数据的长度 */ + virtual BOOL GetPendingDataLength (int& iPending) = 0; + + /* 设置数据报文最大长度(建议在局域网环境下不超过 1432 字节,在广域网环境下不超过 548 字节) */ + virtual void SetMaxDatagramSize (DWORD dwMaxDatagramSize) = 0; + /* 获取数据报文最大长度 */ + virtual DWORD GetMaxDatagramSize() = 0; + + /* 设置组播报文的 TTL(0 - 255) */ + virtual void SetMultiCastTtl (int iMCTtl) = 0; + /* 获取组播报文的 TTL */ + virtual int GetMultiCastTtl () = 0; + + /* 设置是否启用组播环路(TRUE or FALSE) */ + virtual void SetMultiCastLoop (BOOL bMCLoop) = 0; + /* 检测是否启用组播环路 */ + virtual BOOL IsMultiCastLoop () = 0; + + /* 设置地址重用选项 */ + virtual void SetReuseAddressPolicy(EnReuseAddressPolicy enReusePolicy) = 0; + /* 设置工作线程数量(通常设置为 2 * CPU + 2) */ + virtual void SetWorkerThreadCount (DWORD dwWorkerThreadCount) = 0; + /* 设置 Receive 预投递数量(根据负载调整设置,Receive 预投递数量越大则丢包概率越小) */ + virtual void SetPostReceiveCount (DWORD dwPostReceiveCount) = 0; + /* 设置内存块缓存池大小 */ + virtual void SetFreeBufferPoolSize (DWORD dwFreeBufferPoolSize) = 0; + /* 设置内存块缓存池回收阀值 */ + virtual void SetFreeBufferPoolHold (DWORD dwFreeBufferPoolHold) = 0; + + /* 获取地址重用选项 */ + virtual EnReuseAddressPolicy GetReuseAddressPolicy() = 0; + /* 获取工作线程数量 */ + virtual DWORD GetWorkerThreadCount () = 0; + /* 获取 Receive 预投递数量 */ + virtual DWORD GetPostReceiveCount () = 0; + /* 获取内存块缓存池大小 */ + virtual DWORD GetFreeBufferPoolSize () = 0; + /* 获取内存块缓存池回收阀值 */ + virtual DWORD GetFreeBufferPoolHold () = 0; + +public: + virtual ~IUdpNode() = default; +}; + +/************************************************************************ +名称:Client ARQ 模型组件接口 +描述:定义 Client 组件的 ARQ 模型组件的所有操作方法 +************************************************************************/ +class IArqClient +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置是否开启 nodelay 模式(默认:FALSE,不开启) */ + virtual void SetNoDelay (BOOL bNoDelay) = 0; + /* 设置是否关闭拥塞控制(默认:FALSE,不关闭) */ + virtual void SetTurnoffCongestCtrl (BOOL bTurnOff) = 0; + /* 设置数据刷新间隔(毫秒,默认:60) */ + virtual void SetFlushInterval (DWORD dwFlushInterval) = 0; + /* 设置快速重传 ACK 跨越次数(默认:0,关闭快速重传) */ + virtual void SetResendByAcks (DWORD dwResendByAcks) = 0; + /* 设置发送窗口大小(数据包数量,默认:128) */ + virtual void SetSendWndSize (DWORD dwSendWndSize) = 0; + /* 设置接收窗口大小(数据包数量,默认:512) */ + virtual void SetRecvWndSize (DWORD dwRecvWndSize) = 0; + /* 设置最小重传超时时间(毫秒,默认:30) */ + virtual void SetMinRto (DWORD dwMinRto) = 0; + /* 设置快速握手次数限制(默认:5,如果为 0 则不限制) */ + virtual void SetFastLimit (DWORD dwFastLimit) = 0; + /* 设置最大传输单元(默认:0,与 SetMaxDatagramSize() 一致) */ + virtual void SetMaxTransUnit (DWORD dwMaxTransUnit) = 0; + /* 设置最大数据包大小(默认:4096) */ + virtual void SetMaxMessageSize (DWORD dwMaxMessageSize) = 0; + /* 设置握手超时时间(毫秒,默认:5000) */ + virtual void SetHandShakeTimeout (DWORD dwHandShakeTimeout) = 0; + + /* 检测是否开启 nodelay 模式 */ + virtual BOOL IsNoDelay () = 0; + /* 检测是否关闭拥塞控制 */ + virtual BOOL IsTurnoffCongestCtrl () = 0; + /* 获取数据刷新间隔 */ + virtual DWORD GetFlushInterval () = 0; + /* 获取快速重传 ACK 跨越次数 */ + virtual DWORD GetResendByAcks () = 0; + /* 获取发送窗口大小 */ + virtual DWORD GetSendWndSize () = 0; + /* 获取接收窗口大小 */ + virtual DWORD GetRecvWndSize () = 0; + /* 获取最小重传超时时间 */ + virtual DWORD GetMinRto () = 0; + /* 获取快速握手次数限制 */ + virtual DWORD GetFastLimit () = 0; + /* 获取最大传输单元 */ + virtual DWORD GetMaxTransUnit () = 0; + /* 获取最大数据包大小 */ + virtual DWORD GetMaxMessageSize () = 0; + /* 获取握手超时时间 */ + virtual DWORD GetHandShakeTimeout () = 0; + + /* 获取等待发送包数量 */ + virtual BOOL GetWaitingSendMessageCount (int& iCount) = 0; + +public: + virtual ~IArqClient() = default; +}; + +/************************************************************************ +名称:UDP ARQ 通信客户端组件接口 +描述:继承了 ARQ 和 Client 接口 +************************************************************************/ +typedef DualInterface IUdpArqClient; + +#endif + +/************************************************************************ +名称:Server/Agent PULL 模型组件接口 +描述:定义 Server/Agent 组件的 PULL 模型组件的所有操作方法 +************************************************************************/ +class IPullSocket +{ +public: + + /* + * 名称:抓取数据 + * 描述:用户通过该方法从 Socket 组件中抓取数据 + * + * 参数: dwConnID -- 连接 ID + * pData -- 抓取缓冲区 + * iLength -- 抓取数据长度 + * 返回值: EnFetchResult + */ + virtual EnFetchResult Fetch (CONNID dwConnID, BYTE* pData, int iLength) = 0; + + /* + * 名称:窥探数据(不会移除缓冲区数据) + * 描述:用户通过该方法从 Socket 组件中窥探数据 + * + * 参数: dwConnID -- 连接 ID + * pData -- 窥探缓冲区 + * iLength -- 窥探数据长度 + * 返回值: EnFetchResult + */ + virtual EnFetchResult Peek (CONNID dwConnID, BYTE* pData, int iLength) = 0; + +public: + virtual ~IPullSocket() = default; +}; + +/************************************************************************ +名称:Client PULL 模型组件接口 +描述:定义 Client 组件的 PULL 模型组件的所有操作方法 +************************************************************************/ +class IPullClient +{ +public: + + /* + * 名称:抓取数据 + * 描述:用户通过该方法从 Socket 组件中抓取数据 + * + * 参数: pData -- 抓取缓冲区 + * iLength -- 抓取数据长度 + * 返回值: EnFetchResult + */ + virtual EnFetchResult Fetch (BYTE* pData, int iLength) = 0; + + /* + * 名称:窥探数据(不会移除缓冲区数据) + * 描述:用户通过该方法从 Socket 组件中窥探数据 + * + * 参数: pData -- 窥探缓冲区 + * iLength -- 窥探数据长度 + * 返回值: EnFetchResult + */ + virtual EnFetchResult Peek (BYTE* pData, int iLength) = 0; + +public: + virtual ~IPullClient() = default; +}; + +/************************************************************************ +名称:TCP PULL 模型组件接口 +描述:继承了 PULL 和 Socket 接口 +************************************************************************/ +typedef DualInterface ITcpPullServer; +typedef DualInterface ITcpPullAgent; +typedef DualInterface ITcpPullClient; + +/************************************************************************ +名称:Server/Agent PACK 模型组件接口 +描述:定义 Server/Agent 组件的 PACK 模型组件的所有操作方法 +************************************************************************/ +class IPackSocket +{ +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置数据包最大长度(有效数据包最大长度不能超过 4194303/0x3FFFFF 字节,默认:262144/0x40000) */ + virtual void SetMaxPackSize (DWORD dwMaxPackSize) = 0; + /* 设置包头标识(有效包头标识取值范围 0 ~ 1023/0x3FF,当包头标识为 0 时不校验包头,默认:0) */ + virtual void SetPackHeaderFlag (USHORT usPackHeaderFlag) = 0; + + /* 获取数据包最大长度 */ + virtual DWORD GetMaxPackSize () = 0; + /* 获取包头标识 */ + virtual USHORT GetPackHeaderFlag() = 0; + +public: + virtual ~IPackSocket() = default; +}; + +/************************************************************************ +名称:Client PACK 模型组件接口 +描述:定义 Client 组件的 PACK 模型组件的所有操作方法 +************************************************************************/ +class IPackClient +{ +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置数据包最大长度(有效数据包最大长度不能超过 4194303/0x3FFFFF 字节,默认:262144/0x40000) */ + virtual void SetMaxPackSize (DWORD dwMaxPackSize) = 0; + /* 设置包头标识(有效包头标识取值范围 0 ~ 1023/0x3FF,当包头标识为 0 时不校验包头,默认:0) */ + virtual void SetPackHeaderFlag (USHORT usPackHeaderFlag) = 0; + + /* 获取数据包最大长度 */ + virtual DWORD GetMaxPackSize () = 0; + /* 获取包头标识 */ + virtual USHORT GetPackHeaderFlag() = 0; + +public: + virtual ~IPackClient() = default; +}; + +/************************************************************************ +名称:TCP PACK 模型组件接口 +描述:继承了 PACK 和 Socket 接口 +************************************************************************/ +typedef DualInterface ITcpPackServer; +typedef DualInterface ITcpPackAgent; +typedef DualInterface ITcpPackClient; + +/************************************************************************ +名称:Socket 监听器基接口 +描述:定义组件监听器的公共方法 +************************************************************************/ +template class ISocketListenerT +{ +public: + + /* + * 名称:握手完成通知 + * 描述:连接完成握手时,Socket 监听器将收到该通知,监听器接收到该通知后才能开始 + * 数据收发操作 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 引发 OnClose() 事件并关闭连接 + */ + virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID) = 0; + + /* + * 名称:已发送数据通知 + * 描述:成功发送数据后,Socket 监听器将收到该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * pData -- 已发送数据缓冲区 + * iLength -- 已发送数据长度 + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 该通知不允许返回 HR_ERROR(调试模式下引发断言错误) + */ + virtual EnHandleResult OnSend(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength) = 0; + + /* + * 名称:数据到达通知(PUSH 模型) + * 描述:对于 PUSH 模型的 Socket 通信组件,成功接收数据后将向 Socket 监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * pData -- 已接收数据缓冲区 + * iLength -- 已接收数据长度 + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 引发 OnClose() 事件并关闭连接 + */ + virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength) = 0; + + /* + * 名称:数据到达通知(PULL 模型) + * 描述:对于 PULL 模型的 Socket 通信组件,成功接收数据后将向 Socket 监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * iLength -- 已接收数据长度 + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 引发 OnClose() 事件并关闭连接 + */ + virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, int iLength) = 0; + + /* + * 名称:通信错误通知 + * 描述:通信发生错误后,Socket 监听器将收到该通知,并关闭连接 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * enOperation -- Socket 操作类型 + * iErrorCode -- 错误代码 + * 返回值: 忽略返回值 + */ + virtual EnHandleResult OnClose(T* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode) = 0; + +public: + virtual ~ISocketListenerT() = default; +}; + +template class IComplexSocketListenerT : public ISocketListenerT +{ +public: + + /* + * 名称:关闭通信组件通知 + * 描述:通信组件关闭时,Socket 监听器将收到该通知 + * + * 参数: pSender -- 事件源对象 + * 返回值: 忽略返回值 + */ + virtual EnHandleResult OnShutdown(T* pSender) = 0; + +}; + +/************************************************************************ +名称:服务端 Socket 监听器接口 +描述:定义服务端 Socket 监听器的所有事件 +************************************************************************/ +template class IServerListenerT : public IComplexSocketListenerT +{ +public: + + /* + * 名称:准备监听通知 + * 描述:通信服务端组件启动时,在监听 Socket 创建完成并开始执行监听前,Socket 监听 + * 器将收到该通知,监听器可以在通知处理方法中执行 Socket 选项设置等额外工作 + * + * 参数: pSender -- 事件源对象 + * soListen -- 监听 Socket + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 终止启动通信服务组件 + */ + virtual EnHandleResult OnPrepareListen(T* pSender, SOCKET soListen) = 0; + + /* + * 名称:接收连接通知 + * 描述:接收到客户端连接请求时,Socket 监听器将收到该通知,监听器可以在通知处理方 + * 法中执行 Socket 选项设置或拒绝客户端连接等额外工作 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * soClient -- TCP: 客户端 Socket 句柄,UDP: 客户端 Socket SOCKADDR 指针 + * 返回值: HR_OK / HR_IGNORE -- 接受连接 + * HR_ERROR -- 拒绝连接 + */ + virtual EnHandleResult OnAccept(T* pSender, CONNID dwConnID, UINT_PTR soClient) = 0; +}; + +/************************************************************************ +名称:TCP 服务端 Socket 监听器接口 +描述:定义 TCP 服务端 Socket 监听器的所有事件 +************************************************************************/ +class ITcpServerListener : public IServerListenerT +{ +public: + +}; + +/************************************************************************ +名称:PUSH 模型服务端 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CTcpServerListener : public ITcpServerListener +{ +public: + virtual EnHandleResult OnPrepareListen(ITcpServer* pSender, SOCKET soListen) override {return HR_IGNORE;} + virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(ITcpServer* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnShutdown(ITcpServer* pSender) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:PULL 模型服务端 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CTcpPullServerListener : public CTcpServerListener +{ +public: + virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, int iLength) override = 0; + virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} +}; + +#ifdef _UDP_SUPPORT + +/************************************************************************ +名称:UDP 服务端 Socket 监听器接口 +描述:定义 UDP 服务端 Socket 监听器的所有事件 +************************************************************************/ +class IUdpServerListener : public IServerListenerT +{ +public: + +}; + +/************************************************************************ +名称:UDP 服务端 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CUdpServerListener : public IUdpServerListener +{ +public: + virtual EnHandleResult OnPrepareListen(IUdpServer* pSender, SOCKET soListen) override {return HR_IGNORE;} + virtual EnHandleResult OnAccept(IUdpServer* pSender, CONNID dwConnID, UINT_PTR pSockAddr) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(IUdpServer* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(IUdpServer* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(IUdpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnShutdown(IUdpServer* pSender) override {return HR_IGNORE;} +}; + +#endif + +/************************************************************************ +名称:通信代理 Socket 监听器接口 +描述:定义 通信代理 Socket 监听器的所有事件 +************************************************************************/ +template class IAgentListenerT : public IComplexSocketListenerT +{ +public: + + /* + * 名称:准备连接通知 + * 描述:通信客户端组件启动时,在客户端 Socket 创建完成并开始执行连接前,Socket 监听 + * 器将收到该通知,监听器可以在通知处理方法中执行 Socket 选项设置等额外工作 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * socket -- 客户端 Socket + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 终止启动通信客户端组件 + */ + virtual EnHandleResult OnPrepareConnect(T* pSender, CONNID dwConnID, SOCKET socket) = 0; + + /* + * 名称:连接完成通知 + * 描述:与服务端成功建立连接时,Socket 监听器将收到该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 同步连接:终止启动通信客户端组件 + * 异步连接:关闭连接 + */ + virtual EnHandleResult OnConnect(T* pSender, CONNID dwConnID) = 0; +}; + +/************************************************************************ +名称:TCP 通信代理 Socket 监听器接口 +描述:定义 TCP 通信代理 Socket 监听器的所有事件 +************************************************************************/ +class ITcpAgentListener : public IAgentListenerT +{ +public: + +}; + +/************************************************************************ +名称:PUSH 模型通信代理 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CTcpAgentListener : public ITcpAgentListener +{ +public: + virtual EnHandleResult OnPrepareConnect(ITcpAgent* pSender, CONNID dwConnID, SOCKET socket) override {return HR_IGNORE;} + virtual EnHandleResult OnConnect(ITcpAgent* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(ITcpAgent* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnShutdown(ITcpAgent* pSender) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:PULL 通信代理 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CTcpPullAgentListener : public CTcpAgentListener +{ +public: + virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, int iLength) override = 0; + virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:客户端 Socket 监听器接口 +描述:定义客户端 Socket 监听器的所有事件 +************************************************************************/ + +template class IClientListenerT : public ISocketListenerT +{ +public: + + /* + * 名称:准备连接通知 + * 描述:通信客户端组件启动时,在客户端 Socket 创建完成并开始执行连接前,Socket 监听 + * 器将收到该通知,监听器可以在通知处理方法中执行 Socket 选项设置等额外工作 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * socket -- 客户端 Socket + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 终止启动通信客户端组件 + */ + virtual EnHandleResult OnPrepareConnect(T* pSender, CONNID dwConnID, SOCKET socket) = 0; + + /* + * 名称:连接完成通知 + * 描述:与服务端成功建立连接时,Socket 监听器将收到该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 同步连接:终止启动通信客户端组件 + * 异步连接:关闭连接 + */ + virtual EnHandleResult OnConnect(T* pSender, CONNID dwConnID) = 0; +}; + +/************************************************************************ +名称:TCP 客户端 Socket 监听器接口 +描述:定义 TCP 客户端 Socket 监听器的所有事件 +************************************************************************/ +class ITcpClientListener : public IClientListenerT +{ +public: + +}; + +/************************************************************************ +名称:PUSH 模型客户端 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CTcpClientListener : public ITcpClientListener +{ +public: + virtual EnHandleResult OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket) override {return HR_IGNORE;} + virtual EnHandleResult OnConnect(ITcpClient* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(ITcpClient* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:PULL 客户端 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CTcpPullClientListener : public CTcpClientListener +{ +public: + virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, int iLength) = 0; + virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) {return HR_IGNORE;} +}; + +#ifdef _UDP_SUPPORT + +/************************************************************************ +名称:UDP 客户端 Socket 监听器接口 +描述:定义 UDP 客户端 Socket 监听器的所有事件 +************************************************************************/ +class IUdpClientListener : public IClientListenerT +{ +public: + +}; + +/************************************************************************ +名称:UDP 户端 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CUdpClientListener : public IUdpClientListener +{ +public: + virtual EnHandleResult OnPrepareConnect(IUdpClient* pSender, CONNID dwConnID, SOCKET socket) override {return HR_IGNORE;} + virtual EnHandleResult OnConnect(IUdpClient* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(IUdpClient* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(IUdpClient* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(IUdpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:UDP 传播 Socket 监听器接口 +描述:定义 UDP 传播 Socket 监听器的所有事件 +************************************************************************/ +class IUdpCastListener : public IClientListenerT +{ +public: + +}; + +/************************************************************************ +名称:UDP 传播 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CUdpCastListener : public IUdpCastListener +{ +public: + virtual EnHandleResult OnPrepareConnect(IUdpCast* pSender, CONNID dwConnID, SOCKET socket) override {return HR_IGNORE;} + virtual EnHandleResult OnConnect(IUdpCast* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(IUdpCast* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(IUdpCast* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(IUdpCast* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:UDP 节点 Socket 监听器接口 +描述:定义 UDP 节点 Socket 监听器的所有事件 +************************************************************************/ +class IUdpNodeListener +{ +public: + + /* + * 名称:准备监听通知 + * 描述:通信组件启动时,在监听 Socket 创建完成并开始执行监听前,Socket 监听器 + * 将收到该通知,监听器可以在通知处理方法中执行 Socket 选项设置等额外工作 + * + * 参数: pSender -- 事件源对象 + * soListen -- 监听 Socket + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 终止启动通信服务组件 + */ + virtual EnHandleResult OnPrepareListen(IUdpNode* pSender, SOCKET soListen) = 0; + + /* + * 名称:已发送数据通知 + * 描述:成功发送数据后,Socket 监听器将收到该通知 + * + * 参数: pSender -- 事件源对象 + * lpszRemoteAddress -- 远程地址 + * usRemotePort -- 远程端口 + * pData -- 已发送数据缓冲区 + * iLength -- 已发送数据长度 + * 返回值: 忽略返回值 + */ + virtual EnHandleResult OnSend(IUdpNode* pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength) = 0; + + /* + * 名称:数据到达通知(PUSH 模型) + * 描述:成功接收数据后,Socket 监听器将收到该通知 + * + * 参数: pSender -- 事件源对象 + * lpszRemoteAddress -- 远程地址 + * usRemotePort -- 远程端口 + * pData -- 已发送数据缓冲区 + * iLength -- 已发送数据长度 + * 返回值: 忽略返回值 + */ + virtual EnHandleResult OnReceive(IUdpNode* pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength) = 0; + + /* + * 名称:通信错误通知 + * 描述:通信发生错误后,Socket 监听器将收到该通知 + * + * 参数: pSender -- 事件源对象 + * lpszRemoteAddress -- 远程地址 + * usRemotePort -- 远程端口 + * enOperation -- Socket 操作类型 + * iErrorCode -- 错误代码 + * pData -- 本次事件关联的数据缓冲区 + * iLength -- 本次事件关联的数据长度 + * 返回值: 忽略返回值 + */ + virtual EnHandleResult OnError(IUdpNode* pSender, EnSocketOperation enOperation, int iErrorCode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength) = 0; + + /* + * 名称:关闭通信组件通知 + * 描述:通信组件关闭时,Socket 监听器将收到该通知 + * + * 参数: pSender -- 事件源对象 + * 返回值: 忽略返回值 + */ + virtual EnHandleResult OnShutdown(IUdpNode* pSender) = 0; + +public: + virtual ~IUdpNodeListener() = default; +}; + +/************************************************************************ +名称:UDP 节点 Socket 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CUdpNodeListener : public IUdpNodeListener +{ +public: + virtual EnHandleResult OnPrepareListen(IUdpNode* pSender, SOCKET soListen) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(IUdpNode* pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnShutdown(IUdpNode* pSender) override {return HR_IGNORE;} +}; + +#endif + +/*****************************************************************************************************************************************************/ +/****************************************************************** HTTP Interfaces ******************************************************************/ +/*****************************************************************************************************************************************************/ + +#ifdef _HTTP_SUPPORT + +/************************************************************************ +名称:复合 Http 组件接口 +描述:定义复合 Http 组件的所有操作方法和属性访问方法,复合 Http 组件同时管理多个 Http 连接 +************************************************************************/ +class IComplexHttp +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:启动 HTTP 通信 + * 描述:当通信组件设置为非自动启动 HTTP 通信时,需要调用本方法启动 HTTP 通信 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL StartHttp(CONNID dwConnID) = 0; + + /* + * 名称:发送 Chunked 数据分片 + * 描述:向对端发送 Chunked 数据分片 + * + * 参数: dwConnID -- 连接 ID + * pData -- Chunked 数据分片 + * iLength -- 数据分片长度(为 0 表示结束分片) + * lpszExtensions -- 扩展属性(默认:nullptr) + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendChunkData(CONNID dwConnID, const BYTE* pData = nullptr, int iLength = 0, LPCSTR lpszExtensions = nullptr) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置本地协议版本 */ + virtual void SetLocalVersion(EnHttpVersion usVersion) = 0; + /* 获取本地协议版本 */ + virtual EnHttpVersion GetLocalVersion() = 0; + + /* 检查是否升级协议 */ + virtual BOOL IsUpgrade(CONNID dwConnID) = 0; + /* 检查是否有 Keep-Alive 标识 */ + virtual BOOL IsKeepAlive(CONNID dwConnID) = 0; + /* 获取协议版本 */ + virtual USHORT GetVersion(CONNID dwConnID) = 0; + /* 获取内容长度 */ + virtual ULONGLONG GetContentLength(CONNID dwConnID) = 0; + /* 获取内容类型 */ + virtual LPCSTR GetContentType(CONNID dwConnID) = 0; + /* 获取内容编码 */ + virtual LPCSTR GetContentEncoding(CONNID dwConnID) = 0; + /* 获取传输编码 */ + virtual LPCSTR GetTransferEncoding(CONNID dwConnID) = 0; + /* 获取协议升级类型 */ + virtual EnHttpUpgradeType GetUpgradeType(CONNID dwConnID) = 0; + /* 获取解析错误代码 */ + virtual USHORT GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc = nullptr) = 0; + + /* 获取某个请求头(单值) */ + virtual BOOL GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) = 0; + /* 获取某个请求头(多值) */ + virtual BOOL GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount) = 0; + /* 获取所有请求头 */ + virtual BOOL GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount) = 0; + /* 获取所有请求头名称 */ + virtual BOOL GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount) = 0; + + /* 获取 Cookie */ + virtual BOOL GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) = 0; + /* 获取所有 Cookie */ + virtual BOOL GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount) = 0; + + /* + // !! maybe implemented in future !! // + + virtual BOOL GetParam(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue) = 0; + virtual BOOL GetParams(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount) = 0; + virtual BOOL GetAllParams(CONNID dwConnID, LPPARAM lpszParam[], DWORD& dwCount) = 0; + virtual BOOL GetAllParamNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount) = 0; + */ + + /* 获取当前 WebSocket 消息状态,传入 nullptr 则不获取相应字段 */ + virtual BOOL GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain) = 0; + + /* 设置 HTTP 启动方式(默认:TRUE,自动启动) */ + virtual void SetHttpAutoStart(BOOL bAutoStart) = 0; + /* 获取 HTTP 启动方式 */ + virtual BOOL IsHttpAutoStart() = 0; + +public: + virtual ~IComplexHttp() = default; +}; + +/************************************************************************ +名称:复合 Http 请求者组件接口 +描述:定义复合 Http 请求者组件的所有操作方法和属性访问方法 +************************************************************************/ +class IComplexHttpRequester : public IComplexHttp +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:发送 WebSocket 消息 + * 描述:向对端端发送 WebSocket 消息 + * + * 参数: dwConnID -- 连接 ID + * bFinal -- 是否结束帧 + * iReserved -- RSV1/RSV2/RSV3 各 1 位 + * iOperationCode -- 操作码:0x0 - 0xF + * lpszMask -- 掩码(nullptr 或 4 字节掩码,如果为 nullptr 则没有掩码) + * pData -- 消息体数据缓冲区 + * iLength -- 消息体数据长度 + * ullBodyLen -- 消息总长度 + * ullBodyLen = 0 -> 消息总长度为 iLength + * ullBodyLen = iLength -> 消息总长度为 ullBodyLen + * ullBodyLen > iLength -> 消息总长度为 ullBodyLen,后续消息体长度为 ullBOdyLen - iLength,后续消息体通过底层方法 Send() / SendPackets() 发送 + * ullBodyLen < iLength -> 错误参数,发送失败 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + 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) = 0; + + /* + * 名称:发送请求 + * 描述:向服务端发送 HTTP 请求 + * + * 参数: dwConnID -- 连接 ID + * lpszMethod -- 请求方法 + * lpszPath -- 请求路径 + * lpHeaders -- 请求头 + * iHeaderCount -- 请求头数量 + * pBody -- 请求体 + * iLength -- 请求体长度 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendRequest(CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0) = 0; + + /* + * 名称:发送本地文件 + * 描述:向指定连接发送 4096 KB 以下的小文件 + * + * 参数: dwConnID -- 连接 ID + * lpszFileName -- 文件路径 + * lpszMethod -- 请求方法 + * lpszPath -- 请求路径 + * lpHeaders -- 请求头 + * iHeaderCount -- 请求头数量 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + + /* 发送 POST 请求 */ + virtual BOOL SendPost(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) = 0; + /* 发送 PUT 请求 */ + virtual BOOL SendPut(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) = 0; + /* 发送 PATCH 请求 */ + virtual BOOL SendPatch(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) = 0; + /* 发送 GET 请求 */ + virtual BOOL SendGet(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 DELETE 请求 */ + virtual BOOL SendDelete(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 HEAD 请求 */ + virtual BOOL SendHead(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 TRACE 请求 */ + virtual BOOL SendTrace(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 OPTIONS 请求 */ + virtual BOOL SendOptions(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 CONNECT 请求 */ + virtual BOOL SendConnect(CONNID dwConnID, LPCSTR lpszHost, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 获取 HTTP 状态码 */ + virtual USHORT GetStatusCode(CONNID dwConnID) = 0; + + /* 设置是否使用 Cookie(默认:TRUE) */ + virtual void SetUseCookie(BOOL bUseCookie) = 0; + /* 检查是否使用 Cookie */ + virtual BOOL IsUseCookie() = 0; +}; + +/************************************************************************ +名称:复合 Http 响应者组件接口 +描述:定义复合 Http 响应者组件的所有操作方法和属性访问方法 +************************************************************************/ +class IComplexHttpResponder : public IComplexHttp +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:发送 WebSocket 消息 + * 描述:向对端端发送 WebSocket 消息 + * + * 参数: dwConnID -- 连接 ID + * bFinal -- 是否结束帧 + * iReserved -- RSV1/RSV2/RSV3 各 1 位 + * iOperationCode -- 操作码:0x0 - 0xF + * pData -- 消息体数据缓冲区 + * iLength -- 消息体数据长度 + * ullBodyLen -- 消息总长度 + * ullBodyLen = 0 -> 消息总长度为 iLength + * ullBodyLen = iLength -> 消息总长度为 ullBodyLen + * ullBodyLen > iLength -> 消息总长度为 ullBodyLen,后续消息体长度为 ullBOdyLen - iLength,后续消息体通过底层方法 Send() / SendPackets() 发送 + * ullBodyLen < iLength -> 错误参数,发送失败 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0) = 0; + + /* + * 名称:回复请求 + * 描述:向客户端回复 HTTP 请求 + * + * 参数: dwConnID -- 连接 ID + * usStatusCode -- HTTP 状态码 + * lpszDesc -- HTTP 状态描述 + * lpHeaders -- 回复请求头 + * iHeaderCount -- 回复请求头数量 + * pData -- 回复请求体 + * iLength -- 回复请求体长度 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendResponse(CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc = nullptr, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pData = nullptr, int iLength = 0) = 0; + + /* + * 名称:发送本地文件 + * 描述:向指定连接发送 4096 KB 以下的小文件 + * + * 参数: dwConnID -- 连接 ID + * lpszFileName -- 文件路径 + * usStatusCode -- HTTP 状态码 + * lpszDesc -- HTTP 状态描述 + * lpHeaders -- 回复请求头 + * iHeaderCount -- 回复请求头数量 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, USHORT usStatusCode = HSC_OK, LPCSTR lpszDesc = nullptr, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + + /* + * 名称:释放连接 + * 描述:把连接放入释放队列,等待某个时间(通过 SetReleaseDelay() 设置)关闭连接 + * + * 参数: dwConnID -- 连接 ID + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL Release(CONNID dwConnID) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 获取主机 */ + virtual LPCSTR GetHost(CONNID dwConnID) = 0; + + /* 设置连接释放延时(默认:3000 毫秒) */ + virtual void SetReleaseDelay(DWORD dwReleaseDelay) = 0; + /* 获取连接释放延时 */ + virtual DWORD GetReleaseDelay() = 0; + + /* 获取请求行 URL 域掩码(URL 域参考:EnHttpUrlField) */ + virtual USHORT GetUrlFieldSet(CONNID dwConnID) = 0; + /* 获取某个 URL 域值 */ + virtual LPCSTR GetUrlField(CONNID dwConnID, EnHttpUrlField enField) = 0; + /* 获取请求方法 */ + virtual LPCSTR GetMethod(CONNID dwConnID) = 0; +}; + +/************************************************************************ +名称:简单 HTTP 组件接口 +描述:定义 简单 HTTP 组件的所有操作方法和属性访问方法 +************************************************************************/ +class IHttp +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:发送 WebSocket 消息 + * 描述:向对端端发送 WebSocket 消息 + * + * 参数: bFinal -- 是否结束帧 + * iReserved -- RSV1/RSV2/RSV3 各 1 位 + * iOperationCode -- 操作码:0x0 - 0xF + * lpszMask -- 掩码(nullptr 或 4 字节掩码,如果为 nullptr 则没有掩码) + * pData -- 消息体数据缓冲区 + * iLength -- 消息体数据长度 + * ullBodyLen -- 消息总长度 + * ullBodyLen = 0 -> 消息总长度为 iLength + * ullBodyLen = iLength -> 消息总长度为 ullBodyLen + * ullBodyLen > iLength -> 消息总长度为 ullBodyLen,后续消息体长度为 ullBOdyLen - iLength,后续消息体通过底层方法 Send() / SendPackets() 发送 + * ullBodyLen < iLength -> 错误参数,发送失败 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0) = 0; + + /* + * 名称:启动 HTTP 通信 + * 描述:当通信组件设置为非自动启动 HTTP 通信时,需要调用本方法启动 HTTP 通信 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取失败原因 + */ + virtual BOOL StartHttp() = 0; + + /* + * 名称:发送 Chunked 数据分片 + * 描述:向对端发送 Chunked 数据分片 + * + * 参数: pData -- Chunked 数据分片 + * iLength -- 数据分片长度(为 0 表示结束分片) + * lpszExtensions -- 扩展属性(默认:nullptr) + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendChunkData(const BYTE* pData = nullptr, int iLength = 0, LPCSTR lpszExtensions = nullptr) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置本地协议版本 */ + virtual void SetLocalVersion(EnHttpVersion usVersion) = 0; + /* 获取本地协议版本 */ + virtual EnHttpVersion GetLocalVersion() = 0; + + /* 检查是否升级协议 */ + virtual BOOL IsUpgrade() = 0; + /* 检查是否有 Keep-Alive 标识 */ + virtual BOOL IsKeepAlive() = 0; + /* 获取协议版本 */ + virtual USHORT GetVersion() = 0; + /* 获取内容长度 */ + virtual ULONGLONG GetContentLength() = 0; + /* 获取内容类型 */ + virtual LPCSTR GetContentType() = 0; + /* 获取内容编码 */ + virtual LPCSTR GetContentEncoding() = 0; + /* 获取传输编码 */ + virtual LPCSTR GetTransferEncoding() = 0; + /* 获取协议升级类型 */ + virtual EnHttpUpgradeType GetUpgradeType() = 0; + /* 获取解析错误代码 */ + virtual USHORT GetParseErrorCode(LPCSTR* lpszErrorDesc = nullptr) = 0; + + /* 获取 HTTP 状态码 */ + virtual USHORT GetStatusCode() = 0; + + /* 获取某个请求头(单值) */ + virtual BOOL GetHeader(LPCSTR lpszName, LPCSTR* lpszValue) = 0; + /* 获取某个请求头(多值) */ + virtual BOOL GetHeaders(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount) = 0; + /* 获取所有请求头 */ + virtual BOOL GetAllHeaders(THeader lpHeaders[], DWORD& dwCount) = 0; + /* 获取所有请求头名称 */ + virtual BOOL GetAllHeaderNames(LPCSTR lpszName[], DWORD& dwCount) = 0; + + /* 获取 Cookie */ + virtual BOOL GetCookie(LPCSTR lpszName, LPCSTR* lpszValue) = 0; + /* 获取所有 Cookie */ + virtual BOOL GetAllCookies(TCookie lpCookies[], DWORD& dwCount) = 0; + + /* + // !! maybe implemented in future !! // + + virtual BOOL GetParam(LPCSTR lpszName, LPCSTR* lpszValue) = 0; + virtual BOOL GetParams(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount) = 0; + virtual BOOL GetAllParams(LPPARAM lpszParam[], DWORD& dwCount) = 0; + virtual BOOL GetAllParamNames(LPCSTR lpszName[], DWORD& dwCount) = 0; + */ + + /* 获取当前 WebSocket 消息状态,传入 nullptr 则不获取相应字段 */ + virtual BOOL GetWSMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain) = 0; + + /* 设置 HTTP 启动方式(默认:TRUE,自动启动) */ + virtual void SetHttpAutoStart(BOOL bAutoStart) = 0; + /* 获取 HTTP 启动方式 */ + virtual BOOL IsHttpAutoStart() = 0; + +public: + virtual ~IHttp() = default; +}; + +/************************************************************************ +名称:简单 Http 请求者组件接口 +描述:定义简单 Http 请求者组件的所有操作方法和属性访问方法 +************************************************************************/ +class IHttpRequester : public IHttp +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:发送请求 + * 描述:向服务端发送 HTTP 请求 + * + * 参数: lpszMethod -- 请求方法 + * lpszPath -- 请求路径 + * lpHeaders -- 请求头 + * iHeaderCount -- 请求头数量 + * pBody -- 请求体 + * iLength -- 请求体长度 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0) = 0; + + /* + * 名称:发送本地文件 + * 描述:向指定连接发送 4096 KB 以下的小文件 + * + * 参数: dwConnID -- 连接 ID + * lpszFileName -- 文件路径 + * lpszMethod -- 请求方法 + * lpszPath -- 请求路径 + * lpHeaders -- 请求头 + * iHeaderCount -- 请求头数量 + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL SendLocalFile(LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + + /* 发送 POST 请求 */ + virtual BOOL SendPost(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) = 0; + /* 发送 PUT 请求 */ + virtual BOOL SendPut(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) = 0; + /* 发送 PATCH 请求 */ + virtual BOOL SendPatch(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength) = 0; + /* 发送 GET 请求 */ + virtual BOOL SendGet(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 DELETE 请求 */ + virtual BOOL SendDelete(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 HEAD 请求 */ + virtual BOOL SendHead(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 TRACE 请求 */ + virtual BOOL SendTrace(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 OPTIONS 请求 */ + virtual BOOL SendOptions(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + /* 发送 CONNECT 请求 */ + virtual BOOL SendConnect(LPCSTR lpszHost, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置是否使用 Cookie(默认:TRUE) */ + virtual void SetUseCookie(BOOL bUseCookie) = 0; + /* 检查是否使用 Cookie */ + virtual BOOL IsUseCookie() = 0; +}; + +/************************************************************************ +名称:简单 Http 同步请求者组件接口 +描述:定义简单 Http 同步请求者组件的所有操作方法和属性访问方法 +************************************************************************/ +class IHttpSyncRequester : public IHttpRequester +{ +public: + + /* + * 名称:发送 URL 请求 + * 描述:向服务端发送 HTTP URL 请求 + * + * 参数: lpszMethod -- 请求方法 + * lpszUrl -- 请求 URL + * lpHeaders -- 请求头 + * iHeaderCount -- 请求头数量 + * pBody -- 请求体 + * iLength -- 请求体长度 + * bForceReconnect -- 强制重新连接(默认:FALSE,当请求 URL 的主机和端口与现有连接一致时,重用现有连接) + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL OpenUrl(LPCSTR lpszMethod, LPCSTR lpszUrl, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0, BOOL bForceReconnect = FALSE) = 0; + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:清除请求结果 + * 描述:清除上一次请求的响应头和响应体等结果信息(该方法会在每次发送请求前自动调用) + * + * 参数: + * 返回值: TRUE -- 成功 + * FALSE -- 失败 + */ + virtual BOOL CleanupRequestResult () = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 设置连接超时(毫秒,0:系统默认超时,默认:5000) */ + virtual void SetConnectTimeout (DWORD dwConnectTimeout) = 0; + /* 设置请求超时(毫秒,0:无限等待,默认:10000) */ + virtual void SetRequestTimeout (DWORD dwRequestTimeout) = 0; + + /* 获取连接超时 */ + virtual DWORD GetConnectTimeout () = 0; + /* 获取请求超时 */ + virtual DWORD GetRequestTimeout () = 0; + + /* 获取响应体 */ + virtual BOOL GetResponseBody (LPCBYTE* lpszBody, int* iLength) = 0; +}; + + +/************************************************************************ +名称:HTTP 组件接口 +描述:继承了 HTTP 和 Socket 接口 +************************************************************************/ +typedef DualInterface IHttpServer; +typedef DualInterface IHttpAgent; +typedef DualInterface IHttpClient; +typedef DualInterface IHttpSyncClient; + +/************************************************************************ +名称:IComplexHttp 组件监听器基接口 +描述:定义 IComplexHttp 组件监听器的所有事件 +************************************************************************/ +template class IHttpListenerT +{ +public: + + /* + * 名称:开始解析通知 + * 描述:开始解析 HTTP 报文时,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnMessageBegin(T* pSender, CONNID dwConnID) = 0; + + /* + * 名称:请求行解析完成通知(仅用于 HTTP 服务端) + * 描述:请求行解析完成后,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * lpszMethod -- 请求方法名 + * lpszUrl -- 请求行中的 URL 域 + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnRequestLine(T* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl) = 0; + + /* + * 名称:状态行解析完成通知(仅用于 HTTP 客户端) + * 描述:状态行解析完成后,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * usStatusCode -- HTTP 状态码 + * lpszDesc -- 状态描述 + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnStatusLine(T* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc) = 0; + + /* + * 名称:请求头通知 + * 描述:每当解析完成一个请求头后,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * lpszName -- 请求头名称 + * lpszValue -- 请求头值 + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnHeader(T* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue) = 0; + + /* + * 名称:请求头完成通知 + * 描述:解析完成所有请求头后,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * 返回值: HPR_OK -- 继续执行 + * HPR_SKIP_BODY -- 跳过当前请求的 HTTP BODY + * HPR_UPGRADE -- 升级协议 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnHeadersComplete(T* pSender, CONNID dwConnID) = 0; + + /* + * 名称:BODY 报文通知 + * 描述:每当接收到 HTTP BODY 报文,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * pData -- 数据缓冲区 + * iLength -- 数据长度 + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnBody(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength) = 0; + + /* + * 名称:Chunked 报文头通知 + * 描述:每当解析出一个 Chunked 报文头,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * iLength -- Chunked 报文体数据长度 + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnChunkHeader(T* pSender, CONNID dwConnID, int iLength) = 0; + + /* + * 名称:Chunked 报文结束通知 + * 描述:每当解析完一个 Chunked 报文,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnChunkComplete(T* pSender, CONNID dwConnID) = 0; + + /* + * 名称:完成解析通知 + * 描述:每当解析完成一个完整 HTTP 报文,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnParserError() 和 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnMessageComplete(T* pSender, CONNID dwConnID) = 0; + + /* + * 名称:升级协议通知 + * 描述:当需要升级协议时,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * enUpgradeType -- 协议类型 + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnUpgrade(T* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType) = 0; + + /* + * 名称:解析错误通知 + * 描述:当解析 HTTP 报文错误时,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * iErrorCode -- 错误代码 + * lpszErrorDesc -- 错误描述 + * 返回值: HPR_OK -- 继续执行 + * HPR_ERROR -- 引发 OnClose() 事件并关闭连接 + */ + virtual EnHttpParseResult OnParseError(T* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc) = 0; + + /* + * 名称:WebSocket 数据包头通知 + * 描述:当解析 WebSocket 数据包头时,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * bFinal -- 是否结束帧 + * iReserved -- RSV1/RSV2/RSV3 各 1 位 + * iOperationCode -- 操作码:0x0 - 0xF + * lpszMask -- 掩码(nullptr 或 4 字节掩码,如果为 nullptr 则没有掩码) + * ullBodyLen -- 消息体长度 + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 引发 OnClose() 事件并关闭连接 + */ + virtual EnHandleResult OnWSMessageHeader(T* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen) = 0; + + /* + * 名称:WebSocket 数据包体通知 + * 描述:当接收到 WebSocket 数据包体时,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * pData -- 消息体数据缓冲区 + * iLength -- 消息体数据长度 + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 引发 OnClose() 事件并关闭连接 + */ + virtual EnHandleResult OnWSMessageBody(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength) = 0; + + /* + * 名称:WebSocket 数据包完成通知 + * 描述:当完整接收一个 WebSocket 数据包时,向监听器发送该通知 + * + * 参数: pSender -- 事件源对象 + * dwConnID -- 连接 ID + * 返回值: HR_OK / HR_IGNORE -- 继续执行 + * HR_ERROR -- 引发 OnClose() 事件并关闭连接 + */ + virtual EnHandleResult OnWSMessageComplete(T* pSender, CONNID dwConnID) = 0; + +public: + virtual ~IHttpListenerT() = default; +}; + +/************************************************************************ +名称:IHttpServer 组件端监听器接口 +描述:定义 IHttpServer 监听器的所有事件 +************************************************************************/ +class IHttpServerListener : public IHttpListenerT, public ITcpServerListener +{ +public: + +}; + +/************************************************************************ +名称:IHttpAgent 组件端监听器接口 +描述:定义 IHttpAgent 监听器的所有事件 +************************************************************************/ +class IHttpAgentListener : public IHttpListenerT, public ITcpAgentListener +{ +public: + +}; + +/************************************************************************ +名称:IHttpClient 组件端监听器接口 +描述:定义 IHttpClient 监听器的所有事件 +************************************************************************/ +class IHttpClientListener : public IHttpListenerT, public ITcpClientListener +{ +public: + +}; + +/************************************************************************ +名称:IHttpServerListener 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CHttpServerListener : public IHttpServerListener +{ +public: + virtual EnHandleResult OnPrepareListen(ITcpServer* pSender, SOCKET soListen) override {return HR_IGNORE;} + virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(ITcpServer* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnShutdown(ITcpServer* pSender) override {return HR_IGNORE;} + + virtual EnHttpParseResult OnMessageBegin(IHttpServer* pSender, CONNID dwConnID) override {return HPR_OK;} + virtual EnHttpParseResult OnRequestLine(IHttpServer* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl) override {return HPR_OK;} + virtual EnHttpParseResult OnStatusLine(IHttpServer* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc) override {return HPR_OK;} + virtual EnHttpParseResult OnHeader(IHttpServer* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue) override {return HPR_OK;} + virtual EnHttpParseResult OnChunkHeader(IHttpServer* pSender, CONNID dwConnID, int iLength) override {return HPR_OK;} + virtual EnHttpParseResult OnChunkComplete(IHttpServer* pSender, CONNID dwConnID) override {return HPR_OK;} + virtual EnHttpParseResult OnUpgrade(IHttpServer* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType) override {return HPR_OK;} + + virtual EnHandleResult OnWSMessageHeader(IHttpServer* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen) override {return HR_IGNORE;} + virtual EnHandleResult OnWSMessageBody(IHttpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnWSMessageComplete(IHttpServer* pSender, CONNID dwConnID) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:IHttpAgentListener 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CHttpAgentListener : public IHttpAgentListener +{ +public: + virtual EnHandleResult OnPrepareConnect(ITcpAgent* pSender, CONNID dwConnID, SOCKET socket) override {return HR_IGNORE;} + virtual EnHandleResult OnConnect(ITcpAgent* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(ITcpAgent* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnShutdown(ITcpAgent* pSender) override {return HR_IGNORE;} + + virtual EnHttpParseResult OnMessageBegin(IHttpAgent* pSender, CONNID dwConnID) override {return HPR_OK;} + virtual EnHttpParseResult OnRequestLine(IHttpAgent* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl) override {return HPR_OK;} + virtual EnHttpParseResult OnStatusLine(IHttpAgent* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc) override {return HPR_OK;} + virtual EnHttpParseResult OnHeader(IHttpAgent* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue) override {return HPR_OK;} + virtual EnHttpParseResult OnChunkHeader(IHttpAgent* pSender, CONNID dwConnID, int iLength) override {return HPR_OK;} + virtual EnHttpParseResult OnChunkComplete(IHttpAgent* pSender, CONNID dwConnID) override {return HPR_OK;} + virtual EnHttpParseResult OnUpgrade(IHttpAgent* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType) override {return HPR_OK;} + + virtual EnHandleResult OnWSMessageHeader(IHttpAgent* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen) override {return HR_IGNORE;} + virtual EnHandleResult OnWSMessageBody(IHttpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnWSMessageComplete(IHttpAgent* pSender, CONNID dwConnID) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:IHttpClientListener 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ + +class CHttpClientListener : public IHttpClientListener +{ +public: + virtual EnHandleResult OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket) override {return HR_IGNORE;} + virtual EnHandleResult OnConnect(ITcpClient* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnHandShake(ITcpClient* pSender, CONNID dwConnID) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + + virtual EnHttpParseResult OnMessageBegin(IHttpClient* pSender, CONNID dwConnID) override {return HPR_OK;} + virtual EnHttpParseResult OnRequestLine(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl) override {return HPR_OK;} + virtual EnHttpParseResult OnStatusLine(IHttpClient* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc) override {return HPR_OK;} + virtual EnHttpParseResult OnHeader(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue) override {return HPR_OK;} + virtual EnHttpParseResult OnChunkHeader(IHttpClient* pSender, CONNID dwConnID, int iLength) override {return HPR_OK;} + virtual EnHttpParseResult OnChunkComplete(IHttpClient* pSender, CONNID dwConnID) override {return HPR_OK;} + virtual EnHttpParseResult OnUpgrade(IHttpClient* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType) override {return HPR_OK;} + + virtual EnHandleResult OnWSMessageHeader(IHttpClient* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen) override {return HR_IGNORE;} + virtual EnHandleResult OnWSMessageBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HR_IGNORE;} + virtual EnHandleResult OnWSMessageComplete(IHttpClient* pSender, CONNID dwConnID) override {return HR_IGNORE;} +}; + +/************************************************************************ +名称:IHttpClientListener 监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ + +class CHttpSyncClientListener : public CHttpClientListener +{ +public: + virtual EnHandleResult OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode) override {return HR_IGNORE;} + + virtual EnHttpParseResult OnHeadersComplete(IHttpClient* pSender, CONNID dwConnID) override {return HPR_OK;} + virtual EnHttpParseResult OnBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override {return HPR_OK;} + virtual EnHttpParseResult OnMessageComplete(IHttpClient* pSender, CONNID dwConnID) override {return HPR_OK;} + virtual EnHttpParseResult OnParseError(IHttpClient* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc) override {return HPR_OK;} + +}; + +#endif + +/*****************************************************************************************************************************************************/ +/************************************************************** Thread Pool Interfaces ***************************************************************/ +/*****************************************************************************************************************************************************/ + +/************************************************************************ +名称:线程池组件接口 +描述:定义线程池组件的所有操作方法和属性访问方法 +************************************************************************/ +class IHPThreadPool +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:启动线程池组件 + * 描述: + * + * 参数: dwThreadCount -- 线程数量,(默认:0) + * >0 -> dwThreadCount + * =0 -> (CPU核数 * 2 + 2) + * <0 -> (CPU核数 * (-dwThreadCount)) + * dwMaxQueueSize -- 任务队列最大容量(默认:0,不限制) + * enRejectedPolicy -- 任务拒绝处理策略 + * TRP_CALL_FAIL(默认) :立刻返回失败 + * TRP_WAIT_FOR :等待(直到成功、超时或线程池关闭等原因导致失败) + * TRP_CALLER_RUN :调用者线程直接执行 + * dwStackSize -- 线程堆栈空间大小(默认:0 -> 操作系统默认) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Start (DWORD dwThreadCount = 0, DWORD dwMaxQueueSize = 0, EnRejectedPolicy enRejectedPolicy = TRP_CALL_FAIL, DWORD dwStackSize = 0) = 0; + + /* + * 名称:关闭线程池组件 + * 描述:在规定时间内关闭线程池组件,如果工作线程在最大等待时间内未能正常关闭,会尝试强制关闭,这种情况下很可能会造成系统资源泄漏 + * + * 参数: dwMaxWait -- 最大等待时间(毫秒,默认:INFINITE,一直等待) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Stop (DWORD dwMaxWait = INFINITE) = 0; + + /* + * 名称:提交任务 + * 描述:向线程池提交异步任务 + * + * 参数: fnTaskProc -- 任务处理函数 + * pvArg -- 任务参数 + * dwMaxWait -- 任务提交最大等待时间(仅对 TRP_WAIT_FOR 类型线程池生效,默认:INFINITE,一直等待) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + * 其中,错误码 ERROR_DESTINATION_ELEMENT_FULL 表示任务队列已满 + */ + virtual BOOL Submit (Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait = INFINITE) = 0; + + /* + * 名称:提交 Socket 任务 + * 描述:向线程池提交异步 Socket 任务 + * + * 参数: pTask -- 任务参数 + * dwMaxWait -- 任务提交最大等待时间(仅对 TRP_WAIT_FOR 类型线程池生效,默认:INFINITE,一直等待) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + * 其中,错误码 ERROR_DESTINATION_ELEMENT_FULL 表示任务队列已满 + * 注意:如果提交失败,需要手工调用 Destroy_HP_SocketTaskObj() 销毁 TSocketTask 对象 + */ + virtual BOOL Submit (LPTSocketTask pTask, DWORD dwMaxWait = INFINITE) = 0; + + /* + * 名称:调整线程池大小 + * 描述:增加或减少线程池的工作线程数量 + * + * 参数: dwNewThreadCount -- 线程数量 + * >0 -> dwNewThreadCount + * =0 -> (CPU核数 * 2 + 2) + * <0 -> (CPU核数 * (-dwNewThreadCount)) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL AdjustThreadCount(DWORD dwNewThreadCount) = 0; + + /* + * 名称:等待 + * 描述:等待线程池组件停止运行 + * + * 参数: dwMilliseconds -- 超时时间(毫秒,默认:-1,永不超时) + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Wait(DWORD dwMilliseconds = INFINITE) = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 检查线程池组件是否已启动 */ + virtual BOOL HasStarted () = 0; + /* 查看线程池组件当前状态 */ + virtual EnServiceState GetState () = 0; + /* 获取当前任务等待队列大小 */ + virtual DWORD GetQueueSize () = 0; + /* 获取当前正在执行的任务数量 */ + virtual DWORD GetTaskCount () = 0; + /* 获取工作线程数量 */ + virtual DWORD GetThreadCount () = 0; + /* 获取任务队列最大容量 */ + virtual DWORD GetMaxQueueSize () = 0; + /* 获取任务拒绝处理策略 */ + virtual EnRejectedPolicy GetRejectedPolicy () = 0; + +public: + virtual ~IHPThreadPool() = default; +}; + +/************************************************************************ +名称:线程池监听器接口 +描述:定义线程池监听器的所有事件 +************************************************************************/ +class IHPThreadPoolListener +{ +public: + + /* + * 名称:线程池启动通知 + * 描述:线程池启动时监听器将收到该通知,监听器可以在通知处理方法中执行预处理工作 + * + * 参数: pThreadPool -- 线程池对象 + * 返回值: 无 + */ + virtual void OnStartup(IHPThreadPool* pThreadPool) = 0; + + /* + * 名称:线程池启动关闭通知 + * 描述:线程池关闭时监听器将收到该通知,监听器可以在通知处理方法中执行后处理工作 + * + * 参数: pThreadPool -- 线程池对象 + * 返回值: 无 + */ + virtual void OnShutdown(IHPThreadPool* pThreadPool) = 0; + + /* + * 名称:工作线程启动通知 + * 描述:工作线程启动时监听器将收到该通知,监听器可以在通知处理方法中执行线程级别预处理工作 + * + * 参数: pThreadPool -- 线程池对象 + * dwThreadID -- 工作线程 ID + * 返回值: 无 + */ + virtual void OnWorkerThreadStart(IHPThreadPool* pThreadPool, THR_ID dwThreadID) = 0; + + /* + * 名称:工作线程退出通知 + * 描述:工作线程退出时监听器将收到该通知,监听器可以在通知处理方法中执行线程级别后处理工作 + * + * 参数: pThreadPool -- 线程池对象 + * dwThreadID -- 工作线程 ID + * 返回值: 无 + */ + virtual void OnWorkerThreadEnd(IHPThreadPool* pThreadPool, THR_ID dwThreadID) = 0; + +public: + virtual ~IHPThreadPoolListener() {}; +}; + +/************************************************************************ +名称:线程池监听器抽象基类 +描述:定义某些事件的默认处理方法(忽略事件) +************************************************************************/ +class CHPThreadPoolListener : public IHPThreadPoolListener +{ +public: + virtual void OnStartup(IHPThreadPool* pThreadPool) override {} + virtual void OnShutdown(IHPThreadPool* pThreadPool) override {} + virtual void OnWorkerThreadStart(IHPThreadPool* pThreadPool, THR_ID dwThreadID) override {} + virtual void OnWorkerThreadEnd(IHPThreadPool* pThreadPool, THR_ID dwThreadID) override {} +}; + +/************************************************************************ +名称:压缩器接口 +描述:定义压缩器的所有操作方法和属性访问方法 +************************************************************************/ +class IHPCompressor +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:执行压缩 + * 描述:可循环调用以压缩流式或分段数据 + * + * 参数: pData -- 待压缩数据缓冲区 + * iLength -- 待压缩数据长度 + * bLast -- 是否最后一段待压缩数据 + * pContext -- 压缩回调函数 Fn_CompressDataCallback 的上下文参数 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext = nullptr) = 0; + + /* + * 名称:执行压缩 + * 描述:可循环调用以压缩流式或分段数据 + * + * 参数: pData -- 待压缩数据缓冲区 + * iLength -- 待压缩数据长度 + * bLast -- 是否最后一段待压缩数据 + * bFlush -- 是否强制刷新(强制刷新会降低压缩效率,但可对数据进行分段压缩) + * pContext -- 压缩回调函数 Fn_CompressDataCallback 的上下文参数 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush = FALSE, PVOID pContext = nullptr) = 0; + + /* 重置压缩器 */ + virtual BOOL Reset() = 0; + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 检测压缩器是否可用 */ + virtual BOOL IsValid() = 0; + +public: + virtual ~IHPCompressor() = default; +}; + +/************************************************************************ +名称:解压器接口 +描述:定义解压器的所有操作方法和属性访问方法 +************************************************************************/ +class IHPDecompressor +{ +public: + + /***********************************************************************/ + /***************************** 组件操作方法 *****************************/ + + /* + * 名称:执行解压 + * 描述:可循环调用以解压流式或分段数据 + * + * 参数: pData -- 待解压数据缓冲区 + * iLength -- 待解压数据长度 + * pContext -- 解压回调函数 Fn_DecompressDataCallback 的上下文参数 + * + * 返回值: TRUE -- 成功 + * FALSE -- 失败,可通过 SYS_GetLastError() 获取错误代码 + */ + virtual BOOL Process(const BYTE* pData, int iLength, PVOID pContext = nullptr) = 0; + + /* 重置解压器 */ + virtual BOOL Reset() = 0; + +public: + + /***********************************************************************/ + /***************************** 属性访问方法 *****************************/ + + /* 检测解压器是否可用 */ + virtual BOOL IsValid() = 0; + +public: + virtual ~IHPDecompressor() = default; +}; diff --git a/libbrotli.a b/libbrotli.a new file mode 100644 index 0000000..5a6edaa Binary files /dev/null and b/libbrotli.a differ diff --git a/libbrotli.lib b/libbrotli.lib new file mode 100644 index 0000000..5a6edaa Binary files /dev/null and b/libbrotli.lib differ