From be46fb9db8ed0aaf82114994c91c947c511bff8a Mon Sep 17 00:00:00 2001 From: Colin Date: Fri, 24 Oct 2025 12:41:49 +0000 Subject: [PATCH] init. --- .gitignore | 1 + CMakeLists.txt | 61 + README_zh.md | 159 + client/client.cpp | 116 + client/client.vcxproj | 280 + client/client.vcxproj.filters | 138 + client/client.vcxproj.user | 37 + global/Crypto.cpp | 2375 +++++++ global/Crypto.h | 258 + global/helper.cpp | 2476 +++++++ global/helper.h | 604 ++ include/hpsocket/GlobalDef.h | 278 + include/hpsocket/GlobalErrno.h | 252 + include/hpsocket/HPSocket-SSL.h | 338 + include/hpsocket/HPSocket.h | 818 +++ include/hpsocket/HPTypeDef.h | 600 ++ include/hpsocket/SocketInterface.h | 3123 +++++++++ server/server.cpp | 165 + src/ArqHelper.cpp | 40 + src/ArqHelper.h | 791 +++ src/HPSocket-SSL.cpp | 200 + src/HPSocket.cpp | 749 ++ src/HPThreadPool.cpp | 496 ++ src/HPThreadPool.h | 168 + src/HttpAgent.cpp | 479 ++ src/HttpAgent.h | 216 + src/HttpClient.cpp | 598 ++ src/HttpClient.h | 397 ++ src/HttpCookie.cpp | 927 +++ src/HttpCookie.h | 194 + src/HttpHelper.cpp | 469 ++ src/HttpHelper.h | 1387 ++++ src/HttpServer.cpp | 611 ++ src/HttpServer.h | 223 + src/InternalDef.h | 132 + src/MiscHelper.cpp | 51 + src/MiscHelper.h | 157 + src/SSLAgent.cpp | 229 + src/SSLAgent.h | 107 + src/SSLClient.cpp | 150 + src/SSLClient.h | 105 + src/SSLHelper.cpp | 1200 ++++ src/SSLHelper.h | 500 ++ src/SSLServer.cpp | 229 + src/SSLServer.h | 115 + src/SocketHelper.cpp | 1676 +++++ src/SocketHelper.h | 1053 +++ src/SocketObject4C.h | 770 ++ src/TcpAgent.cpp | 1277 ++++ src/TcpAgent.h | 309 + src/TcpClient.cpp | 717 ++ src/TcpClient.h | 248 + src/TcpPackAgent.cpp | 24 + src/TcpPackAgent.h | 221 + src/TcpPackClient.cpp | 24 + src/TcpPackClient.h | 126 + src/TcpPackServer.cpp | 24 + src/TcpPackServer.h | 221 + src/TcpPullAgent.cpp | 24 + src/TcpPullAgent.h | 173 + src/TcpPullClient.cpp | 24 + src/TcpPullClient.h | 89 + src/TcpPullServer.cpp | 24 + src/TcpPullServer.h | 173 + src/TcpServer.cpp | 1164 +++ src/TcpServer.h | 313 + src/UdpArqClient.cpp | 186 + src/UdpArqClient.h | 114 + src/UdpArqServer.cpp | 253 + src/UdpArqServer.h | 120 + src/UdpCast.cpp | 696 ++ src/UdpCast.h | 219 + src/UdpClient.cpp | 824 +++ src/UdpClient.h | 234 + src/UdpNode.cpp | 758 ++ src/UdpNode.h | 202 + src/UdpServer.cpp | 1198 ++++ src/UdpServer.h | 308 + src/common/BufferPool.cpp | 298 + src/common/BufferPool.h | 869 +++ src/common/BufferPtr.h | 198 + src/common/CriSec.h | 291 + src/common/Event.cpp | 24 + src/common/Event.h | 530 ++ src/common/FileHelper.cpp | 237 + src/common/FileHelper.h | 123 + src/common/FuncHelper.cpp | 393 ++ src/common/FuncHelper.h | 426 ++ src/common/GeneralHelper.h | 40 + src/common/IODispatcher.cpp | 350 + src/common/IODispatcher.h | 272 + src/common/PollHelper.cpp | 62 + src/common/PollHelper.h | 51 + src/common/PrivateHeap.h | 152 + src/common/RWLock.cpp | 228 + src/common/RWLock.h | 128 + src/common/RingBuffer.h | 1727 +++++ src/common/STLHelper.h | 1068 +++ src/common/Semaphore.h | 117 + src/common/SignalHandler.h | 183 + src/common/Singleton.h | 117 + src/common/StringT.h | 930 +++ src/common/SysHelper.cpp | 66 + src/common/SysHelper.h | 186 + src/common/Thread.cpp | 50 + src/common/Thread.h | 612 ++ src/common/http/Readme.txt | 14 + src/common/http/llhttp.h | 907 +++ src/common/http/llhttp_api.c | 519 ++ src/common/http/llhttp_internal.c | 10111 +++++++++++++++++++++++++++ src/common/http/llhttp_support.c | 170 + src/common/http/llhttp_url.c | 640 ++ src/common/http/llhttp_url.h | 60 + src/common/kcp/Readme.txt | 4 + src/common/kcp/ikcp.c | 1307 ++++ src/common/kcp/ikcp.h | 416 ++ 116 files changed, 60661 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README_zh.md create mode 100644 client/client.cpp create mode 100644 client/client.vcxproj create mode 100644 client/client.vcxproj.filters create mode 100644 client/client.vcxproj.user create mode 100644 global/Crypto.cpp create mode 100644 global/Crypto.h create mode 100644 global/helper.cpp create mode 100644 global/helper.h create mode 100644 include/hpsocket/GlobalDef.h create mode 100644 include/hpsocket/GlobalErrno.h create mode 100644 include/hpsocket/HPSocket-SSL.h create mode 100644 include/hpsocket/HPSocket.h create mode 100644 include/hpsocket/HPTypeDef.h create mode 100644 include/hpsocket/SocketInterface.h create mode 100644 server/server.cpp create mode 100644 src/ArqHelper.cpp create mode 100644 src/ArqHelper.h create mode 100644 src/HPSocket-SSL.cpp create mode 100644 src/HPSocket.cpp create mode 100644 src/HPThreadPool.cpp create mode 100644 src/HPThreadPool.h create mode 100644 src/HttpAgent.cpp create mode 100644 src/HttpAgent.h create mode 100644 src/HttpClient.cpp create mode 100644 src/HttpClient.h create mode 100644 src/HttpCookie.cpp create mode 100644 src/HttpCookie.h create mode 100644 src/HttpHelper.cpp create mode 100644 src/HttpHelper.h create mode 100644 src/HttpServer.cpp create mode 100644 src/HttpServer.h create mode 100644 src/InternalDef.h create mode 100644 src/MiscHelper.cpp create mode 100644 src/MiscHelper.h create mode 100644 src/SSLAgent.cpp create mode 100644 src/SSLAgent.h create mode 100644 src/SSLClient.cpp create mode 100644 src/SSLClient.h create mode 100644 src/SSLHelper.cpp create mode 100644 src/SSLHelper.h create mode 100644 src/SSLServer.cpp create mode 100644 src/SSLServer.h create mode 100644 src/SocketHelper.cpp create mode 100644 src/SocketHelper.h create mode 100644 src/SocketObject4C.h create mode 100644 src/TcpAgent.cpp create mode 100644 src/TcpAgent.h create mode 100644 src/TcpClient.cpp create mode 100644 src/TcpClient.h create mode 100644 src/TcpPackAgent.cpp create mode 100644 src/TcpPackAgent.h create mode 100644 src/TcpPackClient.cpp create mode 100644 src/TcpPackClient.h create mode 100644 src/TcpPackServer.cpp create mode 100644 src/TcpPackServer.h create mode 100644 src/TcpPullAgent.cpp create mode 100644 src/TcpPullAgent.h create mode 100644 src/TcpPullClient.cpp create mode 100644 src/TcpPullClient.h create mode 100644 src/TcpPullServer.cpp create mode 100644 src/TcpPullServer.h create mode 100644 src/TcpServer.cpp create mode 100644 src/TcpServer.h create mode 100644 src/UdpArqClient.cpp create mode 100644 src/UdpArqClient.h create mode 100644 src/UdpArqServer.cpp create mode 100644 src/UdpArqServer.h create mode 100644 src/UdpCast.cpp create mode 100644 src/UdpCast.h create mode 100644 src/UdpClient.cpp create mode 100644 src/UdpClient.h create mode 100644 src/UdpNode.cpp create mode 100644 src/UdpNode.h create mode 100644 src/UdpServer.cpp create mode 100644 src/UdpServer.h create mode 100644 src/common/BufferPool.cpp create mode 100644 src/common/BufferPool.h create mode 100644 src/common/BufferPtr.h create mode 100644 src/common/CriSec.h create mode 100644 src/common/Event.cpp create mode 100644 src/common/Event.h create mode 100644 src/common/FileHelper.cpp create mode 100644 src/common/FileHelper.h create mode 100644 src/common/FuncHelper.cpp create mode 100644 src/common/FuncHelper.h create mode 100644 src/common/GeneralHelper.h create mode 100644 src/common/IODispatcher.cpp create mode 100644 src/common/IODispatcher.h create mode 100644 src/common/PollHelper.cpp create mode 100644 src/common/PollHelper.h create mode 100644 src/common/PrivateHeap.h create mode 100644 src/common/RWLock.cpp create mode 100644 src/common/RWLock.h create mode 100644 src/common/RingBuffer.h create mode 100644 src/common/STLHelper.h create mode 100644 src/common/Semaphore.h create mode 100644 src/common/SignalHandler.h create mode 100644 src/common/Singleton.h create mode 100644 src/common/StringT.h create mode 100644 src/common/SysHelper.cpp create mode 100644 src/common/SysHelper.h create mode 100644 src/common/Thread.cpp create mode 100644 src/common/Thread.h create mode 100644 src/common/http/Readme.txt create mode 100644 src/common/http/llhttp.h create mode 100644 src/common/http/llhttp_api.c create mode 100644 src/common/http/llhttp_internal.c create mode 100644 src/common/http/llhttp_support.c create mode 100644 src/common/http/llhttp_url.c create mode 100644 src/common/http/llhttp_url.h create mode 100644 src/common/kcp/Readme.txt create mode 100644 src/common/kcp/ikcp.c create mode 100644 src/common/kcp/ikcp.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..959ab17 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.10) +project(HP-Socket LANGUAGES C CXX) + +# 设置编译选项 + + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +set(CMAKE_BUILD_TYPE Debug) # 发布模式(可选 Debug) + +# 定义源码目录和头文件目录 +set(HP_SOCKET_SRC_DIR ${PROJECT_SOURCE_DIR}/src) +set(HP_SOCKET_INC_DIR ${PROJECT_SOURCE_DIR}/include) + +# 收集所有源文件(根据实际源码结构调整) +file(GLOB HP_SOCKET_SOURCES + ${HP_SOCKET_SRC_DIR}/*.c + ${HP_SOCKET_SRC_DIR}/*.cpp + ${HP_SOCKET_SRC_DIR}/linux/*.c + ${HP_SOCKET_SRC_DIR}/linux/*.c + ${HP_SOCKET_SRC_DIR}/common/*.cpp + ${HP_SOCKET_SRC_DIR}/common/http/*.c + ${HP_SOCKET_SRC_DIR}/common/kcp/*.c + ${PROJECT_SOURCE_DIR}/global/*.cpp +) + +# 收集头文件(用于 IDE 索引) +file(GLOB HP_SOCKET_HEADERS + ${HP_SOCKET_INC_DIR}/*.h + ${HP_SOCKET_INC_DIR}/*.hpp +) + +# 包含头文件目录 +include_directories(${HP_SOCKET_INC_DIR}) +include_directories(${HP_SOCKET_SRC_DIR}) +include_directories(${HP_SOCKET_SRC_DIR}/linux) # Linux 平台相关头文件 +include_directories(${PROJECT_SOURCE_DIR}/global) + +# 定义库目标(动态库:SHARED,静态库:STATIC) +add_library(hp-socket SHARED ${HP_SOCKET_SOURCES} ${HP_SOCKET_HEADERS}) + + + + +# 链接系统库(Linux 需链接 pthread 和 rt) +target_link_libraries(hp-socket PRIVATE pthread rt) + + +add_executable(client ${PROJECT_SOURCE_DIR}/client/client.cpp) +target_link_libraries(client PRIVATE pthread rt) +target_link_libraries(client PRIVATE hp-socket) + +add_executable(server ${PROJECT_SOURCE_DIR}/server/server.cpp) +target_link_libraries(server PRIVATE pthread rt) +target_link_libraries(server PRIVATE hp-socket) + +# 可选:若启用 SSL,链接 OpenSSL +# find_package(OpenSSL REQUIRED) +# target_link_libraries(hp-socket PRIVATE OpenSSL::SSL OpenSSL::Crypto) diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 0000000..ce1df60 --- /dev/null +++ b/README_zh.md @@ -0,0 +1,159 @@ +![HP-Socket](Doc/HP-Socket%20Logo.png "HP-Socket") +--- +*高性能跨平台网络通信框架* +## 描述 +- ***Server*** 基于IOCP / EPOLL通信模型,并结合缓存池、私有堆等技术实现高效内存管理,支持超大规模、高并发通信场景。 +- ***Agent*** Agent组件实质上是Multi-Client组件,与Server组件采用相同的技术架构。一个Agent组件对象可同时建立和高效处理大规模Socket连接。 +- ***Client*** 基于Event Select / POLL通信模型,每个组件对象创建一个通信线程并管理一个Socket连接,适用于小规模客户端场景。 +## 文档 +- HP-Socket开发指南 +[[pdf]](Doc/HP-Socket%20Development%20Guide.pdf) +- HP-Socket基础组件类图 +[[uml]](Doc/HP-Socket%20Class%20Diagram.uml) +- HP-Socket基础组件类图 +[[jpg]](Doc/HP-Socket%20Class%20Diagram.jpg) +- HP-Socket SSL组件类图组件 +[[jpg]](Doc/HP-Socket%20SSL%20Class%20Diagram.jpg) +- HP-Socket HTTP组件类图 +[[jpg]](Doc/HP-Socket%20HTTP%20Class%20Diagram.jpg) +## 工作流程 +1. 创建监听器 +2. 创建通信组件(同时绑定监听器) +3. 启动通信组件 +4. 连接到目标主机(*Agent*组件) +5. 处理通信事件(*OnConnect/OnReceive/OnClose*等) +6. 停止通信组件(可选:在第7步销毁通信组件时会自动停止组件) +7. 销毁通信组件 +8. 销毁监听器 + +![Agent工作流程](Doc/HP-Socket-Agent-Demo.jpg "Agent工作流程") +## 示例 +- ***C++示例*** + +``` C++ +#include + +/* Listener Class */ +class CListenerImpl : public CTcpPullServerListener +{ + +public: + // 5. process network events + virtual EnHandleResult OnPrepareListen(ITcpServer* pSender, SOCKET soListen); + virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient); + virtual EnHandleResult OnHandShake(ITcpServer* pSender, CONNID dwConnID); + virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, int iLength); + virtual EnHandleResult OnSend(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength); + virtual EnHandleResult OnClose(ITcpServer* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode); + virtual EnHandleResult OnShutdown(ITcpServer* pSender); +}; + +int main(int argc, char* const argv[]) +{ + // 1. Create listener object + CListenerImpl s_listener; + // 2. Create component object (and binding with listener object) + CTcpPullServerPtr s_pserver(&s_listener); + + // 3. Start component object + if(!s_pserver->Start("0.0.0.0", 5555)) + exit(1); + + /* wait for exit */ + // ... ... + + // 6. (optional) Stop component object + s_pserver->Stop(); + + return 0; + + // 7. Destroy component object automatically + // 8. Destroy listener object automatically +} +``` + +- ***C示例*** + +``` C +#include + +// 5. process network events +EnHandleResult __HP_CALL OnConnect(HP_Agent pSender, HP_CONNID dwConnID); +EnHandleResult __HP_CALL OnReceive(HP_Agent pSender, HP_CONNID dwConnID, int iLength); +EnHandleResult __HP_CALL OnSend(HP_Agent pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength); +EnHandleResult __HP_CALL OnClose(HP_Agent pSender, HP_CONNID dwConnID, En_HP_SocketOperation enOperation, int iErrorCode); +EnHandleResult __HP_CALL OnShutdown(HP_Agent pSender); + +int main(int argc, char* const argv[]) +{ + HP_TcpPullAgentListener s_listener; + HP_TcpPullAgent s_agent; + + // 1. Create listener object + s_listener = ::Create_HP_TcpPullAgentListener(); + // 2. Create component object (and binding with listener object) + s_agent = ::Create_HP_TcpPullAgent(s_listener); + + /* Set listener callbacks */ + ::HP_Set_FN_Agent_OnConnect(s_listener, OnConnect); + ::HP_Set_FN_Agent_OnSend(s_listener, OnSend); + ::HP_Set_FN_Agent_OnPullReceive(s_listener, OnReceive); + ::HP_Set_FN_Agent_OnClose(s_listener, OnClose); + ::HP_Set_FN_Agent_OnShutdown(s_listener, OnShutdown); + + // 3. Start component object + if(!::HP_Agent_Start(s_agent, "0.0.0.0", TRUE)) + exit(1); + + // 4. Connect to dest host + ::HP_Agent_Connect(s_agent, REMOTE_HOST_1, REMOTE_PORT_1, nullptr); + ::HP_Agent_Connect(s_agent, REMOTE_HOST_2, REMOTE_PORT_2, nullptr); + ::HP_Agent_Connect(s_agent, REMOTE_HOST_3, REMOTE_PORT_3, nullptr); + + /* wait for exit */ + // ... ... + + // 6. (optional) Stop component object + ::HP_Agent_Stop(s_agent); + + // 7. Destroy component object + ::Destroy_HP_TcpPullAgent(s_agent); + // 8. Destroy listener object + ::Destroy_HP_TcpPullAgentListener(s_listener); + + return 0; +} +``` + +## 组件列表 +- ***基础组件*** + +![Basic Component](Doc/Basic%20Component%20-%20mini.jpg "基础组件") + +- ***SSL组件*** + +![SSL Component](Doc/SSL%20Component%20-%20mini.jpg "SSL 组件") + +- ***HTTP组件*** + +![HTTP Component](Doc/HTTP%20Component%20-%20mini.jpg "HTTP 组件") + +## 引用项目 + +- *[mimalloc](https://github.com/microsoft/mimalloc)* +- *[jemalloc](https://github.com/jemalloc/jemalloc)* +- *[openssl](https://github.com/openssl/openssl)* +- *[llhttp](https://github.com/nodejs/llhttp)* +- *[zlib](https://github.com/madler/zlib)* +- *[brotli](https://github.com/google/brotli)* +- *[kcp](https://github.com/skywind3000/kcp)* + +## 扩展项目 + +- *[HP-Socket for MacOS](https://gitee.com/xin_chong/HP-Socket-for-macOS)* +- *[HP-Socket for .Net](https://gitee.com/int2e/HPSocket.Net)* + +## 技术交流 + +- *[怪兽乐园①群](https://jq.qq.com/?_wv=1027&k=3UAbrhTG)* +- *[怪兽乐园②群](https://jq.qq.com/?_wv=1027&k=uYBpc6bG)* \ No newline at end of file diff --git a/client/client.cpp b/client/client.cpp new file mode 100644 index 0000000..bcd210d --- /dev/null +++ b/client/client.cpp @@ -0,0 +1,116 @@ +#undef _BROTLI_SUPPORT + +#include "helper.h" +#include "TcpClient.h" + +class CListenerImpl : public CTcpClientListener +{ + +public: + virtual EnHandleResult OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket) override + { + return HR_OK; + } + + virtual EnHandleResult OnConnect(ITcpClient* pSender, CONNID dwConnID) override + { + TCHAR szAddress[100]; + int iAddressLen = sizeof(szAddress) / sizeof(TCHAR); + USHORT usPort; + + pSender->GetRemoteHost(szAddress, iAddressLen, usPort); + + ::PostOnConnect2(dwConnID, szAddress, usPort); + + return HR_OK; + } + + virtual EnHandleResult OnHandShake(ITcpClient* pSender, CONNID dwConnID) override + { + return HR_OK; + } + + virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override + { + ::PostOnReceive(dwConnID, pData, iLength); + return HR_OK; + } + + virtual EnHandleResult OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override + { + ::PostOnSend(dwConnID, pData, iLength); + return HR_OK; + } + + virtual EnHandleResult OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode) override + { + iErrorCode == SE_OK ? ::PostOnClose(dwConnID) : + ::PostOnError(dwConnID, enOperation, iErrorCode); + + return HR_OK; + } + +}; + +CListenerImpl s_listener; +CTcpClient s_client(&s_listener); + +void OnCmdStart(CCommandParser* pParser) +{ + if(s_client.Start(g_app_arg.remote_addr, g_app_arg.port, g_app_arg.async, g_app_arg.bind_addr)) + ::LogClientStart(g_app_arg.remote_addr, g_app_arg.port); + else + ::LogClientStartFail(s_client.GetLastError(), s_client.GetLastErrorDesc()); +} + +void OnCmdStop(CCommandParser* pParser) +{ + if(s_client.Stop()) + ::LogClientStop(); + else + ::LogClientStopFail(s_client.GetLastError(), s_client.GetLastErrorDesc()); +} + +void OnCmdStatus(CCommandParser* pParser) +{ + pParser->PrintStatus(s_client.GetState()); +} + +void OnCmdSend(CCommandParser* pParser) +{ + if(s_client.Send((LPBYTE)(LPCTSTR)pParser->m_strMessage, pParser->m_strMessage.GetLength())) + ::LogSend(s_client.GetConnectionID(), pParser->m_strMessage); + else + ::LogSendFail(s_client.GetConnectionID(), ::GetLastError(), ::GetLastErrorStr()); +} + +void OnCmdPause(CCommandParser* pParser) +{ + if(s_client.PauseReceive(pParser->m_bFlag)) + ::LogPause(s_client.GetConnectionID(), pParser->m_bFlag); + else + ::LogPauseFail(s_client.GetConnectionID(), pParser->m_bFlag); +} + +int main(int argc, char* const argv[]) +{ + CTermAttrInitializer term_attr; + CAppSignalHandler s_signal_handler({SIGTTOU, SIGINT}); + + g_app_arg.ParseArgs(argc, argv); + + s_client.SetKeepAliveTime(g_app_arg.keep_alive ? TCP_KEEPALIVE_TIME : 0); + + CCommandParser::CMD_FUNC fnCmds[CCommandParser::CT_MAX] = {0}; + + fnCmds[CCommandParser::CT_START] = OnCmdStart; + fnCmds[CCommandParser::CT_STOP] = OnCmdStop; + fnCmds[CCommandParser::CT_STATUS] = OnCmdStatus; + fnCmds[CCommandParser::CT_SEND] = OnCmdSend; + fnCmds[CCommandParser::CT_PAUSE] = OnCmdPause; + + CCommandParser s_cmd_parser(CCommandParser::AT_CLIENT, fnCmds); + s_cmd_parser.Run(); + + return EXIT_CODE_OK; +} diff --git a/client/client.vcxproj b/client/client.vcxproj new file mode 100644 index 0000000..89dfa21 --- /dev/null +++ b/client/client.vcxproj @@ -0,0 +1,280 @@ + + + + + Debug + ARM + + + Release + ARM + + + Debug + x86 + + + Release + x86 + + + Debug + x64 + + + Release + x64 + + + + {A72AD679-483D-4754-AAF6-9DCFCDF49F14} + Linux + client + 14.0 + Linux + 1.0 + Generic + {D51BCBC9-82E9-4017-911E-C93873C4EA2B} + client + + + + true + $HOME/MyWork/HP-Socket/Linux/demo + $(SolutionName) + + + false + $HOME/MyWork/HP-Socket/Linux/demo + $(SolutionName) + + + true + $HOME/MyWork/HP-Socket/Linux/demo + $(SolutionName) + + + false + $HOME/MyWork/HP-Socket/Linux/demo + $(SolutionName) + + + true + $HOME/MyWork/HP-Socket/Linux/demo + $(SolutionName) + + + false + $HOME/MyWork/HP-Socket/Linux/demo + $(SolutionName) + + + + + + + + ../../$(Configuration)/$(Platform)/ + $(OutDir)obj/$(SolutionName)/$(ProjectName)/ + hp-$(SolutionName)-$(ProjectName) + .exe + $(RemoteRootDir)/$(SolutionName)/$(ProjectName) + false + false + $(RemoteProjectRelDir)/$(ProjectName)/$(IntDir) + $(RemoteProjectRelDir)/$(ProjectName)/$(OutDir) + $(RemoteOutputRelDir) + + + ../../$(Configuration)/$(Platform)/ + $(OutDir)obj/$(SolutionName)/$(ProjectName)/ + hp-$(SolutionName)-$(ProjectName) + .exe + $(RemoteRootDir)/$(SolutionName)/$(ProjectName) + false + false + $(RemoteProjectRelDir)/$(ProjectName)/$(IntDir) + $(RemoteProjectRelDir)/$(ProjectName)/$(OutDir) + $(RemoteOutputRelDir) + + + ../../$(Configuration)/$(Platform)/ + $(OutDir)obj/$(SolutionName)/$(ProjectName)/ + hp-$(SolutionName)-$(ProjectName) + .exe + $(RemoteRootDir)/$(SolutionName)/$(ProjectName) + false + false + $(RemoteProjectRelDir)/$(ProjectName)/$(IntDir) + $(RemoteProjectRelDir)/$(ProjectName)/$(OutDir) + $(RemoteOutputRelDir) + + + ../../$(Configuration)/$(Platform)/ + $(OutDir)obj/$(SolutionName)/$(ProjectName)/ + hp-$(SolutionName)-$(ProjectName) + .exe + $(RemoteRootDir)/$(SolutionName)/$(ProjectName) + false + false + $(RemoteProjectRelDir)/$(ProjectName)/$(IntDir) + $(RemoteProjectRelDir)/$(ProjectName)/$(OutDir) + $(RemoteOutputRelDir) + + + ../../$(Configuration)/$(Platform)/ + $(OutDir)obj/$(SolutionName)/$(ProjectName)/ + hp-$(SolutionName)-$(ProjectName) + .exe + $(RemoteRootDir)/$(SolutionName)/$(ProjectName) + false + false + $(RemoteProjectRelDir)/$(ProjectName)/$(IntDir) + $(RemoteProjectRelDir)/$(ProjectName)/$(OutDir) + $(RemoteOutputRelDir) + + + ../../$(Configuration)/$(Platform)/ + $(OutDir)obj/$(SolutionName)/$(ProjectName)/ + hp-$(SolutionName)-$(ProjectName) + .exe + $(RemoteRootDir)/$(SolutionName)/$(ProjectName) + false + false + $(RemoteProjectRelDir)/$(ProjectName)/$(IntDir) + $(RemoteProjectRelDir)/$(ProjectName)/$(OutDir) + $(RemoteOutputRelDir) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + no-class-memaccess;no-reorder;switch;no-deprecated-declarations;empty-body;conversion;return-type;parentheses;no-format;uninitialized;unreachable-code;unused-function;unused-value;unused-variable;%(CppAdditionalWarning) + c++17 + true + ../../../dependent/$(Platform)/include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories) + _UDP_DISABLED;_SSL_DISABLED;_HTTP_DISABLED;_ZLIB_DISABLED;_BROTLI_DISABLED; + + + rt;pthread;dl + ../../../dependent/$(Platform)/lib;%(AdditionalLibraryDirectories) + -L"$(RemoteRootDir)/../dependent/$(Platform)/lib" %(AdditionalOptions) + + + + + no-class-memaccess;no-reorder;switch;no-deprecated-declarations;empty-body;conversion;return-type;parentheses;no-format;uninitialized;unreachable-code;unused-function;unused-value;unused-variable;%(CppAdditionalWarning) + c++17 + None + true + ../../../dependent/$(Platform)/include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories) + _UDP_DISABLED;_SSL_DISABLED;_HTTP_DISABLED;_ZLIB_DISABLED;_BROTLI_DISABLED;NDEBUG;%(PreprocessorDefinitions) + + + mimalloc;rt;pthread;dl + ../../../dependent/$(Platform)/lib;%(AdditionalLibraryDirectories) + -L"$(RemoteRootDir)/../dependent/$(Platform)/lib" %(AdditionalOptions) + + + + + no-class-memaccess;no-reorder;switch;no-deprecated-declarations;empty-body;conversion;return-type;parentheses;no-format;uninitialized;unreachable-code;unused-function;unused-value;unused-variable;%(CppAdditionalWarning) + c++17 + true + ../../../dependent/$(Platform)/include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories) + _UDP_DISABLED;_SSL_DISABLED;_HTTP_DISABLED;_ZLIB_DISABLED;_BROTLI_DISABLED; + + + rt;pthread;dl + ../../../dependent/$(Platform)/lib;%(AdditionalLibraryDirectories) + -L"$(RemoteRootDir)/../dependent/$(Platform)/lib" %(AdditionalOptions) + + + + + no-class-memaccess;no-reorder;switch;no-deprecated-declarations;empty-body;conversion;return-type;parentheses;no-format;uninitialized;unreachable-code;unused-function;unused-value;unused-variable;%(CppAdditionalWarning) + c++17 + None + true + ../../../dependent/$(Platform)/include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories) + _UDP_DISABLED;_SSL_DISABLED;_HTTP_DISABLED;_ZLIB_DISABLED;_BROTLI_DISABLED;NDEBUG;%(PreprocessorDefinitions) + + + mimalloc;rt;pthread;dl + ../../../dependent/$(Platform)/lib;%(AdditionalLibraryDirectories) + -L"$(RemoteRootDir)/../dependent/$(Platform)/lib" %(AdditionalOptions) + + + + + no-class-memaccess;no-reorder;switch;no-deprecated-declarations;empty-body;conversion;return-type;parentheses;no-format;uninitialized;unreachable-code;unused-function;unused-value;unused-variable;%(CppAdditionalWarning) + c++17 + true + ../../../dependent/$(Platform)/include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories) + _UDP_DISABLED;_SSL_DISABLED;_HTTP_DISABLED;_ZLIB_DISABLED;_BROTLI_DISABLED; + + + rt;pthread;dl + ../../../dependent/$(Platform)/lib;%(AdditionalLibraryDirectories) + -L"$(RemoteRootDir)/../dependent/$(Platform)/lib" %(AdditionalOptions) + + + + + no-class-memaccess;no-reorder;switch;no-deprecated-declarations;empty-body;conversion;return-type;parentheses;no-format;uninitialized;unreachable-code;unused-function;unused-value;unused-variable;%(CppAdditionalWarning) + c++17 + None + true + ../../../dependent/$(Platform)/include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories) + _UDP_DISABLED;_SSL_DISABLED;_HTTP_DISABLED;_ZLIB_DISABLED;_BROTLI_DISABLED;NDEBUG;%(PreprocessorDefinitions) + + + mimalloc;rt;pthread;dl + ../../../dependent/$(Platform)/lib;%(AdditionalLibraryDirectories) + -L"$(RemoteRootDir)/../dependent/$(Platform)/lib" %(AdditionalOptions) + + + + + \ No newline at end of file diff --git a/client/client.vcxproj.filters b/client/client.vcxproj.filters new file mode 100644 index 0000000..b1117ba --- /dev/null +++ b/client/client.vcxproj.filters @@ -0,0 +1,138 @@ + + + + + {89aa27f5-854a-4f0f-b354-ad45f3467e4a} + + + {62af1e40-2972-4998-98ce-183503d5b9f1} + + + {6dfe1087-bd2e-422b-b9bb-44ea3edf3d8e} + + + {88ead97d-e618-4da0-8572-fcfb5e5cd08c} + + + + + Main + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + HPSocket + + + Global + + + HPSocket + + + Common + + + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + HPSocket + + + HPSocket + + + HPSocket + + + Global + + + Common + + + HPSocket + + + \ No newline at end of file diff --git a/client/client.vcxproj.user b/client/client.vcxproj.user new file mode 100644 index 0000000..3dd3b56 --- /dev/null +++ b/client/client.vcxproj.user @@ -0,0 +1,37 @@ + + + + -50468727;192.168.56.13 (username=, port=22, authentication=Password) + gdb + LinuxDebugger + -a 127.0.0.1 -p 5555 -n 0 + + + -50468727;192.168.56.13 (username=, port=22, authentication=Password) + gdb + LinuxDebugger + -a 127.0.0.1 -p 5555 -n 0 + + + -561002904;192.168.56.14 (username=, port=22, authentication=Password) + gdb + LinuxDebugger + -a 127.0.0.1 -p 5555 -n 0 + + + -561002904;192.168.56.14 (username=, port=22, authentication=Password) + gdb + LinuxDebugger + -a 127.0.0.1 -p 5555 -n 0 + + + gdb + LinuxDebugger + -a 127.0.0.1 -p 5555 -n 0 + + + gdb + LinuxDebugger + -a 127.0.0.1 -p 5555 -n 0 + + \ No newline at end of file diff --git a/global/Crypto.cpp b/global/Crypto.cpp new file mode 100644 index 0000000..67289e7 --- /dev/null +++ b/global/Crypto.cpp @@ -0,0 +1,2375 @@ +#include "Crypto.h" + +#include +#include + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wconversion" + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +/****************************** MACROS ******************************/ + +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +// -------------------------------------------------- BASE64 -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define NEWLINE_INVL 76 + +/**************************** VARIABLES *****************************/ +// Note: To change the charset to a URL encoding, replace the '+' and '/' with '*' and '-' +static const BYTE charset[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +BYTE revchar(char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch -= 'A'; + else if (ch >= 'a' && ch <='z') + ch = ch - 'a' + 26; + else if (ch >= '0' && ch <='9') + ch = ch - '0' + 52; + else if (ch == '+') + ch = 62; + else if (ch == '/') + ch = 63; + + return(ch); +} + +size_t base64_encode(const BYTE in[], BYTE out[], size_t len, int newline_flag) +{ + size_t idx, idx2, blks, blk_ceiling, left_over, newline_count = 0; + + blks = (len / 3); + left_over = len % 3; + + if (out == nullptr) { + idx2 = blks * 4 ; + if (left_over) + idx2 += 4; + if (newline_flag) + idx2 += len / 57; // (NEWLINE_INVL / 4) * 3 = 57. One newline per 57 input bytes. + } + else { + // Since 3 input bytes = 4 output bytes, determine out how many even sets of + // 3 bytes the input has. + blk_ceiling = blks * 3; + for (idx = 0, idx2 = 0; idx < blk_ceiling; idx += 3, idx2 += 4) { + out[idx2] = charset[in[idx] >> 2]; + out[idx2 + 1] = charset[((in[idx] & 0x03) << 4) | (in[idx + 1] >> 4)]; + out[idx2 + 2] = charset[((in[idx + 1] & 0x0f) << 2) | (in[idx + 2] >> 6)]; + out[idx2 + 3] = charset[in[idx + 2] & 0x3F]; + // The offical standard requires a newline every 76 characters. + // (Eg, first newline is character 77 of the output.) + if (((idx2 - newline_count + 4) % NEWLINE_INVL == 0) && newline_flag) { + out[idx2 + 4] = '\n'; + idx2++; + newline_count++; + } + } + + if (left_over == 1) { + out[idx2] = charset[in[idx] >> 2]; + out[idx2 + 1] = charset[(in[idx] & 0x03) << 4]; + out[idx2 + 2] = '='; + out[idx2 + 3] = '='; + idx2 += 4; + } + else if (left_over == 2) { + out[idx2] = charset[in[idx] >> 2]; + out[idx2 + 1] = charset[((in[idx] & 0x03) << 4) | (in[idx + 1] >> 4)]; + out[idx2 + 2] = charset[(in[idx + 1] & 0x0F) << 2]; + out[idx2 + 3] = '='; + idx2 += 4; + } + } + + return(idx2); +} + +size_t base64_decode(const BYTE in[], BYTE out[], size_t len) +{ + size_t idx, idx2, blks, blk_ceiling, left_over; + + if (in[len - 1] == '=') + len--; + if (in[len - 1] == '=') + len--; + + blks = len / 4; + left_over = len % 4; + + if (out == nullptr) { + if (len >= 77 && in[NEWLINE_INVL] == '\n') // Verify that newlines where used. + len -= len / (NEWLINE_INVL + 1); + blks = len / 4; + left_over = len % 4; + + idx = blks * 3; + if (left_over == 2) + idx ++; + else if (left_over == 3) + idx += 2; + } + else { + blk_ceiling = blks * 4; + for (idx = 0, idx2 = 0; idx2 < blk_ceiling; idx += 3, idx2 += 4) { + if (in[idx2] == '\n') + idx2++; + out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4); + out[idx + 1] = (revchar(in[idx2 + 1]) << 4) | (revchar(in[idx2 + 2]) >> 2); + out[idx + 2] = (revchar(in[idx2 + 2]) << 6) | revchar(in[idx2 + 3]); + } + + if (left_over == 2) { + out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4); + idx++; + } + else if (left_over == 3) { + out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4); + out[idx + 1] = (revchar(in[idx2 + 1]) << 4) | (revchar(in[idx2 + 2]) >> 2); + idx += 2; + } + } + + return(idx); +} + +// -------------------------------------------------- URL -------------------------------------------------- // + +#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) (((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) = HEX_VALUE_TO_CHAR((n >> 4)); *((pc) + 1) = HEX_VALUE_TO_CHAR((n & 0X0F));} + +int url_encode(const char* src, const int src_size, char* dest, const int dest_size) +{ + if(src == nullptr || dest == nullptr || src_size <= 0 || dest_size <= 0) + return 0; + + char ch; + int j = 0; + + for(int i = 0; (i < src_size) && (j < dest_size); ++i) + { + ch = src[i]; + + if (((ch>='A') && (ch<'Z')) || + ((ch>='a') && (ch<'z')) || + ((ch>='0') && (ch<'9')) || + (ch == '.' || ch == '-' || ch == '_' || ch == '*')) + dest[j++] = ch; + else if(ch == ' ') + dest[j++] = '+'; + else { + if(j + 3 < dest_size) + { + dest[j++] = '%'; + HEX_VALUE_TO_DOUBLE_CHAR(dest + j, ch); + j += 2; + } + else + return 0; + } + } + + dest[j] = '\0'; + return j; +} + +int url_decode(const char* src, const int src_size, char* dest, const int dest_size) +{ + if(src == nullptr || dest == nullptr || src_size <= 0 || dest_size <= 0) + return 0; + + char ch; + int j = 0; + + for(int i = 0; i < src_size && j < dest_size; ++i) + { + ch = src[i]; + + if(ch == '+') + dest[j++] = ' '; + else if(ch == '%') + { + if(i + 2 < src_size) + { + dest[j++] = HEX_DOUBLE_CHAR_TO_VALUE(src + i + 1); + i += 2; + } + } + else + dest[j++] = ch; + } + + dest[j] = 0; + return j; +} + +// -------------------------------------------------- AES -------------------------------------------------- // + +/****************************** MACROS ******************************/ +// The least significant byte of the word is rotated to the end. +#define KE_ROTWORD(x) (((x) << 8) | ((x) >> 24)) + +/**************************** DATA TYPES ****************************/ +#define AES_128_ROUNDS 10 +#define AES_192_ROUNDS 12 +#define AES_256_ROUNDS 14 + +/*********************** FUNCTION DECLARATIONS **********************/ +void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size); +void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len); +void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len); +void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len); + +/**************************** VARIABLES *****************************/ +// This is the specified AES SBox. To look up a substitution value, put the first +// nibble in the first index (row) and the second nibble in the second index (column). +static const BYTE aes_sbox[16][16] = { + {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, + {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, + {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, + {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, + {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, + {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, + {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, + {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, + {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, + {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, + {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, + {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, + {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, + {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, + {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, + {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} +}; + +static const BYTE aes_invsbox[16][16] = { + {0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB}, + {0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB}, + {0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E}, + {0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25}, + {0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92}, + {0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84}, + {0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06}, + {0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B}, + {0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73}, + {0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E}, + {0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B}, + {0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4}, + {0x1F,0xDD,0xA8,0x33,0x88,0x07,0xC7,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F}, + {0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF}, + {0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61}, + {0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x21,0x0C,0x7D} +}; + +// This table stores pre-calculated values for all possible GF(2^8) calculations.This +// table is only used by the (Inv)MixColumns steps. +// USAGE: The second index (column) is the coefficient of multiplication. Only 7 different +// coefficients are used: 0x01, 0x02, 0x03, 0x09, 0x0b, 0x0d, 0x0e, but multiplication by +// 1 is negligible leaving only 6 coefficients. Each column of the table is devoted to one +// of these coefficients, in the ascending order of value, from values 0x00 to 0xFF. +static const BYTE gf_mul[256][6] = { + {0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x03,0x09,0x0b,0x0d,0x0e}, + {0x04,0x06,0x12,0x16,0x1a,0x1c},{0x06,0x05,0x1b,0x1d,0x17,0x12}, + {0x08,0x0c,0x24,0x2c,0x34,0x38},{0x0a,0x0f,0x2d,0x27,0x39,0x36}, + {0x0c,0x0a,0x36,0x3a,0x2e,0x24},{0x0e,0x09,0x3f,0x31,0x23,0x2a}, + {0x10,0x18,0x48,0x58,0x68,0x70},{0x12,0x1b,0x41,0x53,0x65,0x7e}, + {0x14,0x1e,0x5a,0x4e,0x72,0x6c},{0x16,0x1d,0x53,0x45,0x7f,0x62}, + {0x18,0x14,0x6c,0x74,0x5c,0x48},{0x1a,0x17,0x65,0x7f,0x51,0x46}, + {0x1c,0x12,0x7e,0x62,0x46,0x54},{0x1e,0x11,0x77,0x69,0x4b,0x5a}, + {0x20,0x30,0x90,0xb0,0xd0,0xe0},{0x22,0x33,0x99,0xbb,0xdd,0xee}, + {0x24,0x36,0x82,0xa6,0xca,0xfc},{0x26,0x35,0x8b,0xad,0xc7,0xf2}, + {0x28,0x3c,0xb4,0x9c,0xe4,0xd8},{0x2a,0x3f,0xbd,0x97,0xe9,0xd6}, + {0x2c,0x3a,0xa6,0x8a,0xfe,0xc4},{0x2e,0x39,0xaf,0x81,0xf3,0xca}, + {0x30,0x28,0xd8,0xe8,0xb8,0x90},{0x32,0x2b,0xd1,0xe3,0xb5,0x9e}, + {0x34,0x2e,0xca,0xfe,0xa2,0x8c},{0x36,0x2d,0xc3,0xf5,0xaf,0x82}, + {0x38,0x24,0xfc,0xc4,0x8c,0xa8},{0x3a,0x27,0xf5,0xcf,0x81,0xa6}, + {0x3c,0x22,0xee,0xd2,0x96,0xb4},{0x3e,0x21,0xe7,0xd9,0x9b,0xba}, + {0x40,0x60,0x3b,0x7b,0xbb,0xdb},{0x42,0x63,0x32,0x70,0xb6,0xd5}, + {0x44,0x66,0x29,0x6d,0xa1,0xc7},{0x46,0x65,0x20,0x66,0xac,0xc9}, + {0x48,0x6c,0x1f,0x57,0x8f,0xe3},{0x4a,0x6f,0x16,0x5c,0x82,0xed}, + {0x4c,0x6a,0x0d,0x41,0x95,0xff},{0x4e,0x69,0x04,0x4a,0x98,0xf1}, + {0x50,0x78,0x73,0x23,0xd3,0xab},{0x52,0x7b,0x7a,0x28,0xde,0xa5}, + {0x54,0x7e,0x61,0x35,0xc9,0xb7},{0x56,0x7d,0x68,0x3e,0xc4,0xb9}, + {0x58,0x74,0x57,0x0f,0xe7,0x93},{0x5a,0x77,0x5e,0x04,0xea,0x9d}, + {0x5c,0x72,0x45,0x19,0xfd,0x8f},{0x5e,0x71,0x4c,0x12,0xf0,0x81}, + {0x60,0x50,0xab,0xcb,0x6b,0x3b},{0x62,0x53,0xa2,0xc0,0x66,0x35}, + {0x64,0x56,0xb9,0xdd,0x71,0x27},{0x66,0x55,0xb0,0xd6,0x7c,0x29}, + {0x68,0x5c,0x8f,0xe7,0x5f,0x03},{0x6a,0x5f,0x86,0xec,0x52,0x0d}, + {0x6c,0x5a,0x9d,0xf1,0x45,0x1f},{0x6e,0x59,0x94,0xfa,0x48,0x11}, + {0x70,0x48,0xe3,0x93,0x03,0x4b},{0x72,0x4b,0xea,0x98,0x0e,0x45}, + {0x74,0x4e,0xf1,0x85,0x19,0x57},{0x76,0x4d,0xf8,0x8e,0x14,0x59}, + {0x78,0x44,0xc7,0xbf,0x37,0x73},{0x7a,0x47,0xce,0xb4,0x3a,0x7d}, + {0x7c,0x42,0xd5,0xa9,0x2d,0x6f},{0x7e,0x41,0xdc,0xa2,0x20,0x61}, + {0x80,0xc0,0x76,0xf6,0x6d,0xad},{0x82,0xc3,0x7f,0xfd,0x60,0xa3}, + {0x84,0xc6,0x64,0xe0,0x77,0xb1},{0x86,0xc5,0x6d,0xeb,0x7a,0xbf}, + {0x88,0xcc,0x52,0xda,0x59,0x95},{0x8a,0xcf,0x5b,0xd1,0x54,0x9b}, + {0x8c,0xca,0x40,0xcc,0x43,0x89},{0x8e,0xc9,0x49,0xc7,0x4e,0x87}, + {0x90,0xd8,0x3e,0xae,0x05,0xdd},{0x92,0xdb,0x37,0xa5,0x08,0xd3}, + {0x94,0xde,0x2c,0xb8,0x1f,0xc1},{0x96,0xdd,0x25,0xb3,0x12,0xcf}, + {0x98,0xd4,0x1a,0x82,0x31,0xe5},{0x9a,0xd7,0x13,0x89,0x3c,0xeb}, + {0x9c,0xd2,0x08,0x94,0x2b,0xf9},{0x9e,0xd1,0x01,0x9f,0x26,0xf7}, + {0xa0,0xf0,0xe6,0x46,0xbd,0x4d},{0xa2,0xf3,0xef,0x4d,0xb0,0x43}, + {0xa4,0xf6,0xf4,0x50,0xa7,0x51},{0xa6,0xf5,0xfd,0x5b,0xaa,0x5f}, + {0xa8,0xfc,0xc2,0x6a,0x89,0x75},{0xaa,0xff,0xcb,0x61,0x84,0x7b}, + {0xac,0xfa,0xd0,0x7c,0x93,0x69},{0xae,0xf9,0xd9,0x77,0x9e,0x67}, + {0xb0,0xe8,0xae,0x1e,0xd5,0x3d},{0xb2,0xeb,0xa7,0x15,0xd8,0x33}, + {0xb4,0xee,0xbc,0x08,0xcf,0x21},{0xb6,0xed,0xb5,0x03,0xc2,0x2f}, + {0xb8,0xe4,0x8a,0x32,0xe1,0x05},{0xba,0xe7,0x83,0x39,0xec,0x0b}, + {0xbc,0xe2,0x98,0x24,0xfb,0x19},{0xbe,0xe1,0x91,0x2f,0xf6,0x17}, + {0xc0,0xa0,0x4d,0x8d,0xd6,0x76},{0xc2,0xa3,0x44,0x86,0xdb,0x78}, + {0xc4,0xa6,0x5f,0x9b,0xcc,0x6a},{0xc6,0xa5,0x56,0x90,0xc1,0x64}, + {0xc8,0xac,0x69,0xa1,0xe2,0x4e},{0xca,0xaf,0x60,0xaa,0xef,0x40}, + {0xcc,0xaa,0x7b,0xb7,0xf8,0x52},{0xce,0xa9,0x72,0xbc,0xf5,0x5c}, + {0xd0,0xb8,0x05,0xd5,0xbe,0x06},{0xd2,0xbb,0x0c,0xde,0xb3,0x08}, + {0xd4,0xbe,0x17,0xc3,0xa4,0x1a},{0xd6,0xbd,0x1e,0xc8,0xa9,0x14}, + {0xd8,0xb4,0x21,0xf9,0x8a,0x3e},{0xda,0xb7,0x28,0xf2,0x87,0x30}, + {0xdc,0xb2,0x33,0xef,0x90,0x22},{0xde,0xb1,0x3a,0xe4,0x9d,0x2c}, + {0xe0,0x90,0xdd,0x3d,0x06,0x96},{0xe2,0x93,0xd4,0x36,0x0b,0x98}, + {0xe4,0x96,0xcf,0x2b,0x1c,0x8a},{0xe6,0x95,0xc6,0x20,0x11,0x84}, + {0xe8,0x9c,0xf9,0x11,0x32,0xae},{0xea,0x9f,0xf0,0x1a,0x3f,0xa0}, + {0xec,0x9a,0xeb,0x07,0x28,0xb2},{0xee,0x99,0xe2,0x0c,0x25,0xbc}, + {0xf0,0x88,0x95,0x65,0x6e,0xe6},{0xf2,0x8b,0x9c,0x6e,0x63,0xe8}, + {0xf4,0x8e,0x87,0x73,0x74,0xfa},{0xf6,0x8d,0x8e,0x78,0x79,0xf4}, + {0xf8,0x84,0xb1,0x49,0x5a,0xde},{0xfa,0x87,0xb8,0x42,0x57,0xd0}, + {0xfc,0x82,0xa3,0x5f,0x40,0xc2},{0xfe,0x81,0xaa,0x54,0x4d,0xcc}, + {0x1b,0x9b,0xec,0xf7,0xda,0x41},{0x19,0x98,0xe5,0xfc,0xd7,0x4f}, + {0x1f,0x9d,0xfe,0xe1,0xc0,0x5d},{0x1d,0x9e,0xf7,0xea,0xcd,0x53}, + {0x13,0x97,0xc8,0xdb,0xee,0x79},{0x11,0x94,0xc1,0xd0,0xe3,0x77}, + {0x17,0x91,0xda,0xcd,0xf4,0x65},{0x15,0x92,0xd3,0xc6,0xf9,0x6b}, + {0x0b,0x83,0xa4,0xaf,0xb2,0x31},{0x09,0x80,0xad,0xa4,0xbf,0x3f}, + {0x0f,0x85,0xb6,0xb9,0xa8,0x2d},{0x0d,0x86,0xbf,0xb2,0xa5,0x23}, + {0x03,0x8f,0x80,0x83,0x86,0x09},{0x01,0x8c,0x89,0x88,0x8b,0x07}, + {0x07,0x89,0x92,0x95,0x9c,0x15},{0x05,0x8a,0x9b,0x9e,0x91,0x1b}, + {0x3b,0xab,0x7c,0x47,0x0a,0xa1},{0x39,0xa8,0x75,0x4c,0x07,0xaf}, + {0x3f,0xad,0x6e,0x51,0x10,0xbd},{0x3d,0xae,0x67,0x5a,0x1d,0xb3}, + {0x33,0xa7,0x58,0x6b,0x3e,0x99},{0x31,0xa4,0x51,0x60,0x33,0x97}, + {0x37,0xa1,0x4a,0x7d,0x24,0x85},{0x35,0xa2,0x43,0x76,0x29,0x8b}, + {0x2b,0xb3,0x34,0x1f,0x62,0xd1},{0x29,0xb0,0x3d,0x14,0x6f,0xdf}, + {0x2f,0xb5,0x26,0x09,0x78,0xcd},{0x2d,0xb6,0x2f,0x02,0x75,0xc3}, + {0x23,0xbf,0x10,0x33,0x56,0xe9},{0x21,0xbc,0x19,0x38,0x5b,0xe7}, + {0x27,0xb9,0x02,0x25,0x4c,0xf5},{0x25,0xba,0x0b,0x2e,0x41,0xfb}, + {0x5b,0xfb,0xd7,0x8c,0x61,0x9a},{0x59,0xf8,0xde,0x87,0x6c,0x94}, + {0x5f,0xfd,0xc5,0x9a,0x7b,0x86},{0x5d,0xfe,0xcc,0x91,0x76,0x88}, + {0x53,0xf7,0xf3,0xa0,0x55,0xa2},{0x51,0xf4,0xfa,0xab,0x58,0xac}, + {0x57,0xf1,0xe1,0xb6,0x4f,0xbe},{0x55,0xf2,0xe8,0xbd,0x42,0xb0}, + {0x4b,0xe3,0x9f,0xd4,0x09,0xea},{0x49,0xe0,0x96,0xdf,0x04,0xe4}, + {0x4f,0xe5,0x8d,0xc2,0x13,0xf6},{0x4d,0xe6,0x84,0xc9,0x1e,0xf8}, + {0x43,0xef,0xbb,0xf8,0x3d,0xd2},{0x41,0xec,0xb2,0xf3,0x30,0xdc}, + {0x47,0xe9,0xa9,0xee,0x27,0xce},{0x45,0xea,0xa0,0xe5,0x2a,0xc0}, + {0x7b,0xcb,0x47,0x3c,0xb1,0x7a},{0x79,0xc8,0x4e,0x37,0xbc,0x74}, + {0x7f,0xcd,0x55,0x2a,0xab,0x66},{0x7d,0xce,0x5c,0x21,0xa6,0x68}, + {0x73,0xc7,0x63,0x10,0x85,0x42},{0x71,0xc4,0x6a,0x1b,0x88,0x4c}, + {0x77,0xc1,0x71,0x06,0x9f,0x5e},{0x75,0xc2,0x78,0x0d,0x92,0x50}, + {0x6b,0xd3,0x0f,0x64,0xd9,0x0a},{0x69,0xd0,0x06,0x6f,0xd4,0x04}, + {0x6f,0xd5,0x1d,0x72,0xc3,0x16},{0x6d,0xd6,0x14,0x79,0xce,0x18}, + {0x63,0xdf,0x2b,0x48,0xed,0x32},{0x61,0xdc,0x22,0x43,0xe0,0x3c}, + {0x67,0xd9,0x39,0x5e,0xf7,0x2e},{0x65,0xda,0x30,0x55,0xfa,0x20}, + {0x9b,0x5b,0x9a,0x01,0xb7,0xec},{0x99,0x58,0x93,0x0a,0xba,0xe2}, + {0x9f,0x5d,0x88,0x17,0xad,0xf0},{0x9d,0x5e,0x81,0x1c,0xa0,0xfe}, + {0x93,0x57,0xbe,0x2d,0x83,0xd4},{0x91,0x54,0xb7,0x26,0x8e,0xda}, + {0x97,0x51,0xac,0x3b,0x99,0xc8},{0x95,0x52,0xa5,0x30,0x94,0xc6}, + {0x8b,0x43,0xd2,0x59,0xdf,0x9c},{0x89,0x40,0xdb,0x52,0xd2,0x92}, + {0x8f,0x45,0xc0,0x4f,0xc5,0x80},{0x8d,0x46,0xc9,0x44,0xc8,0x8e}, + {0x83,0x4f,0xf6,0x75,0xeb,0xa4},{0x81,0x4c,0xff,0x7e,0xe6,0xaa}, + {0x87,0x49,0xe4,0x63,0xf1,0xb8},{0x85,0x4a,0xed,0x68,0xfc,0xb6}, + {0xbb,0x6b,0x0a,0xb1,0x67,0x0c},{0xb9,0x68,0x03,0xba,0x6a,0x02}, + {0xbf,0x6d,0x18,0xa7,0x7d,0x10},{0xbd,0x6e,0x11,0xac,0x70,0x1e}, + {0xb3,0x67,0x2e,0x9d,0x53,0x34},{0xb1,0x64,0x27,0x96,0x5e,0x3a}, + {0xb7,0x61,0x3c,0x8b,0x49,0x28},{0xb5,0x62,0x35,0x80,0x44,0x26}, + {0xab,0x73,0x42,0xe9,0x0f,0x7c},{0xa9,0x70,0x4b,0xe2,0x02,0x72}, + {0xaf,0x75,0x50,0xff,0x15,0x60},{0xad,0x76,0x59,0xf4,0x18,0x6e}, + {0xa3,0x7f,0x66,0xc5,0x3b,0x44},{0xa1,0x7c,0x6f,0xce,0x36,0x4a}, + {0xa7,0x79,0x74,0xd3,0x21,0x58},{0xa5,0x7a,0x7d,0xd8,0x2c,0x56}, + {0xdb,0x3b,0xa1,0x7a,0x0c,0x37},{0xd9,0x38,0xa8,0x71,0x01,0x39}, + {0xdf,0x3d,0xb3,0x6c,0x16,0x2b},{0xdd,0x3e,0xba,0x67,0x1b,0x25}, + {0xd3,0x37,0x85,0x56,0x38,0x0f},{0xd1,0x34,0x8c,0x5d,0x35,0x01}, + {0xd7,0x31,0x97,0x40,0x22,0x13},{0xd5,0x32,0x9e,0x4b,0x2f,0x1d}, + {0xcb,0x23,0xe9,0x22,0x64,0x47},{0xc9,0x20,0xe0,0x29,0x69,0x49}, + {0xcf,0x25,0xfb,0x34,0x7e,0x5b},{0xcd,0x26,0xf2,0x3f,0x73,0x55}, + {0xc3,0x2f,0xcd,0x0e,0x50,0x7f},{0xc1,0x2c,0xc4,0x05,0x5d,0x71}, + {0xc7,0x29,0xdf,0x18,0x4a,0x63},{0xc5,0x2a,0xd6,0x13,0x47,0x6d}, + {0xfb,0x0b,0x31,0xca,0xdc,0xd7},{0xf9,0x08,0x38,0xc1,0xd1,0xd9}, + {0xff,0x0d,0x23,0xdc,0xc6,0xcb},{0xfd,0x0e,0x2a,0xd7,0xcb,0xc5}, + {0xf3,0x07,0x15,0xe6,0xe8,0xef},{0xf1,0x04,0x1c,0xed,0xe5,0xe1}, + {0xf7,0x01,0x07,0xf0,0xf2,0xf3},{0xf5,0x02,0x0e,0xfb,0xff,0xfd}, + {0xeb,0x13,0x79,0x92,0xb4,0xa7},{0xe9,0x10,0x70,0x99,0xb9,0xa9}, + {0xef,0x15,0x6b,0x84,0xae,0xbb},{0xed,0x16,0x62,0x8f,0xa3,0xb5}, + {0xe3,0x1f,0x5d,0xbe,0x80,0x9f},{0xe1,0x1c,0x54,0xb5,0x8d,0x91}, + {0xe7,0x19,0x4f,0xa8,0x9a,0x83},{0xe5,0x1a,0x46,0xa3,0x97,0x8d} +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +// XORs the in and out buffers, storing the result in out. Length is in bytes. +void xor_buf(const BYTE in[], BYTE out[], size_t len) +{ + size_t idx; + + for (idx = 0; idx < len; idx++) + out[idx] ^= in[idx]; +} + +/******************* +* AES - CBC +*******************/ +int aes_encrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const UINT key[], int keysize, const BYTE iv[]) +{ + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return(FALSE); + + blocks = (int)(in_len / AES_BLOCK_SIZE); + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); + aes_encrypt(buf_in, buf_out, key, keysize); + memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); + memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); + } + + return(TRUE); +} + +int aes_encrypt_cbc_mac(const BYTE in[], size_t in_len, BYTE out[], const UINT key[], int keysize, const BYTE iv[]) +{ + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return(FALSE); + + blocks = (int)(in_len / AES_BLOCK_SIZE); + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + xor_buf(iv_buf, buf_in, AES_BLOCK_SIZE); + aes_encrypt(buf_in, buf_out, key, keysize); + memcpy(iv_buf, buf_out, AES_BLOCK_SIZE); + // Do not output all encrypted blocks. + } + + memcpy(out, buf_out, AES_BLOCK_SIZE); // Only output the last block. + + return(TRUE); +} + +int aes_decrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const UINT key[], int keysize, const BYTE iv[]) +{ + BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE], iv_buf[AES_BLOCK_SIZE]; + int blocks, idx; + + if (in_len % AES_BLOCK_SIZE != 0) + return(FALSE); + + blocks = (int)(in_len / AES_BLOCK_SIZE); + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + + for (idx = 0; idx < blocks; idx++) { + memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); + aes_decrypt(buf_in, buf_out, key, keysize); + xor_buf(iv_buf, buf_out, AES_BLOCK_SIZE); + memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); + memcpy(iv_buf, buf_in, AES_BLOCK_SIZE); + } + + return(TRUE); +} + +/******************* +* AES - CTR +*******************/ +void increment_iv(BYTE iv[], int counter_size) +{ + int idx; + + // Use counter_size bytes at the end of the IV as the big-endian integer to increment. + for (idx = AES_BLOCK_SIZE - 1; idx >= AES_BLOCK_SIZE - counter_size; idx--) { + iv[idx]++; + if (iv[idx] != 0 || idx == AES_BLOCK_SIZE - counter_size) + break; + } +} + +// Performs the encryption in-place, the input and output buffers may be the same. +// Input may be an arbitrary length (in bytes). +void aes_encrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const UINT key[], int keysize, const BYTE iv[]) +{ + size_t idx = 0, last_block_length; + BYTE iv_buf[AES_BLOCK_SIZE], out_buf[AES_BLOCK_SIZE]; + + if (in != out) + memcpy(out, in, in_len); + + memcpy(iv_buf, iv, AES_BLOCK_SIZE); + last_block_length = in_len - AES_BLOCK_SIZE; + + if (in_len > AES_BLOCK_SIZE) { + for (idx = 0; idx < last_block_length; idx += AES_BLOCK_SIZE) { + aes_encrypt(iv_buf, out_buf, key, keysize); + xor_buf(out_buf, &out[idx], AES_BLOCK_SIZE); + increment_iv(iv_buf, AES_BLOCK_SIZE); + } + } + + aes_encrypt(iv_buf, out_buf, key, keysize); + xor_buf(out_buf, &out[idx], in_len - idx); // Use the Most Significant bytes. +} + +void aes_decrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const UINT key[], int keysize, const BYTE iv[]) +{ + // CTR encryption is its own inverse function. + aes_encrypt_ctr(in, in_len, out, key, keysize, iv); +} + +/******************* +* AES - CCM +*******************/ +// out_len = payload_len + assoc_len +int aes_encrypt_ccm(const BYTE payload[], UINT payload_len, const BYTE assoc[], unsigned short assoc_len, + const BYTE nonce[], unsigned short nonce_len, BYTE out[], UINT *out_len, + UINT mac_len, const BYTE key_str[], int keysize) +{ + BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], *buf; + int end_of_buf, payload_len_store_size; + UINT key[60]; + + if (mac_len != 4 && mac_len != 6 && mac_len != 8 && mac_len != 10 && + mac_len != 12 && mac_len != 14 && mac_len != 16) + return(FALSE); + + if (nonce_len < 7 || nonce_len > 13) + return(FALSE); + + if (assoc_len > 32768 /* = 2^15 */) + return(FALSE); + + buf = (BYTE*)malloc(payload_len + assoc_len + 48 /*Round both payload and associated data up a block size and add an extra block.*/); + if (! buf) + return(FALSE); + + // Prepare the key for usage. + aes_key_setup(key_str, key, keysize); + + // Format the first block of the formatted data. + payload_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + ccm_prepare_first_format_blk(buf, assoc_len, payload_len, payload_len_store_size, mac_len, nonce, nonce_len); + end_of_buf = AES_BLOCK_SIZE; + + // Format the Associated Data, aka, assoc[]. + ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); + + // Format the Payload, aka payload[]. + ccm_format_payload_data(buf, &end_of_buf, payload, payload_len); + + // Create the first counter block. + ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, payload_len_store_size); + + // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. + memset(temp_iv, 0, AES_BLOCK_SIZE); + aes_encrypt_cbc_mac(buf, end_of_buf, mac, key, keysize, temp_iv); + + // Copy the Payload and MAC to the output buffer. + memcpy(out, payload, payload_len); + memcpy(&out[payload_len], mac, mac_len); + + // Encrypt the Payload with CTR mode with a counter starting at 1. + memcpy(temp_iv, counter, AES_BLOCK_SIZE); + increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // Last argument is the byte size of the counting portion of the counter block. /*BUG?*/ + aes_encrypt_ctr(out, payload_len, out, key, keysize, temp_iv); + + // Encrypt the MAC with CTR mode with a counter starting at 0. + aes_encrypt_ctr(&out[payload_len], mac_len, &out[payload_len], key, keysize, counter); + + free(buf); + *out_len = payload_len + mac_len; + + return(TRUE); +} + +// plaintext_len = ciphertext_len - mac_len +// Needs a flag for whether the MAC matches. +int aes_decrypt_ccm(const BYTE ciphertext[], UINT ciphertext_len, const BYTE assoc[], unsigned short assoc_len, + const BYTE nonce[], unsigned short nonce_len, BYTE plaintext[], UINT *plaintext_len, + UINT mac_len, int *mac_auth, const BYTE key_str[], int keysize) +{ + BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], mac_buf[16], *buf; + int end_of_buf, plaintext_len_store_size; + UINT key[60]; + + if (ciphertext_len <= mac_len) + return(FALSE); + + buf = (BYTE*)malloc(assoc_len + ciphertext_len /*ciphertext_len = plaintext_len + mac_len*/ + 48); + if (! buf) + return(FALSE); + + // Prepare the key for usage. + aes_key_setup(key_str, key, keysize); + + // Copy the plaintext and MAC to the output buffers. + *plaintext_len = ciphertext_len - mac_len; + plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + memcpy(plaintext, ciphertext, *plaintext_len); + memcpy(mac, &ciphertext[*plaintext_len], mac_len); + + // Prepare the first counter block for use in decryption. + ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, plaintext_len_store_size); + + // Decrypt the Payload with CTR mode with a counter starting at 1. + memcpy(temp_iv, counter, AES_BLOCK_SIZE); + increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // (AES_BLOCK_SIZE - 1 - mac_len) is the byte size of the counting portion of the counter block. + aes_decrypt_ctr(plaintext, *plaintext_len, plaintext, key, keysize, temp_iv); + + // Setting mac_auth to nullptr disables the authentication check. + if (mac_auth != nullptr) { + // Decrypt the MAC with CTR mode with a counter starting at 0. + aes_decrypt_ctr(mac, mac_len, mac, key, keysize, counter); + + // Format the first block of the formatted data. + plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; + ccm_prepare_first_format_blk(buf, assoc_len, *plaintext_len, plaintext_len_store_size, mac_len, nonce, nonce_len); + end_of_buf = AES_BLOCK_SIZE; + + // Format the Associated Data into the authentication buffer. + ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); + + // Format the Payload into the authentication buffer. + ccm_format_payload_data(buf, &end_of_buf, plaintext, *plaintext_len); + + // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. + memset(temp_iv, 0, AES_BLOCK_SIZE); + aes_encrypt_cbc_mac(buf, end_of_buf, mac_buf, key, keysize, temp_iv); + + // Compare the calculated MAC against the MAC embedded in the ciphertext to see if they are the same. + if (! memcmp(mac, mac_buf, mac_len)) { + *mac_auth = TRUE; + } + else { + *mac_auth = FALSE; + memset(plaintext, 0, *plaintext_len); + } + } + + free(buf); + + return(TRUE); +} + +// Creates the first counter block. First byte is flags, then the nonce, then the incremented part. +void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size) +{ + memset(counter, 0, AES_BLOCK_SIZE); + counter[0] = (payload_len_store_size - 1) & 0x07; + memcpy(&counter[1], nonce, nonce_len); +} + +void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len) +{ + // Set the flags for the first byte of the first block. + buf[0] = ((((mac_len - 2) / 2) & 0x07) << 3) | ((payload_len_store_size - 1) & 0x07); + if (assoc_len > 0) + buf[0] += 0x40; + // Format the rest of the first block, storing the nonce and the size of the payload. + memcpy(&buf[1], nonce, nonce_len); + memset(&buf[1 + nonce_len], 0, AES_BLOCK_SIZE - 1 - nonce_len); + buf[15] = payload_len & 0x000000FF; + buf[14] = (payload_len >> 8) & 0x000000FF; +} + +void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len) +{ + int pad; + + buf[*end_of_buf + 1] = assoc_len & 0x00FF; + buf[*end_of_buf] = (assoc_len >> 8) & 0x00FF; + *end_of_buf += 2; + memcpy(&buf[*end_of_buf], assoc, assoc_len); + *end_of_buf += assoc_len; + pad = AES_BLOCK_SIZE - (*end_of_buf % AES_BLOCK_SIZE); /*BUG?*/ + memset(&buf[*end_of_buf], 0, pad); + *end_of_buf += pad; +} + +void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len) +{ + int pad; + + memcpy(&buf[*end_of_buf], payload, payload_len); + *end_of_buf += payload_len; + pad = *end_of_buf % AES_BLOCK_SIZE; + if (pad != 0) + pad = AES_BLOCK_SIZE - pad; + memset(&buf[*end_of_buf], 0, pad); + *end_of_buf += pad; +} + +/******************* +* AES +*******************/ +///////////////// +// KEY EXPANSION +///////////////// + +// Substitutes a word using the AES S-Box. +UINT SubWord(UINT word) +{ + unsigned int result; + + result = (int)aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F]; + result += (int)aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8; + result += (int)aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16; + result += (int)aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24; + return(result); +} + +// Performs the action of generating the keys that will be used in every round of +// encryption. "key" is the user-supplied input key, "w" is the output key schedule, +// "keysize" is the length in bits of "key", must be 128, 192, or 256. +void aes_key_setup(const BYTE key[], UINT w[], int keysize) +{ + int Nb=4,Nr,Nk,idx; + UINT temp,Rcon[]={0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000, + 0x40000000,0x80000000,0x1b000000,0x36000000,0x6c000000,0xd8000000, + 0xab000000,0x4d000000,0x9a000000}; + + switch (keysize) { + case 128: Nr = 10; Nk = 4; break; + case 192: Nr = 12; Nk = 6; break; + case 256: Nr = 14; Nk = 8; break; + default: return; + } + + for (idx=0; idx < Nk; ++idx) { + w[idx] = ((key[4 * idx]) << 24) | ((key[4 * idx + 1]) << 16) | + ((key[4 * idx + 2]) << 8) | ((key[4 * idx + 3])); + } + + for (idx = Nk; idx < Nb * (Nr+1); ++idx) { + temp = w[idx - 1]; + if ((idx % Nk) == 0) + temp = SubWord(KE_ROTWORD(temp)) ^ Rcon[(idx-1)/Nk]; + else if (Nk > 6 && (idx % Nk) == 4) + temp = SubWord(temp); + w[idx] = w[idx-Nk] ^ temp; + } +} + +///////////////// +// ADD ROUND KEY +///////////////// + +// Performs the AddRoundKey step. Each round has its own pre-generated 16-byte key in the +// form of 4 integers (the "w" array). Each integer is XOR'd by one column of the state. +// Also performs the job of InvAddRoundKey(); since the function is a simple XOR process, +// it is its own inverse. +void AddRoundKey(BYTE state[][4], const UINT w[]) +{ + BYTE subkey[4]; + + // memcpy(subkey,&w[idx],4); // Not accurate for big endian machines + // Subkey 1 + subkey[0] = w[0] >> 24; + subkey[1] = w[0] >> 16; + subkey[2] = w[0] >> 8; + subkey[3] = w[0]; + state[0][0] ^= subkey[0]; + state[1][0] ^= subkey[1]; + state[2][0] ^= subkey[2]; + state[3][0] ^= subkey[3]; + // Subkey 2 + subkey[0] = w[1] >> 24; + subkey[1] = w[1] >> 16; + subkey[2] = w[1] >> 8; + subkey[3] = w[1]; + state[0][1] ^= subkey[0]; + state[1][1] ^= subkey[1]; + state[2][1] ^= subkey[2]; + state[3][1] ^= subkey[3]; + // Subkey 3 + subkey[0] = w[2] >> 24; + subkey[1] = w[2] >> 16; + subkey[2] = w[2] >> 8; + subkey[3] = w[2]; + state[0][2] ^= subkey[0]; + state[1][2] ^= subkey[1]; + state[2][2] ^= subkey[2]; + state[3][2] ^= subkey[3]; + // Subkey 4 + subkey[0] = w[3] >> 24; + subkey[1] = w[3] >> 16; + subkey[2] = w[3] >> 8; + subkey[3] = w[3]; + state[0][3] ^= subkey[0]; + state[1][3] ^= subkey[1]; + state[2][3] ^= subkey[2]; + state[3][3] ^= subkey[3]; +} + +///////////////// +// (Inv)SubBytes +///////////////// + +// Performs the SubBytes step. All bytes in the state are substituted with a +// pre-calculated value from a lookup table. +void SubBytes(BYTE state[][4]) +{ + state[0][0] = aes_sbox[state[0][0] >> 4][state[0][0] & 0x0F]; + state[0][1] = aes_sbox[state[0][1] >> 4][state[0][1] & 0x0F]; + state[0][2] = aes_sbox[state[0][2] >> 4][state[0][2] & 0x0F]; + state[0][3] = aes_sbox[state[0][3] >> 4][state[0][3] & 0x0F]; + state[1][0] = aes_sbox[state[1][0] >> 4][state[1][0] & 0x0F]; + state[1][1] = aes_sbox[state[1][1] >> 4][state[1][1] & 0x0F]; + state[1][2] = aes_sbox[state[1][2] >> 4][state[1][2] & 0x0F]; + state[1][3] = aes_sbox[state[1][3] >> 4][state[1][3] & 0x0F]; + state[2][0] = aes_sbox[state[2][0] >> 4][state[2][0] & 0x0F]; + state[2][1] = aes_sbox[state[2][1] >> 4][state[2][1] & 0x0F]; + state[2][2] = aes_sbox[state[2][2] >> 4][state[2][2] & 0x0F]; + state[2][3] = aes_sbox[state[2][3] >> 4][state[2][3] & 0x0F]; + state[3][0] = aes_sbox[state[3][0] >> 4][state[3][0] & 0x0F]; + state[3][1] = aes_sbox[state[3][1] >> 4][state[3][1] & 0x0F]; + state[3][2] = aes_sbox[state[3][2] >> 4][state[3][2] & 0x0F]; + state[3][3] = aes_sbox[state[3][3] >> 4][state[3][3] & 0x0F]; +} + +void InvSubBytes(BYTE state[][4]) +{ + state[0][0] = aes_invsbox[state[0][0] >> 4][state[0][0] & 0x0F]; + state[0][1] = aes_invsbox[state[0][1] >> 4][state[0][1] & 0x0F]; + state[0][2] = aes_invsbox[state[0][2] >> 4][state[0][2] & 0x0F]; + state[0][3] = aes_invsbox[state[0][3] >> 4][state[0][3] & 0x0F]; + state[1][0] = aes_invsbox[state[1][0] >> 4][state[1][0] & 0x0F]; + state[1][1] = aes_invsbox[state[1][1] >> 4][state[1][1] & 0x0F]; + state[1][2] = aes_invsbox[state[1][2] >> 4][state[1][2] & 0x0F]; + state[1][3] = aes_invsbox[state[1][3] >> 4][state[1][3] & 0x0F]; + state[2][0] = aes_invsbox[state[2][0] >> 4][state[2][0] & 0x0F]; + state[2][1] = aes_invsbox[state[2][1] >> 4][state[2][1] & 0x0F]; + state[2][2] = aes_invsbox[state[2][2] >> 4][state[2][2] & 0x0F]; + state[2][3] = aes_invsbox[state[2][3] >> 4][state[2][3] & 0x0F]; + state[3][0] = aes_invsbox[state[3][0] >> 4][state[3][0] & 0x0F]; + state[3][1] = aes_invsbox[state[3][1] >> 4][state[3][1] & 0x0F]; + state[3][2] = aes_invsbox[state[3][2] >> 4][state[3][2] & 0x0F]; + state[3][3] = aes_invsbox[state[3][3] >> 4][state[3][3] & 0x0F]; +} + +///////////////// +// (Inv)ShiftRows +///////////////// + +// Performs the ShiftRows step. All rows are shifted cylindrically to the left. +void ShiftRows(BYTE state[][4]) +{ + int t; + + // Shift left by 1 + t = state[1][0]; + state[1][0] = state[1][1]; + state[1][1] = state[1][2]; + state[1][2] = state[1][3]; + state[1][3] = t; + // Shift left by 2 + t = state[2][0]; + state[2][0] = state[2][2]; + state[2][2] = t; + t = state[2][1]; + state[2][1] = state[2][3]; + state[2][3] = t; + // Shift left by 3 + t = state[3][0]; + state[3][0] = state[3][3]; + state[3][3] = state[3][2]; + state[3][2] = state[3][1]; + state[3][1] = t; +} + +// All rows are shifted cylindrically to the right. +void InvShiftRows(BYTE state[][4]) +{ + int t; + + // Shift right by 1 + t = state[1][3]; + state[1][3] = state[1][2]; + state[1][2] = state[1][1]; + state[1][1] = state[1][0]; + state[1][0] = t; + // Shift right by 2 + t = state[2][3]; + state[2][3] = state[2][1]; + state[2][1] = t; + t = state[2][2]; + state[2][2] = state[2][0]; + state[2][0] = t; + // Shift right by 3 + t = state[3][3]; + state[3][3] = state[3][0]; + state[3][0] = state[3][1]; + state[3][1] = state[3][2]; + state[3][2] = t; +} + +///////////////// +// (Inv)MixColumns +///////////////// + +// Performs the MixColums step. The state is multiplied by itself using matrix +// multiplication in a Galios Field 2^8. All multiplication is pre-computed in a table. +// Addition is equivilent to XOR. (Must always make a copy of the column as the original +// values will be destoyed.) +void MixColumns(BYTE state[][4]) +{ + BYTE col[4]; + + // Column 1 + col[0] = state[0][0]; + col[1] = state[1][0]; + col[2] = state[2][0]; + col[3] = state[3][0]; + state[0][0] = gf_mul[col[0]][0]; + state[0][0] ^= gf_mul[col[1]][1]; + state[0][0] ^= col[2]; + state[0][0] ^= col[3]; + state[1][0] = col[0]; + state[1][0] ^= gf_mul[col[1]][0]; + state[1][0] ^= gf_mul[col[2]][1]; + state[1][0] ^= col[3]; + state[2][0] = col[0]; + state[2][0] ^= col[1]; + state[2][0] ^= gf_mul[col[2]][0]; + state[2][0] ^= gf_mul[col[3]][1]; + state[3][0] = gf_mul[col[0]][1]; + state[3][0] ^= col[1]; + state[3][0] ^= col[2]; + state[3][0] ^= gf_mul[col[3]][0]; + // Column 2 + col[0] = state[0][1]; + col[1] = state[1][1]; + col[2] = state[2][1]; + col[3] = state[3][1]; + state[0][1] = gf_mul[col[0]][0]; + state[0][1] ^= gf_mul[col[1]][1]; + state[0][1] ^= col[2]; + state[0][1] ^= col[3]; + state[1][1] = col[0]; + state[1][1] ^= gf_mul[col[1]][0]; + state[1][1] ^= gf_mul[col[2]][1]; + state[1][1] ^= col[3]; + state[2][1] = col[0]; + state[2][1] ^= col[1]; + state[2][1] ^= gf_mul[col[2]][0]; + state[2][1] ^= gf_mul[col[3]][1]; + state[3][1] = gf_mul[col[0]][1]; + state[3][1] ^= col[1]; + state[3][1] ^= col[2]; + state[3][1] ^= gf_mul[col[3]][0]; + // Column 3 + col[0] = state[0][2]; + col[1] = state[1][2]; + col[2] = state[2][2]; + col[3] = state[3][2]; + state[0][2] = gf_mul[col[0]][0]; + state[0][2] ^= gf_mul[col[1]][1]; + state[0][2] ^= col[2]; + state[0][2] ^= col[3]; + state[1][2] = col[0]; + state[1][2] ^= gf_mul[col[1]][0]; + state[1][2] ^= gf_mul[col[2]][1]; + state[1][2] ^= col[3]; + state[2][2] = col[0]; + state[2][2] ^= col[1]; + state[2][2] ^= gf_mul[col[2]][0]; + state[2][2] ^= gf_mul[col[3]][1]; + state[3][2] = gf_mul[col[0]][1]; + state[3][2] ^= col[1]; + state[3][2] ^= col[2]; + state[3][2] ^= gf_mul[col[3]][0]; + // Column 4 + col[0] = state[0][3]; + col[1] = state[1][3]; + col[2] = state[2][3]; + col[3] = state[3][3]; + state[0][3] = gf_mul[col[0]][0]; + state[0][3] ^= gf_mul[col[1]][1]; + state[0][3] ^= col[2]; + state[0][3] ^= col[3]; + state[1][3] = col[0]; + state[1][3] ^= gf_mul[col[1]][0]; + state[1][3] ^= gf_mul[col[2]][1]; + state[1][3] ^= col[3]; + state[2][3] = col[0]; + state[2][3] ^= col[1]; + state[2][3] ^= gf_mul[col[2]][0]; + state[2][3] ^= gf_mul[col[3]][1]; + state[3][3] = gf_mul[col[0]][1]; + state[3][3] ^= col[1]; + state[3][3] ^= col[2]; + state[3][3] ^= gf_mul[col[3]][0]; +} + +void InvMixColumns(BYTE state[][4]) +{ + BYTE col[4]; + + // Column 1 + col[0] = state[0][0]; + col[1] = state[1][0]; + col[2] = state[2][0]; + col[3] = state[3][0]; + state[0][0] = gf_mul[col[0]][5]; + state[0][0] ^= gf_mul[col[1]][3]; + state[0][0] ^= gf_mul[col[2]][4]; + state[0][0] ^= gf_mul[col[3]][2]; + state[1][0] = gf_mul[col[0]][2]; + state[1][0] ^= gf_mul[col[1]][5]; + state[1][0] ^= gf_mul[col[2]][3]; + state[1][0] ^= gf_mul[col[3]][4]; + state[2][0] = gf_mul[col[0]][4]; + state[2][0] ^= gf_mul[col[1]][2]; + state[2][0] ^= gf_mul[col[2]][5]; + state[2][0] ^= gf_mul[col[3]][3]; + state[3][0] = gf_mul[col[0]][3]; + state[3][0] ^= gf_mul[col[1]][4]; + state[3][0] ^= gf_mul[col[2]][2]; + state[3][0] ^= gf_mul[col[3]][5]; + // Column 2 + col[0] = state[0][1]; + col[1] = state[1][1]; + col[2] = state[2][1]; + col[3] = state[3][1]; + state[0][1] = gf_mul[col[0]][5]; + state[0][1] ^= gf_mul[col[1]][3]; + state[0][1] ^= gf_mul[col[2]][4]; + state[0][1] ^= gf_mul[col[3]][2]; + state[1][1] = gf_mul[col[0]][2]; + state[1][1] ^= gf_mul[col[1]][5]; + state[1][1] ^= gf_mul[col[2]][3]; + state[1][1] ^= gf_mul[col[3]][4]; + state[2][1] = gf_mul[col[0]][4]; + state[2][1] ^= gf_mul[col[1]][2]; + state[2][1] ^= gf_mul[col[2]][5]; + state[2][1] ^= gf_mul[col[3]][3]; + state[3][1] = gf_mul[col[0]][3]; + state[3][1] ^= gf_mul[col[1]][4]; + state[3][1] ^= gf_mul[col[2]][2]; + state[3][1] ^= gf_mul[col[3]][5]; + // Column 3 + col[0] = state[0][2]; + col[1] = state[1][2]; + col[2] = state[2][2]; + col[3] = state[3][2]; + state[0][2] = gf_mul[col[0]][5]; + state[0][2] ^= gf_mul[col[1]][3]; + state[0][2] ^= gf_mul[col[2]][4]; + state[0][2] ^= gf_mul[col[3]][2]; + state[1][2] = gf_mul[col[0]][2]; + state[1][2] ^= gf_mul[col[1]][5]; + state[1][2] ^= gf_mul[col[2]][3]; + state[1][2] ^= gf_mul[col[3]][4]; + state[2][2] = gf_mul[col[0]][4]; + state[2][2] ^= gf_mul[col[1]][2]; + state[2][2] ^= gf_mul[col[2]][5]; + state[2][2] ^= gf_mul[col[3]][3]; + state[3][2] = gf_mul[col[0]][3]; + state[3][2] ^= gf_mul[col[1]][4]; + state[3][2] ^= gf_mul[col[2]][2]; + state[3][2] ^= gf_mul[col[3]][5]; + // Column 4 + col[0] = state[0][3]; + col[1] = state[1][3]; + col[2] = state[2][3]; + col[3] = state[3][3]; + state[0][3] = gf_mul[col[0]][5]; + state[0][3] ^= gf_mul[col[1]][3]; + state[0][3] ^= gf_mul[col[2]][4]; + state[0][3] ^= gf_mul[col[3]][2]; + state[1][3] = gf_mul[col[0]][2]; + state[1][3] ^= gf_mul[col[1]][5]; + state[1][3] ^= gf_mul[col[2]][3]; + state[1][3] ^= gf_mul[col[3]][4]; + state[2][3] = gf_mul[col[0]][4]; + state[2][3] ^= gf_mul[col[1]][2]; + state[2][3] ^= gf_mul[col[2]][5]; + state[2][3] ^= gf_mul[col[3]][3]; + state[3][3] = gf_mul[col[0]][3]; + state[3][3] ^= gf_mul[col[1]][4]; + state[3][3] ^= gf_mul[col[2]][2]; + state[3][3] ^= gf_mul[col[3]][5]; +} + +///////////////// +// (En/De)Crypt +///////////////// + +void aes_encrypt(const BYTE in[], BYTE out[], const UINT key[], int keysize) +{ + BYTE state[4][4]; + + // Copy input array (should be 16 bytes long) to a matrix (sequential bytes are ordered + // by row, not col) called "state" for processing. + // *** Implementation note: The official AES documentation references the state by + // column, then row. Accessing an element in C requires row then column. Thus, all state + // references in AES must have the column and row indexes reversed for C implementation. + state[0][0] = in[0]; + state[1][0] = in[1]; + state[2][0] = in[2]; + state[3][0] = in[3]; + state[0][1] = in[4]; + state[1][1] = in[5]; + state[2][1] = in[6]; + state[3][1] = in[7]; + state[0][2] = in[8]; + state[1][2] = in[9]; + state[2][2] = in[10]; + state[3][2] = in[11]; + state[0][3] = in[12]; + state[1][3] = in[13]; + state[2][3] = in[14]; + state[3][3] = in[15]; + + // Perform the necessary number of rounds. The round key is added first. + // The last round does not perform the MixColumns step. + AddRoundKey(state,&key[0]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[4]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[8]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[12]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[16]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[20]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[24]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[28]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[32]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[36]); + if (keysize != 128) { + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[40]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[44]); + if (keysize != 192) { + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[48]); + SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[52]); + SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[56]); + } + else { + SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[48]); + } + } + else { + SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[40]); + } + + // Copy the state to the output array. + out[0] = state[0][0]; + out[1] = state[1][0]; + out[2] = state[2][0]; + out[3] = state[3][0]; + out[4] = state[0][1]; + out[5] = state[1][1]; + out[6] = state[2][1]; + out[7] = state[3][1]; + out[8] = state[0][2]; + out[9] = state[1][2]; + out[10] = state[2][2]; + out[11] = state[3][2]; + out[12] = state[0][3]; + out[13] = state[1][3]; + out[14] = state[2][3]; + out[15] = state[3][3]; +} + +void aes_decrypt(const BYTE in[], BYTE out[], const UINT key[], int keysize) +{ + BYTE state[4][4]; + + // Copy the input to the state. + state[0][0] = in[0]; + state[1][0] = in[1]; + state[2][0] = in[2]; + state[3][0] = in[3]; + state[0][1] = in[4]; + state[1][1] = in[5]; + state[2][1] = in[6]; + state[3][1] = in[7]; + state[0][2] = in[8]; + state[1][2] = in[9]; + state[2][2] = in[10]; + state[3][2] = in[11]; + state[0][3] = in[12]; + state[1][3] = in[13]; + state[2][3] = in[14]; + state[3][3] = in[15]; + + // Perform the necessary number of rounds. The round key is added first. + // The last round does not perform the MixColumns step. + if (keysize > 128) { + if (keysize > 192) { + AddRoundKey(state,&key[56]); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[52]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[48]);InvMixColumns(state); + } + else { + AddRoundKey(state,&key[48]); + } + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[44]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[40]);InvMixColumns(state); + } + else { + AddRoundKey(state,&key[40]); + } + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[36]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[32]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[28]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[24]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[20]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[16]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[12]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[8]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[4]);InvMixColumns(state); + InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[0]); + + // Copy the state to the output array. + out[0] = state[0][0]; + out[1] = state[1][0]; + out[2] = state[2][0]; + out[3] = state[3][0]; + out[4] = state[0][1]; + out[5] = state[1][1]; + out[6] = state[2][1]; + out[7] = state[3][1]; + out[8] = state[0][2]; + out[9] = state[1][2]; + out[10] = state[2][2]; + out[11] = state[3][2]; + out[12] = state[0][3]; + out[13] = state[1][3]; + out[14] = state[2][3]; + out[15] = state[3][3]; +} + +// -------------------------------------------------- DES -------------------------------------------------- // + +/****************************** MACROS ******************************/ +// Obtain bit "b" from the left and shift it "c" places from the right +#define BITNUM(a,b,c) (((a[(b)/8] >> (7 - (b%8))) & 0x01) << (c)) +#define BITNUMINTR(a,b,c) ((((a) >> (31 - (b))) & 0x00000001) << (c)) +#define BITNUMINTL(a,b,c) ((((a) << (b)) & 0x80000000) >> (c)) + +// This macro converts a 6 bit block with the S-Box row defined as the first and last +// bits to a 6 bit block with the row defined by the first two bits. +#define SBOXBIT(a) (((a) & 0x20) | (((a) & 0x1f) >> 1) | (((a) & 0x01) << 4)) + +/**************************** VARIABLES *****************************/ +static const BYTE sbox1[64] = { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 +}; +static const BYTE sbox2[64] = { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 +}; +static const BYTE sbox3[64] = { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 +}; +static const BYTE sbox4[64] = { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 +}; +static const BYTE sbox5[64] = { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 +}; +static const BYTE sbox6[64] = { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 +}; +static const BYTE sbox7[64] = { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 +}; +static const BYTE sbox8[64] = { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +// Initial (Inv)Permutation step +void IP(UINT state[], const BYTE in[]) +{ + state[0] = BITNUM(in,57,31) | BITNUM(in,49,30) | BITNUM(in,41,29) | BITNUM(in,33,28) | + BITNUM(in,25,27) | BITNUM(in,17,26) | BITNUM(in,9,25) | BITNUM(in,1,24) | + BITNUM(in,59,23) | BITNUM(in,51,22) | BITNUM(in,43,21) | BITNUM(in,35,20) | + BITNUM(in,27,19) | BITNUM(in,19,18) | BITNUM(in,11,17) | BITNUM(in,3,16) | + BITNUM(in,61,15) | BITNUM(in,53,14) | BITNUM(in,45,13) | BITNUM(in,37,12) | + BITNUM(in,29,11) | BITNUM(in,21,10) | BITNUM(in,13,9) | BITNUM(in,5,8) | + BITNUM(in,63,7) | BITNUM(in,55,6) | BITNUM(in,47,5) | BITNUM(in,39,4) | + BITNUM(in,31,3) | BITNUM(in,23,2) | BITNUM(in,15,1) | BITNUM(in,7,0); + + state[1] = BITNUM(in,56,31) | BITNUM(in,48,30) | BITNUM(in,40,29) | BITNUM(in,32,28) | + BITNUM(in,24,27) | BITNUM(in,16,26) | BITNUM(in,8,25) | BITNUM(in,0,24) | + BITNUM(in,58,23) | BITNUM(in,50,22) | BITNUM(in,42,21) | BITNUM(in,34,20) | + BITNUM(in,26,19) | BITNUM(in,18,18) | BITNUM(in,10,17) | BITNUM(in,2,16) | + BITNUM(in,60,15) | BITNUM(in,52,14) | BITNUM(in,44,13) | BITNUM(in,36,12) | + BITNUM(in,28,11) | BITNUM(in,20,10) | BITNUM(in,12,9) | BITNUM(in,4,8) | + BITNUM(in,62,7) | BITNUM(in,54,6) | BITNUM(in,46,5) | BITNUM(in,38,4) | + BITNUM(in,30,3) | BITNUM(in,22,2) | BITNUM(in,14,1) | BITNUM(in,6,0); +} + +void InvIP(UINT state[], BYTE in[]) +{ + in[0] = BITNUMINTR(state[1],7,7) | BITNUMINTR(state[0],7,6) | BITNUMINTR(state[1],15,5) | + BITNUMINTR(state[0],15,4) | BITNUMINTR(state[1],23,3) | BITNUMINTR(state[0],23,2) | + BITNUMINTR(state[1],31,1) | BITNUMINTR(state[0],31,0); + + in[1] = BITNUMINTR(state[1],6,7) | BITNUMINTR(state[0],6,6) | BITNUMINTR(state[1],14,5) | + BITNUMINTR(state[0],14,4) | BITNUMINTR(state[1],22,3) | BITNUMINTR(state[0],22,2) | + BITNUMINTR(state[1],30,1) | BITNUMINTR(state[0],30,0); + + in[2] = BITNUMINTR(state[1],5,7) | BITNUMINTR(state[0],5,6) | BITNUMINTR(state[1],13,5) | + BITNUMINTR(state[0],13,4) | BITNUMINTR(state[1],21,3) | BITNUMINTR(state[0],21,2) | + BITNUMINTR(state[1],29,1) | BITNUMINTR(state[0],29,0); + + in[3] = BITNUMINTR(state[1],4,7) | BITNUMINTR(state[0],4,6) | BITNUMINTR(state[1],12,5) | + BITNUMINTR(state[0],12,4) | BITNUMINTR(state[1],20,3) | BITNUMINTR(state[0],20,2) | + BITNUMINTR(state[1],28,1) | BITNUMINTR(state[0],28,0); + + in[4] = BITNUMINTR(state[1],3,7) | BITNUMINTR(state[0],3,6) | BITNUMINTR(state[1],11,5) | + BITNUMINTR(state[0],11,4) | BITNUMINTR(state[1],19,3) | BITNUMINTR(state[0],19,2) | + BITNUMINTR(state[1],27,1) | BITNUMINTR(state[0],27,0); + + in[5] = BITNUMINTR(state[1],2,7) | BITNUMINTR(state[0],2,6) | BITNUMINTR(state[1],10,5) | + BITNUMINTR(state[0],10,4) | BITNUMINTR(state[1],18,3) | BITNUMINTR(state[0],18,2) | + BITNUMINTR(state[1],26,1) | BITNUMINTR(state[0],26,0); + + in[6] = BITNUMINTR(state[1],1,7) | BITNUMINTR(state[0],1,6) | BITNUMINTR(state[1],9,5) | + BITNUMINTR(state[0],9,4) | BITNUMINTR(state[1],17,3) | BITNUMINTR(state[0],17,2) | + BITNUMINTR(state[1],25,1) | BITNUMINTR(state[0],25,0); + + in[7] = BITNUMINTR(state[1],0,7) | BITNUMINTR(state[0],0,6) | BITNUMINTR(state[1],8,5) | + BITNUMINTR(state[0],8,4) | BITNUMINTR(state[1],16,3) | BITNUMINTR(state[0],16,2) | + BITNUMINTR(state[1],24,1) | BITNUMINTR(state[0],24,0); +} + +UINT f(UINT state, const BYTE key[]) +{ + BYTE lrgstate[6]; //,i; + UINT t1,t2; + + // Expantion Permutation + t1 = BITNUMINTL(state,31,0) | ((state & 0xf0000000) >> 1) | BITNUMINTL(state,4,5) | + BITNUMINTL(state,3,6) | ((state & 0x0f000000) >> 3) | BITNUMINTL(state,8,11) | + BITNUMINTL(state,7,12) | ((state & 0x00f00000) >> 5) | BITNUMINTL(state,12,17) | + BITNUMINTL(state,11,18) | ((state & 0x000f0000) >> 7) | BITNUMINTL(state,16,23); + + t2 = BITNUMINTL(state,15,0) | ((state & 0x0000f000) << 15) | BITNUMINTL(state,20,5) | + BITNUMINTL(state,19,6) | ((state & 0x00000f00) << 13) | BITNUMINTL(state,24,11) | + BITNUMINTL(state,23,12) | ((state & 0x000000f0) << 11) | BITNUMINTL(state,28,17) | + BITNUMINTL(state,27,18) | ((state & 0x0000000f) << 9) | BITNUMINTL(state,0,23); + + lrgstate[0] = (t1 >> 24) & 0x000000ff; + lrgstate[1] = (t1 >> 16) & 0x000000ff; + lrgstate[2] = (t1 >> 8) & 0x000000ff; + lrgstate[3] = (t2 >> 24) & 0x000000ff; + lrgstate[4] = (t2 >> 16) & 0x000000ff; + lrgstate[5] = (t2 >> 8) & 0x000000ff; + + // Key XOR + lrgstate[0] ^= key[0]; + lrgstate[1] ^= key[1]; + lrgstate[2] ^= key[2]; + lrgstate[3] ^= key[3]; + lrgstate[4] ^= key[4]; + lrgstate[5] ^= key[5]; + + // S-Box Permutation + state = (sbox1[SBOXBIT(lrgstate[0] >> 2)] << 28) | + (sbox2[SBOXBIT(((lrgstate[0] & 0x03) << 4) | (lrgstate[1] >> 4))] << 24) | + (sbox3[SBOXBIT(((lrgstate[1] & 0x0f) << 2) | (lrgstate[2] >> 6))] << 20) | + (sbox4[SBOXBIT(lrgstate[2] & 0x3f)] << 16) | + (sbox5[SBOXBIT(lrgstate[3] >> 2)] << 12) | + (sbox6[SBOXBIT(((lrgstate[3] & 0x03) << 4) | (lrgstate[4] >> 4))] << 8) | + (sbox7[SBOXBIT(((lrgstate[4] & 0x0f) << 2) | (lrgstate[5] >> 6))] << 4) | + sbox8[SBOXBIT(lrgstate[5] & 0x3f)]; + + // P-Box Permutation + state = BITNUMINTL(state,15,0) | BITNUMINTL(state,6,1) | BITNUMINTL(state,19,2) | + BITNUMINTL(state,20,3) | BITNUMINTL(state,28,4) | BITNUMINTL(state,11,5) | + BITNUMINTL(state,27,6) | BITNUMINTL(state,16,7) | BITNUMINTL(state,0,8) | + BITNUMINTL(state,14,9) | BITNUMINTL(state,22,10) | BITNUMINTL(state,25,11) | + BITNUMINTL(state,4,12) | BITNUMINTL(state,17,13) | BITNUMINTL(state,30,14) | + BITNUMINTL(state,9,15) | BITNUMINTL(state,1,16) | BITNUMINTL(state,7,17) | + BITNUMINTL(state,23,18) | BITNUMINTL(state,13,19) | BITNUMINTL(state,31,20) | + BITNUMINTL(state,26,21) | BITNUMINTL(state,2,22) | BITNUMINTL(state,8,23) | + BITNUMINTL(state,18,24) | BITNUMINTL(state,12,25) | BITNUMINTL(state,29,26) | + BITNUMINTL(state,5,27) | BITNUMINTL(state,21,28) | BITNUMINTL(state,10,29) | + BITNUMINTL(state,3,30) | BITNUMINTL(state,24,31); + + // Return the final state value + return(state); +} + +void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode) +{ + UINT i, j, to_gen, C, D; + const UINT key_rnd_shift[16] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1}; + const UINT key_perm_c[28] = {56,48,40,32,24,16,8,0,57,49,41,33,25,17, + 9,1,58,50,42,34,26,18,10,2,59,51,43,35}; + const UINT key_perm_d[28] = {62,54,46,38,30,22,14,6,61,53,45,37,29,21, + 13,5,60,52,44,36,28,20,12,4,27,19,11,3}; + const UINT key_compression[48] = {13,16,10,23,0,4,2,27,14,5,20,9, + 22,18,11,3,25,7,15,6,26,19,12,1, + 40,51,30,36,46,54,29,39,50,44,32,47, + 43,48,38,55,33,52,45,41,49,35,28,31}; + + // Permutated Choice #1 (copy the key in, ignoring parity bits). + for (i = 0, j = 31, C = 0; i < 28; ++i, --j) + C |= BITNUM(key,key_perm_c[i],j); + for (i = 0, j = 31, D = 0; i < 28; ++i, --j) + D |= BITNUM(key,key_perm_d[i],j); + + // Generate the 16 subkeys. + for (i = 0; i < 16; ++i) { + C = ((C << key_rnd_shift[i]) | (C >> (28-key_rnd_shift[i]))) & 0xfffffff0; + D = ((D << key_rnd_shift[i]) | (D >> (28-key_rnd_shift[i]))) & 0xfffffff0; + + // Decryption subkeys are reverse order of encryption subkeys so + // generate them in reverse if the key schedule is for decryption useage. + if (mode == DES_DECRYPT) + to_gen = 15 - i; + else /*(if mode == DES_ENCRYPT)*/ + to_gen = i; + // Initialize the array + for (j = 0; j < 6; ++j) + schedule[to_gen][j] = 0; + for (j = 0; j < 24; ++j) + schedule[to_gen][j/8] |= BITNUMINTR(C,key_compression[j],7 - (j%8)); + for ( ; j < 48; ++j) + schedule[to_gen][j/8] |= BITNUMINTR(D,key_compression[j] - 28,7 - (j%8)); + } +} + +void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]) +{ + UINT state[2],idx,t; + + IP(state,in); + + for (idx=0; idx < 15; ++idx) { + t = state[1]; + state[1] = f(state[1],key[idx]) ^ state[0]; + state[0] = t; + } + // Perform the final loop manually as it doesn't switch sides + state[0] = f(state[1],key[15]) ^ state[0]; + + InvIP(state,out); +} + +void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode) +{ + if (mode == DES_ENCRYPT) { + des_key_setup(&key[0],schedule[0],mode); + des_key_setup(&key[8],schedule[1],(DES_MODE)(!mode)); + des_key_setup(&key[16],schedule[2],mode); + } + else /*if (mode == DES_DECRYPT*/ { + des_key_setup(&key[16],schedule[0],mode); + des_key_setup(&key[8],schedule[1],(DES_MODE)(!mode)); + des_key_setup(&key[0],schedule[2],mode); + } +} + +void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]) +{ + des_crypt(in,out,key[0]); + des_crypt(out,out,key[1]); + des_crypt(out,out,key[2]); +} + +// -------------------------------------------------- MD2 -------------------------------------------------- // + +/**************************** VARIABLES *****************************/ +static const BYTE s[256] = { + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void md2_transform(_MD2_CTX *ctx, BYTE data[]) +{ + int j,k,t; + + //memcpy(&ctx->state[16], data); + for (j=0; j < 16; ++j) { + ctx->state[j + 16] = data[j]; + ctx->state[j + 32] = (ctx->state[j+16] ^ ctx->state[j]); + } + + t = 0; + for (j = 0; j < 18; ++j) { + for (k = 0; k < 48; ++k) { + ctx->state[k] ^= s[t]; + t = ctx->state[k]; + } + t = (t+j) & 0xFF; + } + + t = ctx->checksum[15]; + for (j=0; j < 16; ++j) { + ctx->checksum[j] ^= s[data[j] ^ t]; + t = ctx->checksum[j]; + } +} + +void md2_init(_MD2_CTX *ctx) +{ + int i; + + for (i=0; i < 48; ++i) + ctx->state[i] = 0; + for (i=0; i < 16; ++i) + ctx->checksum[i] = 0; + ctx->len = 0; +} + +void md2_update(_MD2_CTX *ctx, const BYTE data[], size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->len] = data[i]; + ctx->len++; + if (ctx->len == MD2_BLOCK_SIZE) { + md2_transform(ctx, ctx->data); + ctx->len = 0; + } + } +} + +void md2_final(_MD2_CTX *ctx, BYTE hash[]) +{ + int to_pad; + + to_pad = MD2_BLOCK_SIZE - ctx->len; + + while (ctx->len < MD2_BLOCK_SIZE) + ctx->data[ctx->len++] = to_pad; + + md2_transform(ctx, ctx->data); + md2_transform(ctx, ctx->checksum); + + memcpy(hash, ctx->state, MD2_BLOCK_SIZE); +} + +// -------------------------------------------------- MD5 -------------------------------------------------- // + +/****************************** MACROS ******************************/ + +#define F(x,y,z) ((x & y) | (~x & z)) +#define G(x,y,z) ((x & z) | (y & ~z)) +#define H(x,y,z) (x ^ y ^ z) +#define I(x,y,z) (y ^ (x | ~z)) + +#define FF(a,b,c,d,m,s,t) { a += F(b,c,d) + m + t; \ + a = b + ROTLEFT(a,s); } +#define GG(a,b,c,d,m,s,t) { a += G(b,c,d) + m + t; \ + a = b + ROTLEFT(a,s); } +#define HH(a,b,c,d,m,s,t) { a += H(b,c,d) + m + t; \ + a = b + ROTLEFT(a,s); } +#define II(a,b,c,d,m,s,t) { a += I(b,c,d) + m + t; \ + a = b + ROTLEFT(a,s); } + +/*********************** FUNCTION DEFINITIONS ***********************/ +void md5_transform(_MD5_CTX *ctx, const BYTE data[]) +{ + UINT a, b, c, d, m[16], i, j; + + // MD5 specifies big endian byte order, but this implementation assumes a little + // endian byte order CPU. Reverse all the bytes upon input, and re-reverse them + // on output (in md5_final()). + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j]) + (data[j + 1] << 8) + (data[j + 2] << 16) + (data[j + 3] << 24); + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + + FF(a,b,c,d,m[0], 7,0xd76aa478); + FF(d,a,b,c,m[1], 12,0xe8c7b756); + FF(c,d,a,b,m[2], 17,0x242070db); + FF(b,c,d,a,m[3], 22,0xc1bdceee); + FF(a,b,c,d,m[4], 7,0xf57c0faf); + FF(d,a,b,c,m[5], 12,0x4787c62a); + FF(c,d,a,b,m[6], 17,0xa8304613); + FF(b,c,d,a,m[7], 22,0xfd469501); + FF(a,b,c,d,m[8], 7,0x698098d8); + FF(d,a,b,c,m[9], 12,0x8b44f7af); + FF(c,d,a,b,m[10],17,0xffff5bb1); + FF(b,c,d,a,m[11],22,0x895cd7be); + FF(a,b,c,d,m[12], 7,0x6b901122); + FF(d,a,b,c,m[13],12,0xfd987193); + FF(c,d,a,b,m[14],17,0xa679438e); + FF(b,c,d,a,m[15],22,0x49b40821); + + GG(a,b,c,d,m[1], 5,0xf61e2562); + GG(d,a,b,c,m[6], 9,0xc040b340); + GG(c,d,a,b,m[11],14,0x265e5a51); + GG(b,c,d,a,m[0], 20,0xe9b6c7aa); + GG(a,b,c,d,m[5], 5,0xd62f105d); + GG(d,a,b,c,m[10], 9,0x02441453); + GG(c,d,a,b,m[15],14,0xd8a1e681); + GG(b,c,d,a,m[4], 20,0xe7d3fbc8); + GG(a,b,c,d,m[9], 5,0x21e1cde6); + GG(d,a,b,c,m[14], 9,0xc33707d6); + GG(c,d,a,b,m[3], 14,0xf4d50d87); + GG(b,c,d,a,m[8], 20,0x455a14ed); + GG(a,b,c,d,m[13], 5,0xa9e3e905); + GG(d,a,b,c,m[2], 9,0xfcefa3f8); + GG(c,d,a,b,m[7], 14,0x676f02d9); + GG(b,c,d,a,m[12],20,0x8d2a4c8a); + + HH(a,b,c,d,m[5], 4,0xfffa3942); + HH(d,a,b,c,m[8], 11,0x8771f681); + HH(c,d,a,b,m[11],16,0x6d9d6122); + HH(b,c,d,a,m[14],23,0xfde5380c); + HH(a,b,c,d,m[1], 4,0xa4beea44); + HH(d,a,b,c,m[4], 11,0x4bdecfa9); + HH(c,d,a,b,m[7], 16,0xf6bb4b60); + HH(b,c,d,a,m[10],23,0xbebfbc70); + HH(a,b,c,d,m[13], 4,0x289b7ec6); + HH(d,a,b,c,m[0], 11,0xeaa127fa); + HH(c,d,a,b,m[3], 16,0xd4ef3085); + HH(b,c,d,a,m[6], 23,0x04881d05); + HH(a,b,c,d,m[9], 4,0xd9d4d039); + HH(d,a,b,c,m[12],11,0xe6db99e5); + HH(c,d,a,b,m[15],16,0x1fa27cf8); + HH(b,c,d,a,m[2], 23,0xc4ac5665); + + II(a,b,c,d,m[0], 6,0xf4292244); + II(d,a,b,c,m[7], 10,0x432aff97); + II(c,d,a,b,m[14],15,0xab9423a7); + II(b,c,d,a,m[5], 21,0xfc93a039); + II(a,b,c,d,m[12], 6,0x655b59c3); + II(d,a,b,c,m[3], 10,0x8f0ccc92); + II(c,d,a,b,m[10],15,0xffeff47d); + II(b,c,d,a,m[1], 21,0x85845dd1); + II(a,b,c,d,m[8], 6,0x6fa87e4f); + II(d,a,b,c,m[15],10,0xfe2ce6e0); + II(c,d,a,b,m[6], 15,0xa3014314); + II(b,c,d,a,m[13],21,0x4e0811a1); + II(a,b,c,d,m[4], 6,0xf7537e82); + II(d,a,b,c,m[11],10,0xbd3af235); + II(c,d,a,b,m[2], 15,0x2ad7d2bb); + II(b,c,d,a,m[9], 21,0xeb86d391); + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; +} + +void md5_init(_MD5_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void md5_update(_MD5_CTX *ctx, const BYTE data[], size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + md5_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void md5_final(_MD5_CTX *ctx, BYTE hash[]) +{ + size_t i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else if (ctx->datalen >= 56) { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + md5_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[56] = (BYTE)(ctx->bitlen); + ctx->data[57] = (BYTE)(ctx->bitlen >> 8); + ctx->data[58] = (BYTE)(ctx->bitlen >> 16); + ctx->data[59] = (BYTE)(ctx->bitlen >> 24); + ctx->data[60] = (BYTE)(ctx->bitlen >> 32); + ctx->data[61] = (BYTE)(ctx->bitlen >> 40); + ctx->data[62] = (BYTE)(ctx->bitlen >> 48); + ctx->data[63] = (BYTE)(ctx->bitlen >> 56); + md5_transform(ctx, ctx->data); + + // Since this implementation uses little endian byte ordering and MD uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (i * 8)) & 0x000000ff; + } +} + + +// -------------------------------------------------- SHA1 -------------------------------------------------- // + +/****************************** MACROS ******************************/ + + +/*********************** FUNCTION DEFINITIONS ***********************/ +void sha1_transform(_SHA1_CTX *ctx, const BYTE data[]) +{ + UINT a, b, c, d, e, i, j, t, m[80]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) + (data[j + 1] << 16) + (data[j + 2] << 8) + (data[j + 3]); + for ( ; i < 80; ++i) { + m[i] = (m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16]); + m[i] = (m[i] << 1) | (m[i] >> 31); + } + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + + for (i = 0; i < 20; ++i) { + t = ROTLEFT(a, 5) + ((b & c) ^ (~b & d)) + e + ctx->k[0] + m[i]; + e = d; + d = c; + c = ROTLEFT(b, 30); + b = a; + a = t; + } + for ( ; i < 40; ++i) { + t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[1] + m[i]; + e = d; + d = c; + c = ROTLEFT(b, 30); + b = a; + a = t; + } + for ( ; i < 60; ++i) { + t = ROTLEFT(a, 5) + ((b & c) ^ (b & d) ^ (c & d)) + e + ctx->k[2] + m[i]; + e = d; + d = c; + c = ROTLEFT(b, 30); + b = a; + a = t; + } + for ( ; i < 80; ++i) { + t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[3] + m[i]; + e = d; + d = c; + c = ROTLEFT(b, 30); + b = a; + a = t; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; +} + +void sha1_init(_SHA1_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xc3d2e1f0; + ctx->k[0] = 0x5a827999; + ctx->k[1] = 0x6ed9eba1; + ctx->k[2] = 0x8f1bbcdc; + ctx->k[3] = 0xca62c1d6; +} + +void sha1_update(_SHA1_CTX *ctx, const BYTE data[], size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha1_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha1_final(_SHA1_CTX *ctx, BYTE hash[]) +{ + UINT i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha1_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = (BYTE)(ctx->bitlen); + ctx->data[62] = (BYTE)(ctx->bitlen >> 8); + ctx->data[61] = (BYTE)(ctx->bitlen >> 16); + ctx->data[60] = (BYTE)(ctx->bitlen >> 24); + ctx->data[59] = (BYTE)(ctx->bitlen >> 32); + ctx->data[58] = (BYTE)(ctx->bitlen >> 40); + ctx->data[57] = (BYTE)(ctx->bitlen >> 48); + ctx->data[56] = (BYTE)(ctx->bitlen >> 56); + sha1_transform(ctx, ctx->data); + + // Since this implementation uses little endian byte ordering and MD uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + } +} + +// -------------------------------------------------- SHA256 -------------------------------------------------- // + +/****************************** MACROS ******************************/ + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +/**************************** VARIABLES *****************************/ +static const UINT k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void sha256_transform(_SHA256_CTX *ctx, const BYTE data[]) +{ + UINT a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(_SHA256_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(_SHA256_CTX *ctx, const BYTE data[], size_t len) +{ + UINT i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha256_final(_SHA256_CTX *ctx, BYTE hash[]) +{ + UINT i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = (BYTE)(ctx->bitlen); + ctx->data[62] = (BYTE)(ctx->bitlen >> 8); + ctx->data[61] = (BYTE)(ctx->bitlen >> 16); + ctx->data[60] = (BYTE)(ctx->bitlen >> 24); + ctx->data[59] = (BYTE)(ctx->bitlen >> 32); + ctx->data[58] = (BYTE)(ctx->bitlen >> 40); + ctx->data[57] = (BYTE)(ctx->bitlen >> 48); + ctx->data[56] = (BYTE)(ctx->bitlen >> 56); + sha256_transform(ctx, ctx->data); + + // Since this implementation uses little endian byte ordering and SHA uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} + +// -------------------------------------------------- ARCFOUR -------------------------------------------------- // + +/*********************** FUNCTION DEFINITIONS ***********************/ +void arcfour_key_setup(BYTE state[], const BYTE key[], int len) +{ + int i, j; + BYTE t; + + for (i = 0; i < 256; ++i) + state[i] = i; + for (i = 0, j = 0; i < 256; ++i) { + j = (j + state[i] + key[i % len]) % 256; + t = state[i]; + state[i] = state[j]; + state[j] = t; + } +} + +// This does not hold state between calls. It always generates the +// stream starting from the first output byte. +void arcfour_generate_stream(BYTE state[], BYTE out[], size_t len) +{ + int i, j; + size_t idx; + BYTE t; + + for (idx = 0, i = 0, j = 0; idx < len; ++idx) { + i = (i + 1) % 256; + j = (j + state[i]) % 256; + t = state[i]; + state[i] = state[j]; + state[j] = t; + out[idx] = state[(state[i] + state[j]) % 256]; + } +} + +// -------------------------------------------------- BLOWFISH -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define BF(x,t) t = keystruct->s[0][(x) >> 24]; \ + t += keystruct->s[1][((x) >> 16) & 0xff]; \ + t ^= keystruct->s[2][((x) >> 8) & 0xff]; \ + t += keystruct->s[3][(x) & 0xff]; +#define swap(r,l,t) t = l; l = r; r = t; +#define ITERATION(l,r,t,pval) l ^= keystruct->p[pval]; BF(l,t); r^= t; swap(r,l,t); + +/**************************** VARIABLES *****************************/ +static const UINT p_perm[18] = { + 0x243F6A88,0x85A308D3,0x13198A2E,0x03707344,0xA4093822,0x299F31D0,0x082EFA98, + 0xEC4E6C89,0x452821E6,0x38D01377,0xBE5466CF,0x34E90C6C,0xC0AC29B7,0xC97C50DD, + 0x3F84D5B5,0xB5470917,0x9216D5D9,0x8979FB1B +}; + +static const UINT s_perm[4][256] = { { + 0xD1310BA6,0x98DFB5AC,0x2FFD72DB,0xD01ADFB7,0xB8E1AFED,0x6A267E96,0xBA7C9045,0xF12C7F99, + 0x24A19947,0xB3916CF7,0x0801F2E2,0x858EFC16,0x636920D8,0x71574E69,0xA458FEA3,0xF4933D7E, + 0x0D95748F,0x728EB658,0x718BCD58,0x82154AEE,0x7B54A41D,0xC25A59B5,0x9C30D539,0x2AF26013, + 0xC5D1B023,0x286085F0,0xCA417918,0xB8DB38EF,0x8E79DCB0,0x603A180E,0x6C9E0E8B,0xB01E8A3E, + 0xD71577C1,0xBD314B27,0x78AF2FDA,0x55605C60,0xE65525F3,0xAA55AB94,0x57489862,0x63E81440, + 0x55CA396A,0x2AAB10B6,0xB4CC5C34,0x1141E8CE,0xA15486AF,0x7C72E993,0xB3EE1411,0x636FBC2A, + 0x2BA9C55D,0x741831F6,0xCE5C3E16,0x9B87931E,0xAFD6BA33,0x6C24CF5C,0x7A325381,0x28958677, + 0x3B8F4898,0x6B4BB9AF,0xC4BFE81B,0x66282193,0x61D809CC,0xFB21A991,0x487CAC60,0x5DEC8032, + 0xEF845D5D,0xE98575B1,0xDC262302,0xEB651B88,0x23893E81,0xD396ACC5,0x0F6D6FF3,0x83F44239, + 0x2E0B4482,0xA4842004,0x69C8F04A,0x9E1F9B5E,0x21C66842,0xF6E96C9A,0x670C9C61,0xABD388F0, + 0x6A51A0D2,0xD8542F68,0x960FA728,0xAB5133A3,0x6EEF0B6C,0x137A3BE4,0xBA3BF050,0x7EFB2A98, + 0xA1F1651D,0x39AF0176,0x66CA593E,0x82430E88,0x8CEE8619,0x456F9FB4,0x7D84A5C3,0x3B8B5EBE, + 0xE06F75D8,0x85C12073,0x401A449F,0x56C16AA6,0x4ED3AA62,0x363F7706,0x1BFEDF72,0x429B023D, + 0x37D0D724,0xD00A1248,0xDB0FEAD3,0x49F1C09B,0x075372C9,0x80991B7B,0x25D479D8,0xF6E8DEF7, + 0xE3FE501A,0xB6794C3B,0x976CE0BD,0x04C006BA,0xC1A94FB6,0x409F60C4,0x5E5C9EC2,0x196A2463, + 0x68FB6FAF,0x3E6C53B5,0x1339B2EB,0x3B52EC6F,0x6DFC511F,0x9B30952C,0xCC814544,0xAF5EBD09, + 0xBEE3D004,0xDE334AFD,0x660F2807,0x192E4BB3,0xC0CBA857,0x45C8740F,0xD20B5F39,0xB9D3FBDB, + 0x5579C0BD,0x1A60320A,0xD6A100C6,0x402C7279,0x679F25FE,0xFB1FA3CC,0x8EA5E9F8,0xDB3222F8, + 0x3C7516DF,0xFD616B15,0x2F501EC8,0xAD0552AB,0x323DB5FA,0xFD238760,0x53317B48,0x3E00DF82, + 0x9E5C57BB,0xCA6F8CA0,0x1A87562E,0xDF1769DB,0xD542A8F6,0x287EFFC3,0xAC6732C6,0x8C4F5573, + 0x695B27B0,0xBBCA58C8,0xE1FFA35D,0xB8F011A0,0x10FA3D98,0xFD2183B8,0x4AFCB56C,0x2DD1D35B, + 0x9A53E479,0xB6F84565,0xD28E49BC,0x4BFB9790,0xE1DDF2DA,0xA4CB7E33,0x62FB1341,0xCEE4C6E8, + 0xEF20CADA,0x36774C01,0xD07E9EFE,0x2BF11FB4,0x95DBDA4D,0xAE909198,0xEAAD8E71,0x6B93D5A0, + 0xD08ED1D0,0xAFC725E0,0x8E3C5B2F,0x8E7594B7,0x8FF6E2FB,0xF2122B64,0x8888B812,0x900DF01C, + 0x4FAD5EA0,0x688FC31C,0xD1CFF191,0xB3A8C1AD,0x2F2F2218,0xBE0E1777,0xEA752DFE,0x8B021FA1, + 0xE5A0CC0F,0xB56F74E8,0x18ACF3D6,0xCE89E299,0xB4A84FE0,0xFD13E0B7,0x7CC43B81,0xD2ADA8D9, + 0x165FA266,0x80957705,0x93CC7314,0x211A1477,0xE6AD2065,0x77B5FA86,0xC75442F5,0xFB9D35CF, + 0xEBCDAF0C,0x7B3E89A0,0xD6411BD3,0xAE1E7E49,0x00250E2D,0x2071B35E,0x226800BB,0x57B8E0AF, + 0x2464369B,0xF009B91E,0x5563911D,0x59DFA6AA,0x78C14389,0xD95A537F,0x207D5BA2,0x02E5B9C5, + 0x83260376,0x6295CFA9,0x11C81968,0x4E734A41,0xB3472DCA,0x7B14A94A,0x1B510052,0x9A532915, + 0xD60F573F,0xBC9BC6E4,0x2B60A476,0x81E67400,0x08BA6FB5,0x571BE91F,0xF296EC6B,0x2A0DD915, + 0xB6636521,0xE7B9F9B6,0xFF34052E,0xC5855664,0x53B02D5D,0xA99F8FA1,0x08BA4799,0x6E85076A +},{ + 0x4B7A70E9,0xB5B32944,0xDB75092E,0xC4192623,0xAD6EA6B0,0x49A7DF7D,0x9CEE60B8,0x8FEDB266, + 0xECAA8C71,0x699A17FF,0x5664526C,0xC2B19EE1,0x193602A5,0x75094C29,0xA0591340,0xE4183A3E, + 0x3F54989A,0x5B429D65,0x6B8FE4D6,0x99F73FD6,0xA1D29C07,0xEFE830F5,0x4D2D38E6,0xF0255DC1, + 0x4CDD2086,0x8470EB26,0x6382E9C6,0x021ECC5E,0x09686B3F,0x3EBAEFC9,0x3C971814,0x6B6A70A1, + 0x687F3584,0x52A0E286,0xB79C5305,0xAA500737,0x3E07841C,0x7FDEAE5C,0x8E7D44EC,0x5716F2B8, + 0xB03ADA37,0xF0500C0D,0xF01C1F04,0x0200B3FF,0xAE0CF51A,0x3CB574B2,0x25837A58,0xDC0921BD, + 0xD19113F9,0x7CA92FF6,0x94324773,0x22F54701,0x3AE5E581,0x37C2DADC,0xC8B57634,0x9AF3DDA7, + 0xA9446146,0x0FD0030E,0xECC8C73E,0xA4751E41,0xE238CD99,0x3BEA0E2F,0x3280BBA1,0x183EB331, + 0x4E548B38,0x4F6DB908,0x6F420D03,0xF60A04BF,0x2CB81290,0x24977C79,0x5679B072,0xBCAF89AF, + 0xDE9A771F,0xD9930810,0xB38BAE12,0xDCCF3F2E,0x5512721F,0x2E6B7124,0x501ADDE6,0x9F84CD87, + 0x7A584718,0x7408DA17,0xBC9F9ABC,0xE94B7D8C,0xEC7AEC3A,0xDB851DFA,0x63094366,0xC464C3D2, + 0xEF1C1847,0x3215D908,0xDD433B37,0x24C2BA16,0x12A14D43,0x2A65C451,0x50940002,0x133AE4DD, + 0x71DFF89E,0x10314E55,0x81AC77D6,0x5F11199B,0x043556F1,0xD7A3C76B,0x3C11183B,0x5924A509, + 0xF28FE6ED,0x97F1FBFA,0x9EBABF2C,0x1E153C6E,0x86E34570,0xEAE96FB1,0x860E5E0A,0x5A3E2AB3, + 0x771FE71C,0x4E3D06FA,0x2965DCB9,0x99E71D0F,0x803E89D6,0x5266C825,0x2E4CC978,0x9C10B36A, + 0xC6150EBA,0x94E2EA78,0xA5FC3C53,0x1E0A2DF4,0xF2F74EA7,0x361D2B3D,0x1939260F,0x19C27960, + 0x5223A708,0xF71312B6,0xEBADFE6E,0xEAC31F66,0xE3BC4595,0xA67BC883,0xB17F37D1,0x018CFF28, + 0xC332DDEF,0xBE6C5AA5,0x65582185,0x68AB9802,0xEECEA50F,0xDB2F953B,0x2AEF7DAD,0x5B6E2F84, + 0x1521B628,0x29076170,0xECDD4775,0x619F1510,0x13CCA830,0xEB61BD96,0x0334FE1E,0xAA0363CF, + 0xB5735C90,0x4C70A239,0xD59E9E0B,0xCBAADE14,0xEECC86BC,0x60622CA7,0x9CAB5CAB,0xB2F3846E, + 0x648B1EAF,0x19BDF0CA,0xA02369B9,0x655ABB50,0x40685A32,0x3C2AB4B3,0x319EE9D5,0xC021B8F7, + 0x9B540B19,0x875FA099,0x95F7997E,0x623D7DA8,0xF837889A,0x97E32D77,0x11ED935F,0x16681281, + 0x0E358829,0xC7E61FD6,0x96DEDFA1,0x7858BA99,0x57F584A5,0x1B227263,0x9B83C3FF,0x1AC24696, + 0xCDB30AEB,0x532E3054,0x8FD948E4,0x6DBC3128,0x58EBF2EF,0x34C6FFEA,0xFE28ED61,0xEE7C3C73, + 0x5D4A14D9,0xE864B7E3,0x42105D14,0x203E13E0,0x45EEE2B6,0xA3AAABEA,0xDB6C4F15,0xFACB4FD0, + 0xC742F442,0xEF6ABBB5,0x654F3B1D,0x41CD2105,0xD81E799E,0x86854DC7,0xE44B476A,0x3D816250, + 0xCF62A1F2,0x5B8D2646,0xFC8883A0,0xC1C7B6A3,0x7F1524C3,0x69CB7492,0x47848A0B,0x5692B285, + 0x095BBF00,0xAD19489D,0x1462B174,0x23820E00,0x58428D2A,0x0C55F5EA,0x1DADF43E,0x233F7061, + 0x3372F092,0x8D937E41,0xD65FECF1,0x6C223BDB,0x7CDE3759,0xCBEE7460,0x4085F2A7,0xCE77326E, + 0xA6078084,0x19F8509E,0xE8EFD855,0x61D99735,0xA969A7AA,0xC50C06C2,0x5A04ABFC,0x800BCADC, + 0x9E447A2E,0xC3453484,0xFDD56705,0x0E1E9EC9,0xDB73DBD3,0x105588CD,0x675FDA79,0xE3674340, + 0xC5C43465,0x713E38D8,0x3D28F89E,0xF16DFF20,0x153E21E7,0x8FB03D4A,0xE6E39F2B,0xDB83ADF7 +},{ + 0xE93D5A68,0x948140F7,0xF64C261C,0x94692934,0x411520F7,0x7602D4F7,0xBCF46B2E,0xD4A20068, + 0xD4082471,0x3320F46A,0x43B7D4B7,0x500061AF,0x1E39F62E,0x97244546,0x14214F74,0xBF8B8840, + 0x4D95FC1D,0x96B591AF,0x70F4DDD3,0x66A02F45,0xBFBC09EC,0x03BD9785,0x7FAC6DD0,0x31CB8504, + 0x96EB27B3,0x55FD3941,0xDA2547E6,0xABCA0A9A,0x28507825,0x530429F4,0x0A2C86DA,0xE9B66DFB, + 0x68DC1462,0xD7486900,0x680EC0A4,0x27A18DEE,0x4F3FFEA2,0xE887AD8C,0xB58CE006,0x7AF4D6B6, + 0xAACE1E7C,0xD3375FEC,0xCE78A399,0x406B2A42,0x20FE9E35,0xD9F385B9,0xEE39D7AB,0x3B124E8B, + 0x1DC9FAF7,0x4B6D1856,0x26A36631,0xEAE397B2,0x3A6EFA74,0xDD5B4332,0x6841E7F7,0xCA7820FB, + 0xFB0AF54E,0xD8FEB397,0x454056AC,0xBA489527,0x55533A3A,0x20838D87,0xFE6BA9B7,0xD096954B, + 0x55A867BC,0xA1159A58,0xCCA92963,0x99E1DB33,0xA62A4A56,0x3F3125F9,0x5EF47E1C,0x9029317C, + 0xFDF8E802,0x04272F70,0x80BB155C,0x05282CE3,0x95C11548,0xE4C66D22,0x48C1133F,0xC70F86DC, + 0x07F9C9EE,0x41041F0F,0x404779A4,0x5D886E17,0x325F51EB,0xD59BC0D1,0xF2BCC18F,0x41113564, + 0x257B7834,0x602A9C60,0xDFF8E8A3,0x1F636C1B,0x0E12B4C2,0x02E1329E,0xAF664FD1,0xCAD18115, + 0x6B2395E0,0x333E92E1,0x3B240B62,0xEEBEB922,0x85B2A20E,0xE6BA0D99,0xDE720C8C,0x2DA2F728, + 0xD0127845,0x95B794FD,0x647D0862,0xE7CCF5F0,0x5449A36F,0x877D48FA,0xC39DFD27,0xF33E8D1E, + 0x0A476341,0x992EFF74,0x3A6F6EAB,0xF4F8FD37,0xA812DC60,0xA1EBDDF8,0x991BE14C,0xDB6E6B0D, + 0xC67B5510,0x6D672C37,0x2765D43B,0xDCD0E804,0xF1290DC7,0xCC00FFA3,0xB5390F92,0x690FED0B, + 0x667B9FFB,0xCEDB7D9C,0xA091CF0B,0xD9155EA3,0xBB132F88,0x515BAD24,0x7B9479BF,0x763BD6EB, + 0x37392EB3,0xCC115979,0x8026E297,0xF42E312D,0x6842ADA7,0xC66A2B3B,0x12754CCC,0x782EF11C, + 0x6A124237,0xB79251E7,0x06A1BBE6,0x4BFB6350,0x1A6B1018,0x11CAEDFA,0x3D25BDD8,0xE2E1C3C9, + 0x44421659,0x0A121386,0xD90CEC6E,0xD5ABEA2A,0x64AF674E,0xDA86A85F,0xBEBFE988,0x64E4C3FE, + 0x9DBC8057,0xF0F7C086,0x60787BF8,0x6003604D,0xD1FD8346,0xF6381FB0,0x7745AE04,0xD736FCCC, + 0x83426B33,0xF01EAB71,0xB0804187,0x3C005E5F,0x77A057BE,0xBDE8AE24,0x55464299,0xBF582E61, + 0x4E58F48F,0xF2DDFDA2,0xF474EF38,0x8789BDC2,0x5366F9C3,0xC8B38E74,0xB475F255,0x46FCD9B9, + 0x7AEB2661,0x8B1DDF84,0x846A0E79,0x915F95E2,0x466E598E,0x20B45770,0x8CD55591,0xC902DE4C, + 0xB90BACE1,0xBB8205D0,0x11A86248,0x7574A99E,0xB77F19B6,0xE0A9DC09,0x662D09A1,0xC4324633, + 0xE85A1F02,0x09F0BE8C,0x4A99A025,0x1D6EFE10,0x1AB93D1D,0x0BA5A4DF,0xA186F20F,0x2868F169, + 0xDCB7DA83,0x573906FE,0xA1E2CE9B,0x4FCD7F52,0x50115E01,0xA70683FA,0xA002B5C4,0x0DE6D027, + 0x9AF88C27,0x773F8641,0xC3604C06,0x61A806B5,0xF0177A28,0xC0F586E0,0x006058AA,0x30DC7D62, + 0x11E69ED7,0x2338EA63,0x53C2DD94,0xC2C21634,0xBBCBEE56,0x90BCB6DE,0xEBFC7DA1,0xCE591D76, + 0x6F05E409,0x4B7C0188,0x39720A3D,0x7C927C24,0x86E3725F,0x724D9DB9,0x1AC15BB4,0xD39EB8FC, + 0xED545578,0x08FCA5B5,0xD83D7CD3,0x4DAD0FC4,0x1E50EF5E,0xB161E6F8,0xA28514D9,0x6C51133C, + 0x6FD5C7E7,0x56E14EC4,0x362ABFCE,0xDDC6C837,0xD79A3234,0x92638212,0x670EFA8E,0x406000E0 + },{ + 0x3A39CE37,0xD3FAF5CF,0xABC27737,0x5AC52D1B,0x5CB0679E,0x4FA33742,0xD3822740,0x99BC9BBE, + 0xD5118E9D,0xBF0F7315,0xD62D1C7E,0xC700C47B,0xB78C1B6B,0x21A19045,0xB26EB1BE,0x6A366EB4, + 0x5748AB2F,0xBC946E79,0xC6A376D2,0x6549C2C8,0x530FF8EE,0x468DDE7D,0xD5730A1D,0x4CD04DC6, + 0x2939BBDB,0xA9BA4650,0xAC9526E8,0xBE5EE304,0xA1FAD5F0,0x6A2D519A,0x63EF8CE2,0x9A86EE22, + 0xC089C2B8,0x43242EF6,0xA51E03AA,0x9CF2D0A4,0x83C061BA,0x9BE96A4D,0x8FE51550,0xBA645BD6, + 0x2826A2F9,0xA73A3AE1,0x4BA99586,0xEF5562E9,0xC72FEFD3,0xF752F7DA,0x3F046F69,0x77FA0A59, + 0x80E4A915,0x87B08601,0x9B09E6AD,0x3B3EE593,0xE990FD5A,0x9E34D797,0x2CF0B7D9,0x022B8B51, + 0x96D5AC3A,0x017DA67D,0xD1CF3ED6,0x7C7D2D28,0x1F9F25CF,0xADF2B89B,0x5AD6B472,0x5A88F54C, + 0xE029AC71,0xE019A5E6,0x47B0ACFD,0xED93FA9B,0xE8D3C48D,0x283B57CC,0xF8D56629,0x79132E28, + 0x785F0191,0xED756055,0xF7960E44,0xE3D35E8C,0x15056DD4,0x88F46DBA,0x03A16125,0x0564F0BD, + 0xC3EB9E15,0x3C9057A2,0x97271AEC,0xA93A072A,0x1B3F6D9B,0x1E6321F5,0xF59C66FB,0x26DCF319, + 0x7533D928,0xB155FDF5,0x03563482,0x8ABA3CBB,0x28517711,0xC20AD9F8,0xABCC5167,0xCCAD925F, + 0x4DE81751,0x3830DC8E,0x379D5862,0x9320F991,0xEA7A90C2,0xFB3E7BCE,0x5121CE64,0x774FBE32, + 0xA8B6E37E,0xC3293D46,0x48DE5369,0x6413E680,0xA2AE0810,0xDD6DB224,0x69852DFD,0x09072166, + 0xB39A460A,0x6445C0DD,0x586CDECF,0x1C20C8AE,0x5BBEF7DD,0x1B588D40,0xCCD2017F,0x6BB4E3BB, + 0xDDA26A7E,0x3A59FF45,0x3E350A44,0xBCB4CDD5,0x72EACEA8,0xFA6484BB,0x8D6612AE,0xBF3C6F47, + 0xD29BE463,0x542F5D9E,0xAEC2771B,0xF64E6370,0x740E0D8D,0xE75B1357,0xF8721671,0xAF537D5D, + 0x4040CB08,0x4EB4E2CC,0x34D2466A,0x0115AF84,0xE1B00428,0x95983A1D,0x06B89FB4,0xCE6EA048, + 0x6F3F3B82,0x3520AB82,0x011A1D4B,0x277227F8,0x611560B1,0xE7933FDC,0xBB3A792B,0x344525BD, + 0xA08839E1,0x51CE794B,0x2F32C9B7,0xA01FBAC9,0xE01CC87E,0xBCC7D1F6,0xCF0111C3,0xA1E8AAC7, + 0x1A908749,0xD44FBD9A,0xD0DADECB,0xD50ADA38,0x0339C32A,0xC6913667,0x8DF9317C,0xE0B12B4F, + 0xF79E59B7,0x43F5BB3A,0xF2D519FF,0x27D9459C,0xBF97222C,0x15E6FC2A,0x0F91FC71,0x9B941525, + 0xFAE59361,0xCEB69CEB,0xC2A86459,0x12BAA8D1,0xB6C1075E,0xE3056A0C,0x10D25065,0xCB03A442, + 0xE0EC6E0E,0x1698DB3B,0x4C98A0BE,0x3278E964,0x9F1F9532,0xE0D392DF,0xD3A0342B,0x8971F21E, + 0x1B0A7441,0x4BA3348C,0xC5BE7120,0xC37632D8,0xDF359F8D,0x9B992F2E,0xE60B6F47,0x0FE3F11D, + 0xE54CDA54,0x1EDAD891,0xCE6279CF,0xCD3E7E6F,0x1618B166,0xFD2C1D05,0x848FD2C5,0xF6FB2299, + 0xF523F357,0xA6327623,0x93A83531,0x56CCCD02,0xACF08162,0x5A75EBB5,0x6E163697,0x88D273CC, + 0xDE966292,0x81B949D0,0x4C50901B,0x71C65614,0xE6C6C7BD,0x327A140A,0x45E1D006,0xC3F27B9A, + 0xC9AA53FD,0x62A80F00,0xBB25BFE2,0x35BDD2F6,0x71126905,0xB2040222,0xB6CBCF7C,0xCD769C2B, + 0x53113EC0,0x1640E3D3,0x38ABBD60,0x2547ADF0,0xBA38209C,0xF746CE76,0x77AFA1C5,0x20756060, + 0x85CBFE4E,0x8AE88DD8,0x7AAAF9B0,0x4CF9AA7E,0x1948C25C,0x02FB8A8C,0x01C36AE4,0xD6EBE1F9, + 0x90D4F869,0xA65CDEA0,0x3F09252D,0xC208E69F,0xB74E6132,0xCE77E25B,0x578FDFE3,0x3AC372E6 +} }; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void blowfish_encrypt(const BYTE in[], BYTE out[], const _BLOWFISH_KEY *keystruct) +{ + UINT l,r,t; //,i; + + l = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | (in[3]); + r = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | (in[7]); + + ITERATION(l,r,t,0); + ITERATION(l,r,t,1); + ITERATION(l,r,t,2); + ITERATION(l,r,t,3); + ITERATION(l,r,t,4); + ITERATION(l,r,t,5); + ITERATION(l,r,t,6); + ITERATION(l,r,t,7); + ITERATION(l,r,t,8); + ITERATION(l,r,t,9); + ITERATION(l,r,t,10); + ITERATION(l,r,t,11); + ITERATION(l,r,t,12); + ITERATION(l,r,t,13); + ITERATION(l,r,t,14); + l ^= keystruct->p[15]; BF(l,t); r^= t; //Last iteration has no swap() + r ^= keystruct->p[16]; + l ^= keystruct->p[17]; + + out[0] = l >> 24; + out[1] = l >> 16; + out[2] = l >> 8; + out[3] = l; + out[4] = r >> 24; + out[5] = r >> 16; + out[6] = r >> 8; + out[7] = r; +} + +void blowfish_decrypt(const BYTE in[], BYTE out[], const _BLOWFISH_KEY *keystruct) +{ + UINT l,r,t; //,i; + + l = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | (in[3]); + r = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | (in[7]); + + ITERATION(l,r,t,17); + ITERATION(l,r,t,16); + ITERATION(l,r,t,15); + ITERATION(l,r,t,14); + ITERATION(l,r,t,13); + ITERATION(l,r,t,12); + ITERATION(l,r,t,11); + ITERATION(l,r,t,10); + ITERATION(l,r,t,9); + ITERATION(l,r,t,8); + ITERATION(l,r,t,7); + ITERATION(l,r,t,6); + ITERATION(l,r,t,5); + ITERATION(l,r,t,4); + ITERATION(l,r,t,3); + l ^= keystruct->p[2]; BF(l,t); r^= t; //Last iteration has no swap() + r ^= keystruct->p[1]; + l ^= keystruct->p[0]; + + out[0] = l >> 24; + out[1] = l >> 16; + out[2] = l >> 8; + out[3] = l; + out[4] = r >> 24; + out[5] = r >> 16; + out[6] = r >> 8; + out[7] = r; +} + +void blowfish_key_setup(const BYTE user_key[], _BLOWFISH_KEY *keystruct, size_t len) +{ + BYTE block[8]; + int idx,idx2; + + // Copy over the constant init array vals (so the originals aren't destroyed). + memcpy(keystruct->p,p_perm,sizeof(UINT) * 18); + memcpy(keystruct->s,s_perm,sizeof(UINT) * 1024); + + // Combine the key with the P box. Assume key is standard 448 bits (56 bytes) or less. + for (idx = 0, idx2 = 0; idx < 18; ++idx, idx2 += 4) + keystruct->p[idx] ^= (user_key[idx2 % len] << 24) | (user_key[(idx2+1) % len] << 16) + | (user_key[(idx2+2) % len] << 8) | (user_key[(idx2+3) % len]); + // Re-calculate the P box. + memset(block, 0, 8); + for (idx = 0; idx < 18; idx += 2) { + blowfish_encrypt(block,block,keystruct); + keystruct->p[idx] = (block[0] << 24) | (block[1] << 16) | (block[2] << 8) | block[3]; + keystruct->p[idx+1]=(block[4] << 24) | (block[5] << 16) | (block[6] << 8) | block[7]; + } + // Recalculate the S-boxes. + for (idx = 0; idx < 4; ++idx) { + for (idx2 = 0; idx2 < 256; idx2 += 2) { + blowfish_encrypt(block,block,keystruct); + keystruct->s[idx][idx2] = (block[0] << 24) | (block[1] << 16) | + (block[2] << 8) | block[3]; + keystruct->s[idx][idx2+1] = (block[4] << 24) | (block[5] << 16) | + (block[6] << 8) | block[7]; + } + } +} + +// -------------------------------------------------- ROT-13 -------------------------------------------------- // + +/*********************** FUNCTION DEFINITIONS ***********************/ +void rot13(char str[]) +{ + int case_type, idx, len; + + for (idx = 0, len = (int)strlen(str); idx < len; idx++) { + // Only process alphabetic characters. + if (str[idx] < 'A' || (str[idx] > 'Z' && str[idx] < 'a') || str[idx] > 'z') + continue; + // Determine if the char is upper or lower case. + if (str[idx] >= 'a') + case_type = 'a'; + else + case_type = 'A'; + // Rotate the char's value, ensuring it doesn't accidentally "fall off" the end. + str[idx] = (str[idx] + 13) % (case_type + 26); + if (str[idx] < 26) + str[idx] += case_type; + } +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/global/Crypto.h b/global/Crypto.h new file mode 100644 index 0000000..17bc281 --- /dev/null +++ b/global/Crypto.h @@ -0,0 +1,258 @@ +#pragma once + +#include "hpsocket/GlobalDef.h" + +#include + +// -------------------------------------------------- BASE64 -------------------------------------------------- // + +// Returns the size of the output. If called with out = NULL, will just return +// the size of what the output would have been (without a terminating NULL). +size_t base64_encode(const BYTE in[], BYTE out[], size_t len, int newline_flag); + +// Returns the size of the output. If called with out = NULL, will just return +// the size of what the output would have been (without a terminating NULL). +size_t base64_decode(const BYTE in[], BYTE out[], size_t len); + +// -------------------------------------------------- URL -------------------------------------------------- // + +int url_encode(const char* src, const int src_size, char* dest, const int dest_size); +int url_decode(const char* src, const int src_size, char* dest, const int dest_size); + +// -------------------------------------------------- AES -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time + +/**************************** DATA TYPES ****************************/ +//typedef unsigned char BYTE; // 8-bit byte +//typedef unsigned int UINT; // 32-bit word, change to "long" for 16-bit machines + +/*********************** FUNCTION DECLARATIONS **********************/ +/////////////////// +// AES +/////////////////// +// Key setup must be done before any AES en/de-cryption functions can be used. +void aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits + UINT w[], // Output key schedule to be used later + int keysize); // Bit length of the key, 128, 192, or 256 + +void aes_encrypt(const BYTE in[], // 16 bytes of plaintext + BYTE out[], // 16 bytes of ciphertext + const UINT key[], // From the key setup + int keysize); // Bit length of the key, 128, 192, or 256 + +void aes_decrypt(const BYTE in[], // 16 bytes of ciphertext + BYTE out[], // 16 bytes of plaintext + const UINT key[], // From the key setup + int keysize); // Bit length of the key, 128, 192, or 256 + +/////////////////// +// AES - CBC +/////////////////// +int aes_encrypt_cbc(const BYTE in[], // Plaintext + size_t in_len, // Must be a multiple of AES_BLOCK_SIZE + BYTE out[], // Ciphertext, same length as plaintext + const UINT key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + +// Only output the CBC-MAC of the input. +int aes_encrypt_cbc_mac(const BYTE in[], // plaintext + size_t in_len, // Must be a multiple of AES_BLOCK_SIZE + BYTE out[], // Output MAC + const UINT key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + +/////////////////// +// AES - CTR +/////////////////// +void increment_iv(BYTE iv[], // Must be a multiple of AES_BLOCK_SIZE + int counter_size); // Bytes of the IV used for counting (low end) + +void aes_encrypt_ctr(const BYTE in[], // Plaintext + size_t in_len, // Any byte length + BYTE out[], // Ciphertext, same length as plaintext + const UINT key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + +void aes_decrypt_ctr(const BYTE in[], // Ciphertext + size_t in_len, // Any byte length + BYTE out[], // Plaintext, same length as ciphertext + const UINT key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long + +/////////////////// +// AES - CCM +/////////////////// +// Returns True if the input parameters do not violate any constraint. +int aes_encrypt_ccm(const BYTE plaintext[], // IN - Plaintext. + UINT plaintext_len, // IN - Plaintext length. + const BYTE associated_data[], // IN - Associated Data included in authentication, but not encryption. + unsigned short associated_data_len, // IN - Associated Data length in bytes. + const BYTE nonce[], // IN - The Nonce to be used for encryption. + unsigned short nonce_len, // IN - Nonce length in bytes. + BYTE ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC. + UINT *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len. + UINT mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16. + const BYTE key[], // IN - The AES key for encryption. + int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256. + +// Returns True if the input parameters do not violate any constraint. +// Use mac_auth to ensure decryption/validation was preformed correctly. +// If authentication does not succeed, the plaintext is zeroed out. To overwride +// this, call with mac_auth = NULL. The proper proceedure is to decrypt with +// authentication enabled (mac_auth != NULL) and make a second call to that +// ignores authentication explicitly if the first call failes. +int aes_decrypt_ccm(const BYTE ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC. + UINT ciphertext_len, // IN - Ciphertext length in bytes. + const BYTE assoc[], // IN - The Associated Data, required for authentication. + unsigned short assoc_len, // IN - Associated Data length in bytes. + const BYTE nonce[], // IN - The Nonce to use for decryption, same one as for encryption. + unsigned short nonce_len, // IN - Nonce length in bytes. + BYTE plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - mac_len. + UINT *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len . + UINT mac_len, // IN - The length of the MAC that was calculated. + int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the authentication. + const BYTE key[], // IN - The AES key for decryption. + int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256. + +// -------------------------------------------------- DES -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define DES_BLOCK_SIZE 8 // DES operates on 8 bytes at a time + +/**************************** DATA TYPES ****************************/ + +typedef enum { + DES_ENCRYPT, + DES_DECRYPT +} DES_MODE; + +/*********************** FUNCTION DECLARATIONS **********************/ +void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode); +void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]); + +void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode); +void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]); + +// -------------------------------------------------- MD2 -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define MD2_BLOCK_SIZE 16 + +/**************************** DATA TYPES ****************************/ + +typedef struct { + BYTE data[16]; + BYTE state[48]; + BYTE checksum[16]; + int len; +} _MD2_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ +void md2_init(_MD2_CTX *ctx); +void md2_update(_MD2_CTX *ctx, const BYTE data[], size_t len); +void md2_final(_MD2_CTX *ctx, BYTE hash[]); // size of hash must be MD2_BLOCK_SIZE + +// -------------------------------------------------- MD5 -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define MD5_BLOCK_SIZE 16 // MD5 outputs a 16 byte digest + +/**************************** DATA TYPES ****************************/ + +typedef struct { + BYTE data[64]; + UINT datalen; + unsigned long long bitlen; + UINT state[4]; +} _MD5_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ +void md5_init(_MD5_CTX *ctx); +void md5_update(_MD5_CTX *ctx, const BYTE data[], size_t len); +void md5_final(_MD5_CTX *ctx, BYTE hash[]); + +// -------------------------------------------------- SHA1 -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define SHA1_BLOCK_SIZE 20 // SHA1 outputs a 20 byte digest + +/**************************** DATA TYPES ****************************/ + +typedef struct { + BYTE data[64]; + UINT datalen; + unsigned long long bitlen; + UINT state[5]; + UINT k[4]; +} _SHA1_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ + +void sha1_init(_SHA1_CTX *ctx); +void sha1_update(_SHA1_CTX *ctx, const BYTE data[], size_t len); +void sha1_final(_SHA1_CTX *ctx, BYTE hash[]); + +// -------------------------------------------------- SHA256 -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest + +/**************************** DATA TYPES ****************************/ + +typedef struct { + BYTE data[64]; + UINT datalen; + unsigned long long bitlen; + UINT state[8]; +} _SHA256_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ +void sha256_init(_SHA256_CTX *ctx); +void sha256_update(_SHA256_CTX *ctx, const BYTE data[], size_t len); +void sha256_final(_SHA256_CTX *ctx, BYTE hash[]); + +// -------------------------------------------------- ARCFOUR -------------------------------------------------- // + +/**************************** DATA TYPES ****************************/ + +/*********************** FUNCTION DECLARATIONS **********************/ +// Input: state - the state used to generate the keystream +// key - Key to use to initialize the state +// len - length of key in bytes (valid lenth is 1 to 256) +void arcfour_key_setup(BYTE state[], const BYTE key[], int len); + +// Pseudo-Random Generator Algorithm +// Input: state - the state used to generate the keystream +// out - Must be allocated to be of at least "len" length +// len - number of bytes to generate +void arcfour_generate_stream(BYTE state[], BYTE out[], size_t len); + +// -------------------------------------------------- BLOWFISH -------------------------------------------------- // + +/****************************** MACROS ******************************/ +#define BLOWFISH_BLOCK_SIZE 8 // Blowfish operates on 8 bytes at a time + +/**************************** DATA TYPES ****************************/ + +typedef struct { + WORD p[18]; + WORD s[4][256]; +} _BLOWFISH_KEY; + +/*********************** FUNCTION DECLARATIONS **********************/ +void blowfish_key_setup(const BYTE user_key[], _BLOWFISH_KEY *keystruct, size_t len); +void blowfish_encrypt(const BYTE in[], BYTE out[], const _BLOWFISH_KEY *keystruct); +void blowfish_decrypt(const BYTE in[], BYTE out[], const _BLOWFISH_KEY *keystruct); + +// -------------------------------------------------- ROT-13 -------------------------------------------------- // + +/*********************** FUNCTION DECLARATIONS **********************/ +// Performs IN PLACE rotation of the input. Assumes input is NULL terminated. +// Preserves each charcter's case. Ignores non alphabetic characters. +void rot13(char str[]); diff --git a/global/helper.cpp b/global/helper.cpp new file mode 100644 index 0000000..a777457 --- /dev/null +++ b/global/helper.cpp @@ -0,0 +1,2476 @@ +#include "helper.h" +#include "common/FileHelper.h" + +#include +using namespace std; + +app_arg g_app_arg; + +char app_arg::OPTIONS[] = ":a:p:b:d:j:t:e:i:c:l:s:m:o:z:x:y:n:r:u:k:w:q:hv"; + +app_arg::app_arg() +{ + // -a + remote_addr = IPV4_LOOPBACK_ADDRESS; + // -p + port = DEF_TCP_UDP_PORT; + // -b + bind_addr = ""; + // -d + local_port = 0; + // -j + reject_addr = ""; + // -n + async = true; + // -t + thread_count = DEFAULT_WORKER_THREAD_COUNT; + // -e + test_times = 100; + // -i + test_interval = 1; + // -c + conn_count = 100; + // -l + data_length = 5000; + // -s + send_policy = SP_PACK; + // -m + max_conn = 10000; + // -q + keep_alive = true; + + // -o + cast_mode = CM_MULTICAST; + // -r + reuse_addr = RAP_ADDR_ONLY; + // -u + ip_loop = true; + // -k + ttl = 1; + + // -x + http_port = DEF_HTTP_PORT; + // -y + https_port = DEF_HTTPS_PORT; + // -z + http_use_cookie = true; + // -w + http_with_listener = true;; +} + +app_arg::~app_arg() +{ + PRINTLN("bye ~ bye ~"); +} + +void app_arg::ParseArgs(int argc, char* const argv[]) +{ + int c; + CString strOptArg; + + + while((c = ::getopt(argc, argv, OPTIONS)) != -1) + { + strOptArg = optarg; + + if(strOptArg == "-") + strOptArg.Empty(); + + switch(c) + { + case 'a': remote_addr = strOptArg; break; + case 'p': port = (USHORT)atoi(strOptArg); break; + case 'b': bind_addr = strOptArg; break; + case 'd': local_port = (USHORT)atoi(strOptArg); break; + case 'j': reject_addr = strOptArg; break; + case 'n': async = (bool)atoi(strOptArg); break; + case 't': thread_count = (DWORD)atoi(strOptArg); break; + case 'e': test_times = (DWORD)atoi(strOptArg); break; + case 'i': test_interval = (DWORD)atoi(strOptArg); break; + case 'c': conn_count = (DWORD)atoi(strOptArg); break; + case 'l': data_length = (DWORD)atoi(strOptArg); break; + case 's': send_policy = (EnSendPolicy)atoi(strOptArg); break; + case 'm': max_conn = (DWORD)atoi(strOptArg); break; + case 'q': keep_alive = (bool)atoi(strOptArg); break; + case 'o': cast_mode = (EnCastMode)atoi(strOptArg); break; + case 'r': reuse_addr = (EnReuseAddressPolicy)atoi(strOptArg);break; + case 'u': ip_loop = (bool)atoi(strOptArg); break; + case 'k': ttl = (int)atoi(strOptArg); break; + case 'x': http_port = (USHORT)atoi(strOptArg); break; + case 'y': https_port = (USHORT)atoi(strOptArg); break; + case 'z': http_use_cookie = (bool)atoi(strOptArg); break; + case 'w': http_with_listener= (bool)atoi(strOptArg); break; + case 'v': PrintVersion(); exit(EXIT_CODE_OK); + case 'h': PrintUsage(); exit(EXIT_CODE_OK); + case '?': ERROR_EXIT2(EXIT_CODE_CONFIG, ERROR_INVALID_PARAMETER); + case ':': ERROR_EXIT2(EXIT_CODE_CONFIG, ERROR_INVALID_PARAMETER); + default: ERROR_EXIT2(EXIT_CODE_CONFIG, ERROR_OBJECT_NOT_FOUND); + } + } +} + +void app_arg::PrintUsage() +{ + PRINTLN("--------------------------- Command Line Args ---------------------------"); + PRINTLN(" -%s: %-20s-%s: %-20s-%s: %-20s", "a", "remote_addr", "b", "bind_addr", "c", "conn_count"); + PRINTLN(" -%s: %-20s-%s: %-20s-%s: %-20s", "d", "local_port", "e", "test_times", "h", "(PRINT THIS USAGE)"); + PRINTLN(" -%s: %-20s-%s: %-20s-%s: %-20s", "i", "test_interval", "j", "reject_addr", "k", "ttl"); + PRINTLN(" -%s: %-20s-%s: %-20s-%s: %-20s", "l", "data_length", "m", "max_conn", "n", "async"); + PRINTLN(" -%s: %-20s-%s: %-20s-%s: %-20s", "o", "cast_mode", "p", "port", "q", "keep_alive"); + PRINTLN(" -%s: %-20s-%s: %-20s-%s: %-20s", "r", "reuse_addr", "s", "send_policy", "t", "thread_count"); + PRINTLN(" -%s: %-20s-%s: %-20s-%s: %-20s", "u", "ip_loop", "v", "(PRINT VERSION)", "w", "http_with_listener"); + PRINTLN(" -%s: %-20s-%s: %-20s-%s: %-20s", "x", "http_port", "y", "https_port", "z", "http_use_cookie"); + PRINTLN("-------------------------------------------------------------------------"); +} + +void app_arg::PrintVersion() +{ + DWORD dwVersion = ::GetHPSocketVersion(); + DWORD dwMagor = dwVersion >> 24; + DWORD dwMinor = (dwVersion >> 16) & 0xFF; + DWORD dwRevise = (dwVersion >> 8) & 0xFF; + DWORD dwBuild = dwVersion & 0xFF; + + PRINTLN("HP-Socket for Linux v%d.%d.%d [BN:%02d]", dwMagor, dwMinor, dwRevise, dwBuild); +} + +void app_arg::ShowPFMTestArgs(BOOL bAgent) +{ + PRINTLN("PFM Test Args:"); + PRINTLN("-------------------+-------------------"); + PRINTLN("%18s : %s", "remote_addr", (LPCTSTR)remote_addr); + PRINTLN("%18s : %u", "port", port); + PRINTLN("%18s : %u", "test_times", test_times); + PRINTLN("%18s : %u", "test_interval", test_interval); + PRINTLN("%18s : %u", "conn_count", conn_count); + PRINTLN("%18s : %u", "data_length", data_length); + if(bAgent) { + PRINTLN("%18s : %u", "thread_count", thread_count); + PRINTLN("%18s : %u", "max_conn", max_conn); } + PRINTLN("-------------------+-------------------"); +} + +CCommandParser::CCommandParser(CCommandParser::EnAppType enAppType, CMD_FUNC fnCmds[CT_MAX]) +: m_enAppType(enAppType) +{ + m_szCmdNameFuncs[CT_START] = {"start" , fnCmds[CT_START]}; + m_szCmdNameFuncs[CT_STOP] = {"stop" , fnCmds[CT_STOP]}; + m_szCmdNameFuncs[CT_STATUS] = {"status" , fnCmds[CT_STATUS]}; + m_szCmdNameFuncs[CT_SEND] = {"send" , fnCmds[CT_SEND]}; + m_szCmdNameFuncs[CT_SENDC] = {"sendc" , fnCmds[CT_SENDC]}; + m_szCmdNameFuncs[CT_PAUSE] = {"pause" , fnCmds[CT_PAUSE]}; + m_szCmdNameFuncs[CT_KICK] = {"kick" , fnCmds[CT_KICK]}; + m_szCmdNameFuncs[CT_KICK_L] = {"kickl" , fnCmds[CT_KICK_L]}; + m_szCmdNameFuncs[CT_KICK_S] = {"kicks" , fnCmds[CT_KICK_S]}; + m_szCmdNameFuncs[CT_CONNECT]= {"connect" , fnCmds[CT_CONNECT]}; + m_szCmdNameFuncs[CT_STAT] = {"stat" , fnCmds[CT_STAT]}; + + Reset(); +} + +BOOL CCommandParser::Run() +{ + if(m_thWorker.IsRunning()) + return FALSE; + + Reset(); + + return m_thWorker.Start(this, &CCommandParser::WorkerProc) && WaitForExit(); +} + +BOOL CCommandParser::WaitForExit() +{ + return m_thWorker.Join(); +} + +void CCommandParser::WorkerProc(PVOID pv) +{ + CString strLine; + + PrintUsage(); + printf("> "); + + while(TRUE) + { + Reset(); + + if(!std::getline(cin, strLine)) + { + PRINTLN(); + break; + } + + Parse((LPTSTR)(LPCTSTR)strLine, strLine.GetLength()); + + printf("> "); + } + + /* + PrintUsage(); + + char* lpszLine = nullptr; + SIZE_T nSize = 0; + + printf("> "); + + while(TRUE) + { + Reset(); + + SSIZE_T nRead = getline(&lpszLine, &nSize, stdin); + + if(nRead == EOF) + { + PRINTLN(); + break; + } + + lpszLine[--nRead] = 0; + Parse(lpszLine, nRead); + + printf("> "); + + if(nSize >= 4096) + { + free(lpszLine); + + lpszLine = nullptr; + nSize = 0; + } + } + + free(lpszLine); + + */ +} + +void CCommandParser::Parse(LPTSTR lpszLine, SSIZE_T nLength) +{ + LPTSTR lpszArg = lpszLine; + LPTSTR lpszCmd = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszCmd)) + return; + + int i = CT_START; + + for(; i < CT_MAX; i++) + { + if(strcmp(lpszCmd, m_szCmdNameFuncs[i].name) == 0) + break; + } + + EnCmdType type = (EnCmdType)i; + + if(type == CT_MAX) + { + if(strcmp(lpszCmd, "?") == 0) + PrintUsage(); + else if(strcmp(lpszCmd, "args") == 0) + app_arg::PrintUsage(); + else + { + PRINTLN(" '%s' : command NOT found ~", lpszCmd); + } + + return; + } + + if( ((m_enAppType == AT_CLIENT || m_enAppType == AT_NODE) && + (type == CT_KICK || type == CT_KICK_L || type == CT_KICK_S)) + || (m_enAppType == AT_SERVER && type == CT_CONNECT ) + || (m_enAppType != AT_NODE && type == CT_SENDC) ) + { + PRINTLN(" '%s' : command NOT supported ~", lpszCmd); + return; + } + + if(m_szCmdNameFuncs[type].func == nullptr) + { + PRINTLN(" '%s' : command NOT handled ~", lpszCmd); + return; + } + + ParseCmdArgs(type, lpszArg); +} + +void CCommandParser::ParseCmdArgs(EnCmdType type, LPTSTR lpszArg) +{ + ASSERT(type >= CT_START && type < CT_MAX); + + LPTSTR lpszParam1 = lpszArg; + + if( (m_enAppType != AT_CLIENT || type != CT_SEND) && + (m_enAppType != AT_NODE || type != CT_SENDC) ) + lpszParam1 = ::StrSep2(&lpszArg, " \t"); + + if(type == CT_START || type == CT_STOP || type == CT_STATUS || type == CT_STAT) + { + if(!::IsStrEmpty(lpszParam1)) + goto ERROR_USAGE; + } + else if(type == CT_SEND) + { + LPTSTR lpszMsg = nullptr; + + if(m_enAppType == AT_CLIENT) + lpszMsg = lpszParam1; + else if(m_enAppType == AT_NODE) + { + LPTSTR lpszHost = lpszParam1; + + if(::IsStrEmpty(lpszHost)) + goto ERROR_USAGE; + + m_strRemoteAddr = lpszHost; + + LPTSTR lpszPort = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszPort)) + goto ERROR_USAGE; + if((m_usRemotePort = (USHORT)atoi(lpszPort)) <= 0) + goto ERROR_USAGE; + + lpszMsg = lpszArg; + } + else + { + LPTSTR lpszConnID = lpszParam1; + + if(::IsStrEmpty(lpszConnID)) + goto ERROR_USAGE; + if((m_dwConnID = atol(lpszConnID)) <= 0) + goto ERROR_USAGE; + + lpszMsg = lpszArg; + } + + while(!::IsStrEmpty(lpszMsg)) + { + if(lpszMsg[0] != ' ' && lpszMsg[0] != '\t') + break; + + ++lpszMsg; + } + + if(m_enAppType != AT_NODE && ::IsStrEmpty(lpszMsg)) + goto ERROR_USAGE; + + m_strMessage = lpszMsg; + } + else if(type == CT_SENDC) + { + ASSERT(m_enAppType == AT_NODE); + + LPTSTR lpszMsg = lpszParam1; + + while(!::IsStrEmpty(lpszMsg)) + { + if(lpszMsg[0] != ' ' && lpszMsg[0] != '\t') + break; + + ++lpszMsg; + } + + m_strMessage = lpszMsg; + } + else if(type == CT_PAUSE || type == CT_KICK) + { + LPTSTR lpszFlag = nullptr; + + if(m_enAppType == AT_CLIENT) + lpszFlag = lpszParam1; + else + { + LPTSTR lpszConnID = lpszParam1; + + if(::IsStrEmpty(lpszConnID)) + goto ERROR_USAGE; + if((m_dwConnID = atol(lpszConnID)) <= 0) + goto ERROR_USAGE; + + lpszFlag = ::StrSep2(&lpszArg, " \t"); + } + + if(::IsStrEmpty(lpszFlag)) + m_bFlag = TRUE; + else + { + if(!::IsStrEmpty(::StrSep2(&lpszArg, " \t"))) + goto ERROR_USAGE; + if(::IsStrEmpty(lpszFlag) || (lpszFlag[0] != '0' && lpszFlag[0] != '1') || lpszFlag[1] != 0) + goto ERROR_USAGE; + + m_bFlag = (BOOL)(lpszFlag[0] - '0'); + } + } + else if(type == CT_KICK_L || type == CT_KICK_S) + { + ASSERT(m_enAppType != AT_CLIENT); + + LPTSTR lpszSeconds = lpszParam1; + + if(::IsStrEmpty(lpszSeconds)) + { + m_dwSeconds = 60; + m_bFlag = TRUE; + } + else + { + if(((int)(m_dwSeconds = atoi(lpszSeconds))) <= 0) + goto ERROR_USAGE; + + LPTSTR lpszFlag = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszFlag)) + m_bFlag = TRUE; + else + { + if(!::IsStrEmpty(::StrSep2(&lpszArg, " \t"))) + goto ERROR_USAGE; + if(::IsStrEmpty(lpszFlag) || (lpszFlag[0] != '0' && lpszFlag[0] != '1') || lpszFlag[1] != 0) + goto ERROR_USAGE; + + m_bFlag = (BOOL)(lpszFlag[0] - '0'); + } + } + } + else if(type == CT_CONNECT) + { + ASSERT(m_enAppType == AT_AGENT); + + LPTSTR lpszHost = lpszParam1; + + if(::IsStrEmpty(lpszHost)) + { + m_strRemoteAddr = g_app_arg.remote_addr; + m_usRemotePort = g_app_arg.port; + } + else + { + LPTSTR lpszPort = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszPort) || !::IsStrEmpty(::StrSep2(&lpszArg, " \t"))) + goto ERROR_USAGE; + if((m_usRemotePort = (USHORT)atoi(lpszPort)) <= 0) + goto ERROR_USAGE; + + m_strRemoteAddr = lpszHost; + } + } + + m_szCmdNameFuncs[type].func(this); + + return; + +ERROR_USAGE: + PRINTLN(" '%s' usage : %s", m_szCmdNameFuncs[type].name, (LPCTSTR)GetCmdUsage(type)); +} + +void CCommandParser::Reset() +{ + m_bFlag = TRUE; + m_dwConnID = 0; + m_usRemotePort = 0; + m_dwSeconds = 0; + m_strMessage = nullptr; + m_strRemoteAddr = nullptr; +} + +void CCommandParser::PrintUsage() +{ + PRINTLN("------ ACTION -----+----------------------- USAGE -----------------------"); + + PrintCmdUsage(); + + PRINTLN("%-18s : %6s%s", "Print Usage", "", "?"); + PRINTLN("%-18s : %3s%s", "Print Command Args", "", "args"); + PRINTLN("%-18s : %1s%s", "Quit", "", "Ctrl+D"); + PRINTLN("-------------------+-----------------------------------------------------"); +} + +void CCommandParser::PrintCmdUsage() +{ + if(m_szCmdNameFuncs[CT_START].func) + PRINTLN("%-18s : %2s%s", "Start Up", "", (LPCTSTR)GetCmdUsage(CT_START)); + if(m_szCmdNameFuncs[CT_STOP].func) + PRINTLN("%-18s : %3s%s", "Shut Down", "", (LPCTSTR)GetCmdUsage(CT_STOP)); + if(m_szCmdNameFuncs[CT_STATUS].func) + PRINTLN("%-18s : %1s%s", "Show Status", "", (LPCTSTR)GetCmdUsage(CT_STATUS)); + if(m_szCmdNameFuncs[CT_CONNECT].func && m_enAppType == AT_AGENT) + PRINTLN("%-18s : %s%s", "Connect To", "", (LPCTSTR)GetCmdUsage(CT_CONNECT)); + if(m_szCmdNameFuncs[CT_SEND].func) + PRINTLN("%-18s : %3s%s", "Send Message", "", (LPCTSTR)GetCmdUsage(CT_SEND)); + if(m_szCmdNameFuncs[CT_SENDC].func) + PRINTLN("%-18s : %2s%s", "Send Cast Message", "", (LPCTSTR)GetCmdUsage(CT_SENDC)); + if(m_szCmdNameFuncs[CT_PAUSE].func) + PRINTLN("%-18s : %2s%s", "Pause/Unpause Recv", "", (LPCTSTR)GetCmdUsage(CT_PAUSE)); + if(m_enAppType != AT_CLIENT) { + if(m_szCmdNameFuncs[CT_KICK].func) + PRINTLN("%-18s : %3s%s", "Kick Connection", "", (LPCTSTR)GetCmdUsage(CT_KICK)); + if(m_szCmdNameFuncs[CT_KICK_L].func) + PRINTLN("%-18s : %2s%s", "Kick Long Conns", "", (LPCTSTR)GetCmdUsage(CT_KICK_L)); + if(m_szCmdNameFuncs[CT_KICK_S].func) + PRINTLN("%-18s : %2s%s", "Kick Silence Conns", "", (LPCTSTR)GetCmdUsage(CT_KICK_S)); } + if(m_enAppType == AT_SERVER) { + if(m_szCmdNameFuncs[CT_STAT].func) + PRINTLN("%-18s : %3s%s", "Stat R/S bytes", "", (LPCTSTR)GetCmdUsage(CT_STAT)); } +} + +CString CCommandParser::GetCmdUsage(CCommandParser::EnCmdType type) +{ + CString strUsage = m_szCmdNameFuncs[type].name; + + switch(type) + { + case CT_SEND: + if(m_enAppType == AT_NODE) + strUsage += " {Host} {Port} [{Message}]"; + else + { + if(m_enAppType != AT_CLIENT) + strUsage += " {ConnID}"; + strUsage += " {Message}"; + } + break; + case CT_SENDC: + strUsage += " [{Message}]"; + break; + case CT_PAUSE: + if(m_enAppType != AT_CLIENT) + strUsage += " {ConnID}"; + strUsage += " [0|1] # (0 - unpause, 1 - pause)"; + break; + case CT_KICK: + if(m_enAppType != AT_CLIENT) + strUsage += " {ConnID} [0|1] # (0 - unforced, 1 - forced)"; + else + strUsage = nullptr; + break; + case CT_KICK_L: + case CT_KICK_S: + if(m_enAppType != AT_CLIENT) + strUsage += " [{Seconds}] [0|1] # (default: {60, 1})"; + else + strUsage = nullptr; + break; + case CT_CONNECT: + if(m_enAppType != AT_SERVER) + strUsage += " [{Host} {Port}]"; + else + strUsage = nullptr; + break; + default : + ; + } + + return strUsage; +} + +void CCommandParser::PrintStatus(EnServiceState enStatus, LPCTSTR lpszName) +{ + CString strStatus; + + if(m_enAppType == AT_SERVER) + strStatus += "Server"; + else if(m_enAppType == AT_AGENT) + strStatus += "Agent"; + else if(m_enAppType == AT_CLIENT) + strStatus += "Client"; + else if(m_enAppType == AT_NODE) + strStatus += "Node"; + else + ASSERT(FALSE); + + if(!::IsStrEmpty(lpszName)) + strStatus.AppendChar(' ').Append(lpszName); + + if(enStatus == SS_STARTING) + strStatus += " is starting"; + else if(enStatus == SS_STARTED) + strStatus += " has started"; + else if(enStatus == SS_STOPPING) + strStatus += " is stopping"; + else if(enStatus == SS_STOPPED) + strStatus += " has stopped"; + else + ASSERT(FALSE); + + puts(strStatus); +} + +#ifdef _NEED_HTTP + +void CHttpCommandParser::ParseCmdArgs(EnCmdType type, LPTSTR lpszArg) +{ + ASSERT(type >= CT_START && type < CT_MAX); + + switch(type) + { + case CT_START: + { + if(m_enAppType == CCommandParser::AT_SERVER) + { + LPTSTR lpszParam1 = ::StrSep2(&lpszArg, " \t"); + + if(!::IsStrEmpty(lpszParam1)) + goto ERROR_USAGE; + } + else if(!ParseCmdOptions(lpszArg, ":s:")) + goto ERROR_USAGE; + } + + break; + + case CT_STOP: + case CT_STAT: + case CT_STATUS: + { + LPTSTR lpszParam1 = ::StrSep2(&lpszArg, " \t"); + + if(!::IsStrEmpty(lpszParam1)) + goto ERROR_USAGE; + } + + break; + + case CT_SEND: + { + if(m_enAppType != AT_CLIENT) + { + LPTSTR lpszConnID = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszConnID)) + goto ERROR_USAGE; + if((m_dwConnID = atol(lpszConnID)) <= 0) + goto ERROR_USAGE; + } + + if(m_enAppType == AT_SERVER) + { + if(!ParseCmdOptions(lpszArg, ":s:d:")) + goto ERROR_USAGE; + } + else + { + if(!ParseCmdOptions(lpszArg, ":p:x:h:d:f:")) + goto ERROR_USAGE; + + if(m_strPath.IsEmpty()) + m_strPath = "/"; + if(m_strMethod.IsEmpty()) + m_strMethod = HTTP_METHOD_GET; + } + } + + break; + + case CT_KICK: + { + LPTSTR lpszConnID = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszConnID)) + goto ERROR_USAGE; + if((m_dwConnID = atol(lpszConnID)) <= 0) + goto ERROR_USAGE; + + if(m_enAppType == AT_SERVER && !ParseCmdOptions(lpszArg, ":s:")) + goto ERROR_USAGE; + else if(m_enAppType == AT_AGENT && !::IsStrEmpty(::StrSep2(&lpszArg, " \t"))) + goto ERROR_USAGE; + } + + break; + + case CT_KICK_L: + case CT_KICK_S: + { + m_dwSeconds = 60; + + if(::IsStrEmpty(lpszArg)) + break; + + char c = ::TrimLeft(&lpszArg, " \t")[0]; + + if(c == 0) + break; + else if(c == '-') + { + if(m_enAppType != AT_SERVER || !ParseCmdOptions(lpszArg, ":s:")) + goto ERROR_USAGE; + } + else + { + LPTSTR lpszSeconds = ::StrSep2(&lpszArg, " \t"); + + if(((int)(m_dwSeconds = atoi(lpszSeconds))) <= 0) + goto ERROR_USAGE; + + if(::IsStrEmpty(lpszArg)) + break; + + char c = ::TrimLeft(&lpszArg, " \t")[0]; + + if(c == 0) + break; + else if(c == '-') + { + if(m_enAppType != AT_SERVER || !ParseCmdOptions(lpszArg, ":s:")) + goto ERROR_USAGE; + } + else + goto ERROR_USAGE; + } + } + + break; + + case CT_PAUSE: + { + if(m_enAppType != AT_CLIENT) + { + LPTSTR lpszConnID = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszConnID)) + goto ERROR_USAGE; + if((m_dwConnID = atol(lpszConnID)) <= 0) + goto ERROR_USAGE; + } + + m_bFlag = TRUE; + + if(::IsStrEmpty(lpszArg)) + break; + + char c = ::TrimLeft(&lpszArg, " \t")[0]; + + if(c == 0) + break; + else if(c == '-') + { + if(m_enAppType != AT_SERVER || !ParseCmdOptions(lpszArg, ":s:")) + goto ERROR_USAGE; + } + else + { + LPTSTR lpszFlag = ::StrSep2(&lpszArg, " \t"); + + if((lpszFlag[0] != '0' && lpszFlag[0] != '1') || lpszFlag[1] != 0) + goto ERROR_USAGE; + + m_bFlag = (BOOL)(lpszFlag[0] - '0'); + + if(::IsStrEmpty(lpszArg)) + break; + + char c = ::TrimLeft(&lpszArg, " \t")[0]; + + if(c == 0) + break; + else if(c == '-') + { + if(m_enAppType != AT_SERVER || !ParseCmdOptions(lpszArg, ":s:")) + goto ERROR_USAGE; + } + else + goto ERROR_USAGE; + } + } + + break; + + case CT_CONNECT: + { + if(m_enAppType == AT_AGENT) + { + LPTSTR lpszHost = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszHost)) + { + m_strRemoteAddr = g_app_arg.remote_addr; + m_usRemotePort = 0; + } + else + { + LPTSTR lpszPort = ::StrSep2(&lpszArg, " \t"); + + if(::IsStrEmpty(lpszPort) || !::IsStrEmpty(::StrSep2(&lpszArg, " \t"))) + goto ERROR_USAGE; + if((m_usRemotePort = (USHORT)atoi(lpszPort)) <= 0) + goto ERROR_USAGE; + + m_strRemoteAddr = lpszHost; + } + } + else if(m_enAppType == AT_CLIENT) + { + m_bFlag = FALSE; + m_strPath = ::StrSep2(&lpszArg, " \t"); + + if(m_strPath.IsEmpty()) + goto ERROR_USAGE; + + if(strnicmp(m_strPath, STR_HTTPS_SCHEMA, strlen(STR_HTTPS_SCHEMA)) == 0) + m_bHttps = TRUE; + else if(strnicmp(m_strPath, STR_HTTP_SCHEMA, strlen(STR_HTTP_SCHEMA)) == 0) + m_bHttps = FALSE; + else + goto ERROR_USAGE; + + if(!ParseCmdOptions(lpszArg, ":x:h:d:f:r:")) + goto ERROR_USAGE; + + if(m_strMethod.IsEmpty()) + m_strMethod = HTTP_METHOD_GET; + } + } + + break; + + default: + ASSERT(FALSE); + } + + m_szCmdNameFuncs[type].func(this); + + return; + +ERROR_USAGE: + PRINTLN(" '%s' usage : %s", m_szCmdNameFuncs[type].name, (LPCTSTR)GetCmdUsage(type)); +} + +BOOL CHttpCommandParser::ParseCmdOptions(LPCTSTR lpszArg, LPCTSTR lpszOptions) +{ + if(::IsStrEmpty(lpszArg)) + return TRUE; + + vector vtItem; + + if(!SplitStr(lpszArg, vtItem, " \t", "\'\"")) + return FALSE; + + int iSize = (int)vtItem.size(); + + if(iSize % 2 != 0) + return FALSE; + + for(int i = 0; i < iSize; i += 2) + { + CString& strOpt = vtItem[i]; + CString& strVal = vtItem[i + 1]; + + if(strOpt.GetLength() != 2 || strOpt[0] != '-') + return FALSE; + + TCHAR c = strOpt[1]; + + if(c == ':' || ::StrChr(lpszOptions, c) == nullptr) + return FALSE; + + if(c == 's') + { + if(strVal != "0" && strVal != "1") + return FALSE; + + m_bHttps = (BOOL)(strVal[0] - '0'); + } + else if(c == 'p') + { + m_strPath = strVal; + } + else if(c == 'x') + { + m_strMethod = strVal; + m_strMethod.MakeUpper(); + } + else if(c == 'h') + { + int iPos = strVal.Find(':'); + + if(iPos <= 0) + return FALSE; + + m_vtHeaders.push_back(strVal.Left(iPos)); + m_vtHeaders.push_back(strVal.Mid(iPos + 1).Trim()); + } + else if(c == 'd') + { + m_strData = strVal; + } + else if(c == 'f') + { + m_strFilePath = strVal; + } + else if(c == 'r') + { + if(strVal != "0" && strVal != "1") + return FALSE; + + m_bFlag = (BOOL)(strVal[0] - '0'); + } + else + { + return FALSE; + } + } + + return TRUE; +} + +void CHttpCommandParser::Reset() +{ + __super::Reset(); + m_vtHeaders.clear(); + + m_bHttps = FALSE; + m_strPath = nullptr; + m_strMethod = nullptr; + m_strData = nullptr; + m_strFilePath = nullptr; +} + +void CHttpCommandParser::PrintCmdUsage() +{ + switch(m_enAppType) + { + case AT_SERVER: + if(m_szCmdNameFuncs[CT_START].func) + PRINTLN("%-18s : %2s%s", "Start Up", "", (LPCTSTR)GetCmdUsage(CT_START)); + if(m_szCmdNameFuncs[CT_STOP].func) + PRINTLN("%-18s : %3s%s", "Shut Down", "", (LPCTSTR)GetCmdUsage(CT_STOP)); + if(m_szCmdNameFuncs[CT_STATUS].func) + PRINTLN("%-18s : %1s%s", "Show Status", "", (LPCTSTR)GetCmdUsage(CT_STATUS)); + if(m_szCmdNameFuncs[CT_SEND].func) + PRINTLN("%-18s : %3s%s", "Send WS Message", "", (LPCTSTR)GetCmdUsage(CT_SEND)); + if(m_szCmdNameFuncs[CT_PAUSE].func) + PRINTLN("%-18s : %2s%s", "Pause/Unpause Recv", "", (LPCTSTR)GetCmdUsage(CT_PAUSE)); + if(m_szCmdNameFuncs[CT_KICK].func) + PRINTLN("%-18s : %3s%s", "Release Connection", "", (LPCTSTR)GetCmdUsage(CT_KICK)); + if(m_szCmdNameFuncs[CT_KICK_L].func) + PRINTLN("%-18s : %2s%s", "Kick Long Conns", "", (LPCTSTR)GetCmdUsage(CT_KICK_L)); + if(m_szCmdNameFuncs[CT_KICK_S].func) + PRINTLN("%-18s : %2s%s", "Kick Silence Conns", "", (LPCTSTR)GetCmdUsage(CT_KICK_S)); + if(m_szCmdNameFuncs[CT_STAT].func) + PRINTLN("%-18s : %3s%s", "Stat R/S bytes", "", (LPCTSTR)GetCmdUsage(CT_STAT)); + break; + case AT_AGENT: + if(m_szCmdNameFuncs[CT_START].func) + PRINTLN("%-18s : %2s%s", "Start Up", "", (LPCTSTR)GetCmdUsage(CT_START)); + if(m_szCmdNameFuncs[CT_STOP].func) + PRINTLN("%-18s : %3s%s", "Shut Down", "", (LPCTSTR)GetCmdUsage(CT_STOP)); + if(m_szCmdNameFuncs[CT_STATUS].func) + PRINTLN("%-18s : %1s%s", "Show Status", "", (LPCTSTR)GetCmdUsage(CT_STATUS)); + if(m_szCmdNameFuncs[CT_CONNECT].func) + PRINTLN("%-18s : %s%s", "Connect To", "", (LPCTSTR)GetCmdUsage(CT_CONNECT)); + if(m_szCmdNameFuncs[CT_SEND].func) + PRINTLN("%-18s : %3s%s", "Send Message", "", (LPCTSTR)GetCmdUsage(CT_SEND)); + if(m_szCmdNameFuncs[CT_PAUSE].func) + PRINTLN("%-18s : %2s%s", "Pause/Unpause Recv", "", (LPCTSTR)GetCmdUsage(CT_PAUSE)); + if(m_szCmdNameFuncs[CT_KICK].func) + PRINTLN("%-18s : %3s%s", "Kick Connection", "", (LPCTSTR)GetCmdUsage(CT_KICK)); + if(m_szCmdNameFuncs[CT_KICK_L].func) + PRINTLN("%-18s : %2s%s", "Kick Long Conns", "", (LPCTSTR)GetCmdUsage(CT_KICK_L)); + if(m_szCmdNameFuncs[CT_KICK_S].func) + PRINTLN("%-18s : %2s%s", "Kick Silence Conns", "", (LPCTSTR)GetCmdUsage(CT_KICK_S)); + break; + case AT_CLIENT: + if(m_szCmdNameFuncs[CT_START].func) + PRINTLN("%-18s : %2s%s", "Start Up", "", (LPCTSTR)GetCmdUsage(CT_START)); + if(m_szCmdNameFuncs[CT_STOP].func) + PRINTLN("%-18s : %3s%s", "Shut Down", "", (LPCTSTR)GetCmdUsage(CT_STOP)); + if(m_szCmdNameFuncs[CT_STATUS].func) + PRINTLN("%-18s : %1s%s", "Show Status", "", (LPCTSTR)GetCmdUsage(CT_STATUS)); + if(m_szCmdNameFuncs[CT_CONNECT].func) + PRINTLN("%-18s : %s%s", "Open URL", "", (LPCTSTR)GetCmdUsage(CT_CONNECT)); + if(m_szCmdNameFuncs[CT_SEND].func) + PRINTLN("%-18s : %3s%s", "Send Message", "", (LPCTSTR)GetCmdUsage(CT_SEND)); + if(m_szCmdNameFuncs[CT_PAUSE].func) + PRINTLN("%-18s : %2s%s", "Pause/Unpause Recv", "", (LPCTSTR)GetCmdUsage(CT_PAUSE)); + break; + default: + ASSERT(FALSE); + } +} + +CString CHttpCommandParser::GetCmdUsage(CCommandParser::EnCmdType type) +{ + CString strUsage = m_szCmdNameFuncs[type].name; + + switch(type) + { + case CT_START: + if(m_enAppType != AT_SERVER) + strUsage += " [-s 0|1] # (s-0 - http, s-1 - https)"; + break; + case CT_SEND: + if(m_enAppType == AT_SERVER) + strUsage += " {ConnID} [-s 0|1] -d {WS-Data} # (WS only, s-0 - ws, s-1 - wss)"; + else + { + if(m_enAppType != AT_CLIENT) + strUsage += " {ConnID}"; + strUsage += " [-x {Method}] [-p {Path}] [-h {Header}]* [-d {Data}] [-f {File}]"; + } + break; + case CT_PAUSE: + if(m_enAppType != AT_CLIENT) + strUsage += " {ConnID}"; + strUsage += " [0|1]"; + if(m_enAppType == AT_SERVER) + strUsage += " [-s 0|1]"; + strUsage += " # (0 - unpause, 1 - pause"; + if(m_enAppType == AT_SERVER) + strUsage += ", s-0 - http, s-1 - https"; + strUsage += ")"; + break; + case CT_KICK: + if(m_enAppType == AT_CLIENT) + strUsage = nullptr; + else + { + strUsage += " {ConnID}"; + if(m_enAppType == AT_SERVER) + strUsage += " [-s 0|1] # (s-0 - http, s-1 - https)"; + } + break; + case CT_KICK_L: + case CT_KICK_S: + if(m_enAppType == AT_CLIENT) + strUsage = nullptr; + else + { + strUsage += " [{Seconds}]"; + if(m_enAppType == AT_SERVER) + strUsage += " [-s 0|1] # (default: 60s, s-0 - http, s-1 - https)"; + else + strUsage += " # (default: 60s)"; + } + break; + case CT_CONNECT: + if(m_enAppType == AT_AGENT) + strUsage += " [{Host} {Port}]"; + else if(m_enAppType == AT_CLIENT) + strUsage += " {URL} [-x {Method}] [-h {Header}]* [-d {Data}] [-f {File}] [-r 0|1] # (r-0 - reuse connection, r-1 - force reconnect)"; + else + strUsage = nullptr; + break; + default : + ; + } + + return strUsage; +} + +#endif + +void server_statistics_info::Reset(BOOL bResetClientCount) +{ + if(bResetClientCount) + m_lClientCount = 0L; + + m_llTotalSent = 0L; + m_llTotalReceived = 0L; +} + +void server_statistics_info::AddTotalRecv(int iLength) +{ + ::InterlockedExchangeAdd(&m_llTotalReceived, iLength); +} + +void server_statistics_info::AddTotalSend(int iLength) +{ + ::InterlockedExchangeAdd(&m_llTotalSent, iLength); +} + +void server_statistics_info::CheckClientCount() +{ + if(m_lClientCount == 0) + { + CCriSecLock lock(m_cs); + + if(m_lClientCount == 0) + { + Reset(FALSE); + } + } + + InterlockedIncrement(&m_lClientCount); +} + +void server_statistics_info::CheckStatistics() +{ + if(m_lClientCount > 0) + { + CCriSecLock lock(m_cs); + + if(m_lClientCount > 0) + { + ::InterlockedDecrement(&m_lClientCount); + + if(m_lClientCount == 0) + { + ::WaitFor(500L); + ::PostServerStatics((LONGLONG)m_llTotalSent, (LONGLONG)m_llTotalReceived); + + printf("> "); + fflush(stdout); + } + } + } +} + +void client_statistics_info::Reset() +{ + m_iConnected = 0; + m_dwBeginTickCount = 0; + m_dwTimeconsuming = 0; + m_llTotalReceived = 0; + m_llTotalSent = 0; + m_llExpectReceived = (LONGLONG)g_app_arg.test_times * (LONGLONG)g_app_arg.conn_count * (LONGLONG)g_app_arg.data_length; +} + +void client_statistics_info::StartTest() +{ + m_dwBeginTickCount = ::TimeGetTime(); +} + +void client_statistics_info::AddTotalRecv(int iLength) +{ + ::InterlockedExchangeAdd(&m_llTotalReceived, iLength); + + if(m_llTotalReceived == m_llExpectReceived) + { + m_dwTimeconsuming = ::GetTimeGap32(m_dwBeginTickCount); + ::PostTimeConsuming(m_dwTimeconsuming); + + printf("> "); + fflush(stdout); + } + + ASSERT(m_llTotalReceived <= m_llExpectReceived); +} + +void client_statistics_info::AddTotalSend(int iLength) +{ + ::InterlockedExchangeAdd(&m_llTotalSent, iLength); +} + +void client_statistics_info::TermConnected() +{ + if(m_iConnected >= 0) + m_iConnected = -0xFFFFFF; +} + +void client_statistics_info::AddConnected() +{ + ::InterlockedIncrement(&m_iConnected); +} + +int client_statistics_info::GetConnected() +{ + return m_iConnected; +} + +void client_statistics_info::CheckStatistics(BOOL bCheckSend) +{ + ::WaitFor(100L); + + CString strMsg; + strMsg.Format( _T("*** Summary: expect - %lld, send - %lld, recv - %lld ***"), + m_llExpectReceived, m_llTotalSent, m_llTotalReceived); + + ::LogMsg(strMsg); + + if(m_llExpectReceived == m_llTotalReceived && (!bCheckSend || m_llTotalSent == m_llExpectReceived)) + strMsg.Format(_T("*** Success: time consuming - %u millisecond ! ***"), m_dwTimeconsuming); + else + strMsg.Format(_T("*** Fail: manual terminated ? (or data lost) ***")); + + ::LogMsg(strMsg); +} + +info_msg* info_msg::Construct(CONNID dwConnID, LPCTSTR lpszEvent, int iContentLength, LPCTSTR lpszContent, LPCTSTR lpszName) +{ + return new info_msg(dwConnID, lpszEvent, iContentLength, lpszContent, lpszName); +} + +void info_msg::Destruct(info_msg* pMsg) +{ + delete pMsg; +} + +info_msg::info_msg(CONNID dwConnID, LPCTSTR lpszEvent, int iContentLength, LPCTSTR lpszContent, LPCTSTR lpszName) +: connID(dwConnID), evt(lpszEvent), contentLength(iContentLength), content(lpszContent), name(nullptr) +{ + if(lpszName) + { + int len = lstrlen(lpszName); + + if(len > 0) + { + name = new TCHAR[len + 1]; + memcpy((LPSTR)name, lpszName, (len + 1) * sizeof(TCHAR)); + } + } +} + +info_msg::~info_msg() +{ + if(name) + delete[] name; + + if(contentLength > 0) + delete[] content; +} + +inline CString SafeString(LPCTSTR lpszName) +{ + CString str(lpszName); + + if(lpszName) str.AppendChar(' '); + + return str; +} + +inline CString SafeString2(LPCTSTR lpszName) +{ + CString str(lpszName); + + if(lpszName) str.Append(_T(" #")); + + return str; +} + +void LogServerStart(LPCTSTR lpszAddress, USHORT port, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sServer Start OK --> (%s#%d)"), (LPCTSTR)SafeString(lpszName), lpszAddress, port); + LogMsg(msg); +} + +void LogServerStartFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sServer Start Fail --> %s (%d) [%d]"), (LPCTSTR)SafeString(lpszName), lpszDesc, code, ::GetLastError()); + LogMsg(msg); +} + +void LogServerStop(LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sServer Stop"), (LPCTSTR)SafeString(lpszName)); + + LogMsg(msg); +} + +void LogServerStopFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sServer Stop Fail --> %s (%d)"), (LPCTSTR)SafeString(lpszName), lpszDesc, code); + LogMsg(msg); +} + +void LogAgentStart(LPCTSTR lpszAddress, BOOL bAsync, LPCTSTR lpszName) +{ + if(!lpszAddress || lpszAddress[0] == 0) + lpszAddress = _T("***"); + + char c = bAsync ? 'A' : 'S'; + + CString msg; + msg.Format(_T("# %sAgent Start OK --> (%s - %c)"), (LPCTSTR)SafeString(lpszName), lpszAddress, c); + LogMsg(msg); +} + +void LogAgentStarting(LPCTSTR lpszAddress, BOOL bAsync, LPCTSTR lpszName) +{ + if(!lpszAddress || lpszAddress[0] == 0) + lpszAddress = _T("***"); + + char c = bAsync ? 'A' : 'S'; + + CString msg; + msg.Format(_T("# %sAgent Starting --> (%s - %d)"), (LPCTSTR)SafeString(lpszName), lpszAddress, c); + LogMsg(msg); +} + +void LogAgentStartFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sAgent Start Fail --> %s (%d) [%d]"), (LPCTSTR)SafeString(lpszName), lpszDesc, code, ::GetLastError()); + LogMsg(msg); +} + +void LogAgentStopping(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sAgent Stopping --> (%zu)"), (LPCTSTR)SafeString(lpszName), dwConnID); + LogMsg(msg); +} + +void LogAgentStop(LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sAgent Stop"), (LPCTSTR)SafeString(lpszName)); + + LogMsg(msg); +} + +void LogAgentStopFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sAgent Stop Fail --> %s (%d) [%d]"), (LPCTSTR)SafeString(lpszName), lpszDesc, code, ::GetLastError()); + LogMsg(msg); +} + +void LogAgentSendFail(int iSequence, int iSocketIndex, DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sAgent Send Fail [SOCK: %d, SEQ: %d] --> %s (%d)"), (LPCTSTR)SafeString(lpszName), iSocketIndex, iSequence, lpszDesc, code); + LogMsg(msg); +} + +void LogClientStart(LPCTSTR lpszAddress, USHORT port, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sClient Start OK --> (%s#%d)"), (LPCTSTR)SafeString(lpszName), lpszAddress, port); + LogMsg(msg); +} + +void LogClientStarting(LPCTSTR lpszAddress, USHORT port, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sClient Starting --> (%s#%d)"), (LPCTSTR)SafeString(lpszName), lpszAddress, port); + LogMsg(msg); +} + +void LogClientStartFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sClient Start Fail --> %s (%d) [%d]"), (LPCTSTR)SafeString(lpszName), lpszDesc, code, ::GetLastError()); + LogMsg(msg); +} + +void LogClientStopping(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sClient Stopping --> (%zu)"), (LPCTSTR)SafeString(lpszName), dwConnID); + LogMsg(msg); +} + +void LogClientStop(LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sClient Stop"), (LPCTSTR)SafeString(lpszName)); + + LogMsg(msg); +} + +void LogClientStopFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sClient Stop Fail --> %s (%d) [%d]"), (LPCTSTR)SafeString(lpszName), lpszDesc, code, ::GetLastError()); + LogMsg(msg); +} + +void LogClientSendFail(int iSequence, int iSocketIndex, DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sClient Send Fail [SOCK: %d, SEQ: %d] --> %s (%d)"), (LPCTSTR)SafeString(lpszName), iSocketIndex, iSequence, lpszDesc, code); + LogMsg(msg); +} + +void LogStart(LPCTSTR lpszAddress, USHORT port, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sStart OK --> (%s#%d)"), (LPCTSTR)SafeString(lpszName), lpszAddress, port); + LogMsg(msg); +} + +void LogStartFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sStart Fail --> %s (%d) [%d]"), (LPCTSTR)SafeString(lpszName), lpszDesc, code, ::GetLastError()); + LogMsg(msg); +} + +void LogStop(LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sStop"), (LPCTSTR)SafeString(lpszName)); + + LogMsg(msg); +} + +void LogSend(CONNID dwConnID, LPCTSTR lpszContent, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu Send OK --> %s"), (LPCTSTR)SafeString2(lpszName), dwConnID, lpszContent); + LogMsg(msg); +} + +void LogSend(LPCTSTR lpszContent, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sSend OK --> %s"), (LPCTSTR)SafeString(lpszName), lpszContent); + LogMsg(msg); +} + +void LogSending(CONNID dwConnID, LPCTSTR lpszContent, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu Sending --> %s"), (LPCTSTR)SafeString2(lpszName), dwConnID, lpszContent); + LogMsg(msg); +} + +void LogSendFail(CONNID dwConnID, DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu Send Fail --> %s (%d)"), (LPCTSTR)SafeString2(lpszName), dwConnID, lpszDesc, code); + LogMsg(msg); +} + +void LogSendFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sSend Fail --> %s (%d)"), (LPCTSTR)SafeString(lpszName), lpszDesc, code); + LogMsg(msg); +} + +void LogDisconnect(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, Disconnect OK"), (LPCTSTR)SafeString2(lpszName), dwConnID); + LogMsg(msg); +} + +void LogDisconnectFail(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, Disconnect Fail"), (LPCTSTR)SafeString2(lpszName), dwConnID); + LogMsg(msg); +} + +void LogDisconnect2(CONNID dwConnID, BOOL bForce, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, Disconnect OK (%c)"), (LPCTSTR)SafeString2(lpszName), dwConnID, bForce ? 'F' : 'N'); + LogMsg(msg); +} + +void LogDisconnectFail2(CONNID dwConnID, BOOL bForce, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, Disconnect Fail (%c)"), (LPCTSTR)SafeString2(lpszName), dwConnID, bForce ? 'F' : 'N'); + LogMsg(msg); +} + +void LogDisconnectLong(DWORD dwSeconds, BOOL bForce, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sDisconnect OK (%us, %c)"), (LPCTSTR)SafeString(lpszName), dwSeconds, bForce ? 'F' : 'N'); + LogMsg(msg); +} + +void LogDisconnectFailLong(DWORD dwSeconds, BOOL bForce, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sDisconnect Fail (%us, %c)"), (LPCTSTR)SafeString(lpszName), dwSeconds, bForce ? 'F' : 'N'); + LogMsg(msg); +} + +void LogPause(CONNID dwConnID, BOOL bPause, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, %s OK"), (LPCTSTR)SafeString2(lpszName), dwConnID, bPause ? "Pause" : "Unpause"); + LogMsg(msg); +} + +void LogPauseFail(CONNID dwConnID, BOOL bPause, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, %s Fail --> %s (%d)"), (LPCTSTR)SafeString2(lpszName), dwConnID, bPause ? "Pause" : "Unpause", ::GetLastErrorStr(), ::GetLastError()); + LogMsg(msg); +} + +void LogConnect(LPCTSTR lpszAddress, USHORT usPort, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sConnecting to %s#%d ..."), (LPCTSTR)SafeString(lpszName), lpszAddress, usPort); + LogMsg(msg); +} + +void LogConnectFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %sConnect Fail --> %s (%d)"), (LPCTSTR)SafeString(lpszName), lpszDesc, code); + LogMsg(msg); +} + +void LogRelease(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, Release OK"), (LPCTSTR)SafeString2(lpszName), dwConnID); + LogMsg(msg); +} + +void LogReleaseFail(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, Release Fail"), (LPCTSTR)SafeString2(lpszName), dwConnID); + LogMsg(msg); +} + +void LogDetect(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, Detect Connection OK"), (LPCTSTR)SafeString2(lpszName), dwConnID); + LogMsg(msg); +} + +void LogDetectFail(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T("# %s%zu, Detect Connection Fail"), (LPCTSTR)SafeString2(lpszName), dwConnID); + LogMsg(msg); +} + +void LogOnConnect(CONNID dwConnID, const CString& strAddress, USHORT usPort, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("local address: %s#%d"), (LPCTSTR)strAddress, usPort); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CONNECT, content_len, lpszContent, lpszName); + + LogInfoMsg(msg); +} + +void LogOnConnect2(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T(" > [ %s%zu, %s ]"), (LPCTSTR)SafeString2(lpszName), dwConnID, EVT_ON_CONNECT); + LogMsg(msg); +} + +void LogOnConnect3(CONNID dwConnID, const CString& strAddress, USHORT usPort, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("remote address: %s#%d"), (LPCTSTR)strAddress, usPort); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CONNECT, content_len, lpszContent, lpszName); + + LogInfoMsg(msg); +} + +void LogOnHandShake2(CONNID dwConnID, LPCTSTR lpszName) +{ + CString msg; + msg.Format(_T(" > [ %s%zu, %s ]"), (LPCTSTR)SafeString2(lpszName), dwConnID, EVT_ON_HAND_SHAKE); + LogMsg(msg); +} + +void LogOnClose(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CLOSE, 0, nullptr, lpszName); + + LogInfoMsg(msg); +} + +void PostOnSend(CONNID dwConnID, const BYTE* pData, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[20]; + wsprintf(lpszContent, _T("(%d bytes)"), iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_SEND, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnSendTo(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, const BYTE* pData, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("<%s#%d> (%d bytes)"), lpszAddress, usPort, iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_SEND, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnReceive(CONNID dwConnID, const BYTE* pData, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[20]; + wsprintf(lpszContent, _T("(%d bytes)"), iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_RECEIVE, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnReceiveFrom(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, const BYTE* pData, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("<%s#%d> (%d bytes)"), lpszAddress, usPort, iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_RECEIVE, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnReceiveCast(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, const BYTE* pData, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("<%s#%d> (%d bytes)"), lpszAddress, usPort, iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_RECEIVE, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnClose(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CLOSE, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostOnError(CONNID dwConnID, int enOperation, int iErrorCode, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("OP: %d, CODE: %d"), enOperation, iErrorCode); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_ERROR, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnError2(CONNID dwConnID, int enOperation, int iErrorCode, LPCTSTR lpszAddress, USHORT usPort, const BYTE* pBuffer, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[150]; + wsprintf(lpszContent, _T("<%s#%d> OP: %d, CODE: %d (DATA: 0x%X, LEN: %d>"), lpszAddress, usPort, enOperation, iErrorCode, pBuffer, iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_ERROR, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnAccept(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, BOOL bPass, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("%s (%s#%d)"), bPass ? _T("PASS") : _T("REJECT"), lpszAddress, usPort); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_ACCEPT, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnAccept2(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_ACCEPT, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostOnHandShake(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_HAND_SHAKE, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostOnPrepareListen(LPCTSTR lpszAddress, USHORT usPort, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("bind address: %s#%d"), lpszAddress, usPort); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(0, EVT_ON_PREPARE_LISTEN, content_len, lpszContent, lpszName); + + LogInfoMsg(msg); +} + +void PostOnPrepareConnect(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_PREPARE_CONNECT, 0, nullptr, lpszName); + + LogInfoMsg(msg); +} + +void PostOnConnect(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("local address: %s#%d"), lpszAddress, usPort); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CONNECT, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnConnect2(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("remote address: %s#%d"), lpszAddress, usPort); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CONNECT, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnConnect3(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CONNECT, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostOnShutdown(LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(0, EVT_ON_SHUTDOWN, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostServerStatics(const LONGLONG& llTotalSent, const LONGLONG& llTotalReceived, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("*** Summary: send - %lld, recv - %lld ***"), llTotalSent, llTotalReceived); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(0, EVT_ON_END_TEST, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostServerTemporaryStatics(const LONGLONG& llTotalSent, const LONGLONG& llTotalReceived, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("*** Summary: send - %lld, recv - %lld ***"), llTotalSent, llTotalReceived); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(0, EVT_ON_STAT_TEST, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostTimeConsuming(DWORD dwTickCount, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("Total Time Consuming: %u"), dwTickCount); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(0, EVT_ON_END_TEST, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +#ifdef _NEED_HTTP + +void PostOnMessageBegin(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_MESSAGE_BEGIN, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostOnRequestLine(CONNID dwConnID, LPCSTR lpszMethod, USHORT usUrlFieldSet, LPCSTR lpszUrl, LPCTSTR lpszName) +{ + USES_CONVERSION; + + int content_len = (int)(strlen(lpszMethod) + strlen(lpszUrl) + 20); + LPTSTR lpszContent = new TCHAR[content_len]; + + wsprintf(lpszContent, _T("[%s/0x%02X] : %s"), A2T(lpszMethod), usUrlFieldSet, A2T(lpszUrl)); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_REQUEST_LINE, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnStatusLine(CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc, LPCTSTR lpszName) +{ + USES_CONVERSION; + + int content_len = (int)(strlen(lpszDesc) + 10); + LPTSTR lpszContent = new TCHAR[content_len]; + + wsprintf(lpszContent, _T("(%u) : %s"), usStatusCode, A2T(lpszDesc)); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_STATUS_LINE, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnHeader(CONNID dwConnID, LPCSTR lpszHeaderName, LPCSTR lpszHeaderValue, LPCTSTR lpszName) +{ + USES_CONVERSION; + + int content_len = (int)(strlen(lpszHeaderName) + strlen(lpszHeaderValue) + 10); + LPTSTR lpszContent = new TCHAR[content_len]; + + wsprintf(lpszContent, _T("%s: %s"), A2T(lpszHeaderName), A2T(lpszHeaderValue)); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_HEADER, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnHeadersComplete(CONNID dwConnID, LPCSTR lpszSummary, LPCTSTR lpszName) +{ + USES_CONVERSION; + + static LPCTSTR PREFIX = _T("* * * * * * * * * Summary * * * * * * * * *\r\n"); + static int PREFIX_LEN = lstrlen(PREFIX); + + LPCTSTR lpszSummaryT = A2CT(lpszSummary); + + + int content_len = lstrlen(lpszSummaryT) + PREFIX_LEN + 1; + LPTSTR lpszContent = new TCHAR[content_len]; + + memcpy(lpszContent, PREFIX, PREFIX_LEN * sizeof(TCHAR)); + memcpy(lpszContent + PREFIX_LEN, lpszSummaryT, (content_len - PREFIX_LEN) * sizeof(TCHAR)); + + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_HEADERS_COMPLETE, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnBody(CONNID dwConnID, const BYTE* pData, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[20]; + wsprintf(lpszContent, _T("(%d bytes)"), iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_BODY, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnChunkHeader(CONNID dwConnID, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[20]; + wsprintf(lpszContent, _T("(%d bytes)"), iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CHUNK_HEADER, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnChunkComplete(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_CHUNK_COMPLETE, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostOnMessageComplete(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_MESSAGE_COMPLETE, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostOnUpgrade(CONNID dwConnID, EnHttpUpgradeType enUpgradeType, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[20]; + wsprintf(lpszContent, _T("(type: %d)"), enUpgradeType); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_UPGRADE, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnParseError(CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc, LPCTSTR lpszName) +{ + USES_CONVERSION; + + int content_len = (int)(strlen(lpszErrorDesc) + 10); + LPTSTR lpszContent = new TCHAR[content_len]; + + wsprintf(lpszContent, _T("(%i) : %s"), iErrorCode, A2T(lpszErrorDesc)); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_PARSE_ERROR, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnWSMessageHeader(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[100]; + wsprintf(lpszContent, _T("(fin: %d, rsv: 0x%x, oc: 0x%x, mask: %d, len: %lld)"), bFinal, iReserved, iOperationCode, lpszMask ? 1 : 0, ullBodyLen); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_WS_MSG_HEADER, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnWSMessageBody(CONNID dwConnID, const BYTE* pData, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[20]; + wsprintf(lpszContent, _T("(%d bytes)"), iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_WS_MSG_BODY, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostOnWSMessageComplete(CONNID dwConnID, LPCTSTR lpszName) +{ + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_WS_MSG_COMPLETE, 0, nullptr, lpszName); + + PostInfoMsg(msg); +} + +void PostUncompressBody(CONNID dwConnID, int iLength, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[40]; + wsprintf(lpszContent, _T("(%d bytes)"), iLength); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_UNCOMPRESS_BODY, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +void PostUncompressBodyFail(CONNID dwConnID, int iResult, LPCTSTR lpszName) +{ + LPTSTR lpszContent = new TCHAR[40]; + wsprintf(lpszContent, _T("(rs: %d)"), iResult); + int content_len = lstrlen(lpszContent); + info_msg* msg = info_msg::Construct(dwConnID, EVT_ON_UNCOMPRESS_BODY_FAIL, content_len, lpszContent, lpszName); + + PostInfoMsg(msg); +} + +#endif + +void PostInfoMsg(info_msg* msg) +{ + LogInfoMsg(msg); +} + +void LogInfoMsg(info_msg* pInfoMsg) +{ + CString msg; + + if(pInfoMsg->name) + { + if(pInfoMsg->connID > 0) + { + if(pInfoMsg->contentLength > 0) + msg.Format(_T("[ %s #%zu, %s ] -> %s"), pInfoMsg->name, pInfoMsg->connID, pInfoMsg->evt, pInfoMsg->content); + else + msg.Format(_T("[ %s #%zu, %s ]"), pInfoMsg->name, pInfoMsg->connID, pInfoMsg->evt); + } + else + { + if(pInfoMsg->contentLength > 0) + msg.Format(_T("[ %s - %s ] -> %s"), pInfoMsg->name, pInfoMsg->evt, pInfoMsg->content); + else + msg.Format(_T("[ %s - %s ]"), pInfoMsg->name, pInfoMsg->evt); + } + } + else + { + if(pInfoMsg->connID > 0) + { + if(pInfoMsg->contentLength > 0) + msg.Format(_T("[ %zu, %s ] -> %s"), pInfoMsg->connID, pInfoMsg->evt, pInfoMsg->content); + else + msg.Format(_T("[ %zu, %s ]"), pInfoMsg->connID, pInfoMsg->evt); + } + else + { + if(pInfoMsg->contentLength > 0) + msg.Format(_T("[ %s ] -> %s"), pInfoMsg->evt, pInfoMsg->content); + else + msg.Format(_T("[ %s ]"), pInfoMsg->evt); + } + } + + LogMsg(msg); + + info_msg::Destruct(pInfoMsg); +} + +void LogMsg(const CString& msg) +{ + puts(msg); +} + +BOOL SplitStr(LPCTSTR lpszSrc, vector& vtItem, LPCTSTR pszSepectors, LPCTSTR pszQMarks) +{ + vtItem.clear(); + + CString strQMarks = pszQMarks; + CString strSepectors = pszSepectors; + + if(strSepectors.IsEmpty()) + strSepectors = _T(" "); + + if(!strQMarks.IsEmpty()) + if(strQMarks.FindOneOf(strSepectors) != -1) + return FALSE; + + BOOL bRetVal = TRUE; + CString strSrc = lpszSrc; + + while(!strSrc.Trim(strSepectors).IsEmpty()) + { + CString strItem; + + int iSrcLen = strSrc.GetLength(); + int iPos1 = strSrc.FindOneOf(strSepectors); + int iPos2 = !strQMarks.IsEmpty() ? strSrc.FindOneOf(strQMarks) : -1; + int iPos3 = -1; + + if(iPos1 == -1 && iPos2 == -1) + strItem = strSrc; + else if(iPos1 != -1 && (iPos1 < iPos2 || iPos2 == -1)) + strItem = strSrc.Left(iPos1); + else // (iPos1 > iPos2 || iPos1 == -1) + { + TCHAR tc = strSrc[iPos2]; + iPos3 = strSrc.Find(tc, iPos2 + 1); + if(iPos3 != -1) + strItem = strSrc.Mid(iPos2 + 1, iPos3 - iPos2 - 1); + else + { + vtItem.clear(); + bRetVal = FALSE; + break; + } + } + + vtItem.push_back(strItem); + + strSrc = strSrc.Right(iPos3 == -1 ? (iSrcLen - (iPos1 == -1 ? strItem.GetLength() : iPos1 + 1)) : (iSrcLen - iPos3 - 1)); + } + + return bRetVal; +} + +sa_family_t GuessAddrFamily(LPCTSTR lpszAddress) +{ + if (!lpszAddress || lpszAddress[0] == 0) + return AF_UNSPEC; + + if(::StrChr(lpszAddress, ':')) + 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; +} + +CBufferPtr* GeneratePkgBuffer(DWORD seq, LPCTSTR lpszName, short age, LPCTSTR lpszDesc) +{ + USES_CONVERSION; + + LPCSTR name = T2A((LPTSTR)lpszName); + LPCSTR desc = T2A((LPTSTR)lpszDesc); + int desc_len = (int)strlen(desc) + 1; + int body_len = (int)offsetof(TPkgBody, desc) + desc_len; + + TPkgBody* pBody = (TPkgBody*)alloca(body_len); + memset(pBody, 0, body_len); + + pBody->age = age; + strcpy(pBody->name, name); + strcpy(pBody->desc, desc); + + TPkgHeader header; + header.seq = seq; + header.body_len = body_len; + + return GeneratePkgBuffer(header, *pBody); +} + +CBufferPtr* GeneratePkgBuffer(const TPkgHeader& header, const TPkgBody& body) +{ + int header_len = sizeof(TPkgHeader); + int body_len = header.body_len; + + CBufferPtr* pBuffer = new CBufferPtr(header_len + body_len); + + memcpy(pBuffer->Ptr(), (BYTE*)&header, header_len); + memcpy(pBuffer->Ptr() + header_len, (BYTE*)&body, body_len); + + return pBuffer; +} + +LPCTSTR GetLoopbackAddress(LPCTSTR lpszLikeAddress) +{ + sa_family_t f = GuessAddrFamily(lpszLikeAddress); + + if(f == AF_INET) + return IPV4_LOOPBACK_ADDRESS; + if(f == AF_INET6) + return IPV6_LOOPBACK_ADDRESS; + + return nullptr; +} + +LPCTSTR GetAnyAddress(LPCTSTR lpszLikeAddress) +{ + sa_family_t f = GuessAddrFamily(lpszLikeAddress); + + if(f == AF_INET) + return IPV4_ANY_ADDRESS; + if(f == AF_INET6) + return IPV6_ANY_ADDRESS; + + return nullptr; +} + +LPCTSTR g_lpszDefaultCookieFile = GetDefaultCookieFile(); + +LPCTSTR GetDefaultCookieFile() +{ + static TCHAR c_szCookieFile[MAX_PATH] = {0}; + + if(c_szCookieFile[0] == 0) + { + CString strName = ::GetModuleFileName(); + ASSERT(!strName.IsEmpty()); + + strName.Append(".cki"); + lstrcpy(c_szCookieFile, strName); + } + + return c_szCookieFile; +} + +#ifdef _NEED_SSL + +#include "../../src/common/FuncHelper.h" + +#define SSL_CERT_RELATIVE_PATH_1 _T("/hp-ssl-cert/") +#define SSL_CERT_RELATIVE_PATH_2 _T("/../../ssl-cert/") + +LPCSTR g_c_lpszPemCert = + "-----BEGIN CERTIFICATE-----\n" + "MIIDszCCApugAwIBAgIBATANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJDTjEL\n" + "MAkGA1UECAwCR0QxCzAJBgNVBAcMAkdaMQwwCgYDVQQKDANTU1QxDzANBgNVBAsM\n" + "Bkplc3NtYTETMBEGA1UEAwwKamVzc21hLm9yZzEeMBwGCSqGSIb3DQEJARYPbGRj\n" + "c2FhQDIxY24uY29tMCAXDTI0MDYyNjA1MjUwOFoYDzIyNDMwNzA5MDUyNTA4WjBu\n" + "MQswCQYDVQQGEwJDTjELMAkGA1UECAwCR0QxDDAKBgNVBAoMA1NTVDEPMA0GA1UE\n" + "CwwGSmVzc21hMRMwEQYDVQQDDApqZXNzbWEub3JnMR4wHAYJKoZIhvcNAQkBFg9s\n" + "ZGNzYWFAMjFjbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCD\n" + "+MyrJEKCheRoOpMRjR78S8hr9W7XN0/EZWyVKwXRT7EE0aGiQdH/W2a+qpWRMa6E\n" + "Qi47zdBnt0P8ZoFiItQhuhwUJ064afpVoaHHX25UdbF8r+sRTofadughETBBj2Cf\n" + "qh0ia6EOB0QvpJpywWmGZPoMtypjbUiTb/YGOJh2qsVr67MN/E48vt7qt0VxF9SE\n" + "pucvqhraTBljWCeRVCae2c0yBSpq/n+7NhamK7+g3xxCKWRz4pN3wrIoEsXTboTh\n" + "z940caDgthCc23VJ080DN44jZg6c87huKIuxbebJqw2HCM4DwrW+OSzTLszpFAXZ\n" + "yarllOzWnBut20zmYnl1AgMBAAGjTTBLMAkGA1UdEwQCMAAwHQYDVR0OBBYEFJ5E\n" + "RJmJ4pUzEbcU9Yge6nr0oi51MB8GA1UdIwQYMBaAFN49z48DywmoD4cNTQgC6nn2\n" + "QJoUMA0GCSqGSIb3DQEBCwUAA4IBAQBpoSFfDDDKMAy95tSROpYu5WSWQXe6B7kl\n" + "PGJAF6mWe/4b7jHQqDUVkEmFmbMWUAtpTC3P01TrV77dhIosAnC/B76fb7Pto8W4\n" + "cjGpWAT0sSegZuhnLtguTGlnR0vVSh/yRRDEtjN8loWpu3BLWVHYOKnn62QGfY0B\n" + "sRGrfZsKvwB+1w+HOvGopnWv6UYwrzEKthjPMR65rOsoManOv24ua8baJmq0gqF9\n" + "752kD8n703uWUBx79/QlNIPMZC1iUIi1mEjyrTgSag6+3sWAIKihaoF/Nf9d01nw\n" + "iL16EIT5dJ0QJWDCeIxhuTZckw+gL1pBeQU7pqzKHPnvo+8GBnTG\n" + "-----END CERTIFICATE-----\n"; + +LPCSTR g_c_lpszPemKey = + "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + "MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIK2UJW9QXIj4CAggA\n" + "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCDDZQLhAdT91jd6v/5H0+GBIIE\n" + "0PH6tKl+nPi8sU0ryjxDIrHwrT/ZFah+3TAHGE/YFAOZnzRyCFHQTvUZX4p8eSmw\n" + "WOpt5NBUPJ3mT0Ctt7lGBRy4AXSyBrFSamlTruM3P1e3ijluYjMbweZFfCWPq8c/\n" + "jPjbcUkXe6mD96aPSTt/jIunexS8AKovu8c/bFLyTLDk38lATc+GnXQQJ0KiXCRu\n" + "vpjVSKcv2Br6cWqaNTZ71FvH1RmSD6K6givc0w65pKruHYTMApIRR8YC5Y0vx0gD\n" + "6nS12LV/EJEtxTfZFlrzZTRWZISPIzYGuTfS+3bPePlxpbwzhN6vmvgjKhdk+3lN\n" + "3W3ZfqODNhoOKG+mG5Fdj7vR2PU1UND6UUd3+FrzWkXikmalAAwKzRLnyTR1T2rl\n" + "RhM0Qe/HZianeEQTHpCw27gOz1OMw2EKfIEHM6W2BKGOTY5ls5dqgMfP1ZoQUrOr\n" + "59tJo4GpWYFGCuHhTEa5OS/gsgnzymGrkuEwPsdSQaBdzV7lFGTv2/ryKX+vNm9V\n" + "CmKw0nHzOVP19+WL4vPDtbRnLUk8KV9Mg7PdSbGbNcMmTEBk8ju8OvjIUIWZbRTa\n" + "n5C6fhD1DYZcczmlCILYgXyJISu7EDf3z9cKRAf5VbRAedDMB/xHWmrmlxUJ37Kt\n" + "tVgaCD0U6Q3q+3y6OOwugc8UbSo4yA/DbLlG0/U7afwQaNxTLa4HGBQljpoNStIt\n" + "Vgfy2olqHXaf2doSQtsYEl9MHa6neuGfZQMtonDkejnx4KKU+cMhe+KijEUwieYx\n" + "7aoPB71b82XODquDPAL5zOegj0eYgKn5iXyOx5W44S34zfclxtxxgfsDJ3qJ9qoL\n" + "sSenrQ3xAYHJSZRcqEgO31XhoEnkyt1V7G0Bk4/GUMD6uQudr3nsw/ulJpAlNK15\n" + "ZxTSKWrtwOWdwcTj6B14K6wcqMFVNF1Ydbv/qp0b5q5S/orYHzRIPcFmdOAIsjyO\n" + "6na7+D31BH/4pf+TASBNqRNRw5CBqNcGcfiXk11AywxUnmD5ZvC/C0pTpTD/9qC4\n" + "LucWJ0sNAtPq8suFjKqQ+wMvq3rUh050NRm2cm2nUJLxafTnr0v3+kKYbVW8pSWB\n" + "NMelZMVGF1MDYBujg8Mw/xuMhPeLozCZeKmo7eu7aDMXzQMZLfAEJAzU9Du8H4nq\n" + "GgQVUgEkS5rdbjZGkHP0FuM8m8lueKEPDYwHCJv9Be5Z/uxp9OO/Lmdlha0J7gJu\n" + "pihNkAYVxRst96b5okXKooYi/TZxAdThoPYH28VwinGR1I3/8I3M5DbUPIgHhDeB\n" + "ga3u7jt7ZNDUgavukUD0S7WioRb5ooXrXGZ1xmzKLCmMdCDC5S32fQS0wRGfVoMl\n" + "hWbaT+0uak+fOpqVRxSNyE3Ek788ua5iPHaTSXJSoe5lv7OQKDSZ/+wFeLmDPf4M\n" + "BHL2gBLD6RNkz5cWgy14sQcJKNAnyptU4EGPyURZcB8APtB/ITAS2Az/JSxvSBgq\n" + "g/L1FujnP2QEpWpVKkTNxsF867bUPN34KrlPKYjNqcKA2pD4fkFoKSeeNtOEWa++\n" + "d6q9y+mDD97SnIFAAhDFlukzXtyl4MU6uiqRldFiuEt3KzvV19n8M+NyyYIFhfdg\n" + "6TkYEbMJPQ/Y3EGNmyMqbFdJzrdl/B8pr7JQnikTfUZZ\n" + "-----END ENCRYPTED PRIVATE KEY-----\n"; + +LPCSTR g_c_lpszCAPemCert = + "-----BEGIN CERTIFICATE-----\n" + "MIID2TCCAsGgAwIBAgIUM8TTtPU+ejzffYXCcs/zZsU7OuIwDQYJKoZIhvcNAQEL\n" + "BQAwezELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJHWjEMMAoG\n" + "A1UECgwDU1NUMQ8wDQYDVQQLDAZKZXNzbWExEzARBgNVBAMMCmplc3NtYS5vcmcx\n" + "HjAcBgkqhkiG9w0BCQEWD2xkY3NhYUAyMWNuLmNvbTAgFw0yNDA2MjYwNTA0NDNa\n" + "GA8yMjcwMTEyNDA1MDQ0M1owezELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQsw\n" + "CQYDVQQHDAJHWjEMMAoGA1UECgwDU1NUMQ8wDQYDVQQLDAZKZXNzbWExEzARBgNV\n" + "BAMMCmplc3NtYS5vcmcxHjAcBgkqhkiG9w0BCQEWD2xkY3NhYUAyMWNuLmNvbTCC\n" + "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAML+v79+aLQt0Za0dTIZHI5B\n" + "NDs0g5G8bhdOTlW/kNWflaziZ3GY6d6nJSkQ5e29kyFKxlOD6Gls6bOJ86U71u4R\n" + "bCmoFvRTDH4q2cJ/+PbiioLpNveDG6lnRCs9JNRQoJrkpRo6urnVnAdsIf6UFjLI\n" + "dlByNMPGYJ0V8/oKJG5Vu5gcbZV0jVA5+tswkH/zquexEXoKvp18mcwl+pNc/LwW\n" + "0WnGj0uoJjxHg4GsS78PASjhxMR/2d/1OpgPauldFaNHjVPtaLqJnuejwA6M6Sz8\n" + "iFPybAQAMpHL9W8kf08jtbnFvnm4ibUkQL5h+OJoIEQa9AVZOSoFG2/g5Zcn8X8C\n" + "AwEAAaNTMFEwHQYDVR0OBBYEFN49z48DywmoD4cNTQgC6nn2QJoUMB8GA1UdIwQY\n" + "MBaAFN49z48DywmoD4cNTQgC6nn2QJoUMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI\n" + "hvcNAQELBQADggEBALJnYrYBSZLyYX14FQ04zxG3AX0CtQzNOOa7LDrr+H8Ly+nK\n" + "qS87gg2njMVZH1zM2demtMwydR/F2Ui8ggaduMvc9h5YgQKEwYl8KarJEY03oZoe\n" + "zbQGBxCXpDOtMs1vujzcl/iZbSzwEDF3g4la5U8q4MlmfGFKz9CJbvoxecqYA206\n" + "nNbW2XZsW/xMiQv6iAw5iP/LOR9HAyxcvXIsL790nfcgnTYLmyP254Dj4outc6R+\n" + "PA+f/c1FvkbUBTR5vJt2tsvHcNU218rY2hyOIhDmZeUWprqBO19sUk3scLbVPr3+\n" + "WEWEl2XaCekKuPtAnMgVQuFsocXGyiuIhkOe5Z4=\n" + "-----END CERTIFICATE-----\n"; + +LPCSTR g_s_lpszPemCert = + "-----BEGIN CERTIFICATE-----\n" + "MIIEJjCCAw6gAwIBAgIBAzANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJDTjEL\n" + "MAkGA1UECAwCR0QxCzAJBgNVBAcMAkdaMQwwCgYDVQQKDANTU1QxDzANBgNVBAsM\n" + "Bkplc3NtYTETMBEGA1UEAwwKamVzc21hLm9yZzEeMBwGCSqGSIb3DQEJARYPbGRj\n" + "c2FhQDIxY24uY29tMCAXDTI0MDYyNjA1MTY1NFoYDzIyNDMwNzA5MDUxNjU0WjBu\n" + "MQswCQYDVQQGEwJDTjELMAkGA1UECAwCR0QxDDAKBgNVBAoMA1NTVDEPMA0GA1UE\n" + "CwwGSmVzc21hMRMwEQYDVQQDDApqZXNzbWEub3JnMR4wHAYJKoZIhvcNAQkBFg9s\n" + "ZGNzYWFAMjFjbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7\n" + "x3ilLjZtH2ZKuofj4FpVl/IF2yDI5503YQbwllxp7kNEaqTyjJUgmLlZBbwHQzTD\n" + "xfPk/nZ/m3xUHsVjwXMZqNNufgtSLaBoK4CvBAOTkELphIOZdJYPpuaU66W0phjG\n" + "VM2R4EFm/rTXddZ7N6sq3fYEL0bxqUJ6fW/+0QhdNSwfdevdAHgOmGkrTj5rILJ8\n" + "A7FwbkcuV5vBWZ+9ZhNG4csqAjH5LLLCn5hJdhE9WqUp+slfIuXE5vZGDpCQrcc5\n" + "I1qWt8VNfdwzaBDL/hXl7pAiVpZRvQxyJgbUMLr2QrYFwrPkgpncU7R7AyT/C0tO\n" + "vgPVZb+IGqbf+NrbHEk3AgMBAAGjgb8wgbwwHwYDVR0jBBgwFoAU3j3PjwPLCagP\n" + "hw1NCALqefZAmhQwCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAwYgYDVR0RBFswWYIJ\n" + "bG9jYWxob3N0ggpqZXNzbWEub3JnggwqLmplc3NtYS5vcmeCCmplc3NtYS5jb22C\n" + "DCouamVzc21hLmNvbYIKamVzc21hLm5ldIIMKi5qZXNzbWEubmV0MB0GA1UdDgQW\n" + "BBRZ97VSkfue5s8/OkYvUe+lXUgsHzANBgkqhkiG9w0BAQsFAAOCAQEAvM1QrhTL\n" + "/I1ws4fozKQlfmd3S0AdfFJX4BMTbp88proWpjrNZt6ZJ3EETHKcXu0z4gn3jWy6\n" + "1d8bDCytYQXjpG3Na7Ym5jc/E7wc+XUCUxlW41G/HnaLaIVccmRbyxMOWkr3yUX1\n" + "tc8rxUSKWzZBmYtJ49QzIvNzDuoLklE44g8XuqsZZlOZ2wRWJxc/hDG0MkKhRnc1\n" + "mqeaoY/79QZNE1RvX/aRRJoSl7NQ00/rMP8MU6OMzPvbIsMVK2uT+BVZG0RZJXaG\n" + "ikQJvxYZrDVZdRZL6tWPtS2wI49KkzGHNH4S1Fni/dDq3P2rxzisMY1gtKQLeVYY\n" + "eTQDDybjTWWiTg==\n" + "-----END CERTIFICATE-----\n"; + +LPCSTR g_s_lpszPemKey = + "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + "MIIFHTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIgAqUxS2ufB0CAggA\n" + "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBD8E9f397mpmzmM9zYTWt2DBIIE\n" + "wIo2i0YH7cT/WCLmSnVvpsbayeo0mbVUFS4xp2VerQUS+UXHlrrOFeKU8NYfG6SS\n" + "m+3PXksvUlDV9iGT0p/w2Fm1GVKhE7UAeTJkff7KWi2pc926DUbYxhFOUNyCnrQS\n" + "pWCdV6M/243A0kHg6kIlSblsXjzKMC6eSlh2FGa7M7my9A+nu0WGOqvazkOm/8jk\n" + "h20xmB3t/Qa8NQp59E9oLXs5+CokIKL17+PmtBKeKpQBxAUtFjcqRAugIiNpPjOS\n" + "K99cY/Tt1C8ugMIsFH1/4HzFDYiSDRZ630dKOzBruprHVkIvQXI1CW6edPlfFZKG\n" + "yHeH95kigPVWUxWwluwALVmlPwD4h/GWHGOS7HH6x4ubStogjmC2B9f4VQCd6YRu\n" + "lee3cdDvdqLoRCoU48SoM/+RMR3NpF508ulWhKrDZ4eGcqdUYrx3rWVyOrmfMJDe\n" + "kfckGKnhCA1hL3wb/HQuAs2st2lDKBwsYIsOc8UXhueRFHKk+W0O/5kureMPF14O\n" + "DAxqAW3meHq7CLY8WuqatptIrJVDT8Wkbx47tXhLapTwlI0Cbf2AGfJIWSGmM+VF\n" + "I5l13pW2tycQnAXzSdd0y9wE0df/EoyXfIJeEfBNkzVhkIIC3KmOiI8BCnei6UR9\n" + "jun4+6aeFyfGybJ04ybixXyFsCsVUa5QcnhPwJvC8QsVFpuzOttQ5cf4Xn57DyxT\n" + "4CzWieJ6iQpfAJahRcj/4O6KRmWuMPpkK8XsCgzOYM1MxETq4HbqXEp68KiT6Q9r\n" + "jEAmbfZ8NdPvAPZ/iXKtA/eaMDDy6EbzDscUBg/TSxsF286F+wH2kXvkbwL2E/zh\n" + "LsTcjsGUdNKxlDJaivi7dDbSzxzvcDYRh+8Bd/vOw2gJF2ohwXXp3GKTVu71LS+b\n" + "YruQ55Lauh761ziI/z7qZw9ko8erb5vcsqLh9duqtxTBnQEd05ufFhNnXk4Vq8Cp\n" + "hr30Qy5sJ7TUuAVs2RSuGHd0Q5l8NGLjQwtkDx5ofizZKQOMWTq8S3IA98QyFka0\n" + "e+XaGGQ/KZJciIoCkuzAX4mn/aIffMldQIEg5ybslBc326SdTe52ex5YlmUuyvbO\n" + "zDotjC/eeQEFvq6Xb14N+u7mp8xL5Dlro79aL3VNNGa3lgKP6lWtMjgcyZrWMdc4\n" + "xaV0sVbfRO8Pj4y3cZGXol529zSNSIc7wT2/kyvF6RgJEcluAIPAJ8ea6CXKqDfe\n" + "sYZDL1emVoKMoFy3YsnEI+py2xxSsU4pjGPanZZaVfrDfw3qnQWPovfOx5fVc6Om\n" + "U55o3nbR5wtjPlQmcdVlT/fo7m/OUu3UgdjyLFcljeezJGeskJ3PMYbSsi7Mf+yF\n" + "/BEW6IfqicjG9TTMGzNACHH0iqAzW6lrC60UXMRIXrs/segt3+r5JqfRH58TMBR+\n" + "O5xk6sjOL4uqTsJRmbMapHf4tPxli8PdvNYN+YTQ4tlwazrckwgC5TJ3GA5JM3Ox\n" + "ZQIIKf1joemxq/VT2IpsqwMY67KC4OUOiRy471guGdljFarACe8rzZh8BHONveJd\n" + "XDgM0oPBkR9z4BGlfbBgAG0bIRNSXp8yGaZMiuIHbI8I4TnR84KgyUUscsIlH03A\n" + "8PQL73vd5pU4jC6WdOaXwkI=\n" + "-----END ENCRYPTED PRIVATE KEY-----\n"; + +LPCSTR g_s_lpszCAPemCert = + "-----BEGIN CERTIFICATE-----\n" + "MIID2TCCAsGgAwIBAgIUM8TTtPU+ejzffYXCcs/zZsU7OuIwDQYJKoZIhvcNAQEL\n" + "BQAwezELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQswCQYDVQQHDAJHWjEMMAoG\n" + "A1UECgwDU1NUMQ8wDQYDVQQLDAZKZXNzbWExEzARBgNVBAMMCmplc3NtYS5vcmcx\n" + "HjAcBgkqhkiG9w0BCQEWD2xkY3NhYUAyMWNuLmNvbTAgFw0yNDA2MjYwNTA0NDNa\n" + "GA8yMjcwMTEyNDA1MDQ0M1owezELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMQsw\n" + "CQYDVQQHDAJHWjEMMAoGA1UECgwDU1NUMQ8wDQYDVQQLDAZKZXNzbWExEzARBgNV\n" + "BAMMCmplc3NtYS5vcmcxHjAcBgkqhkiG9w0BCQEWD2xkY3NhYUAyMWNuLmNvbTCC\n" + "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAML+v79+aLQt0Za0dTIZHI5B\n" + "NDs0g5G8bhdOTlW/kNWflaziZ3GY6d6nJSkQ5e29kyFKxlOD6Gls6bOJ86U71u4R\n" + "bCmoFvRTDH4q2cJ/+PbiioLpNveDG6lnRCs9JNRQoJrkpRo6urnVnAdsIf6UFjLI\n" + "dlByNMPGYJ0V8/oKJG5Vu5gcbZV0jVA5+tswkH/zquexEXoKvp18mcwl+pNc/LwW\n" + "0WnGj0uoJjxHg4GsS78PASjhxMR/2d/1OpgPauldFaNHjVPtaLqJnuejwA6M6Sz8\n" + "iFPybAQAMpHL9W8kf08jtbnFvnm4ibUkQL5h+OJoIEQa9AVZOSoFG2/g5Zcn8X8C\n" + "AwEAAaNTMFEwHQYDVR0OBBYEFN49z48DywmoD4cNTQgC6nn2QJoUMB8GA1UdIwQY\n" + "MBaAFN49z48DywmoD4cNTQgC6nn2QJoUMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI\n" + "hvcNAQELBQADggEBALJnYrYBSZLyYX14FQ04zxG3AX0CtQzNOOa7LDrr+H8Ly+nK\n" + "qS87gg2njMVZH1zM2demtMwydR/F2Ui8ggaduMvc9h5YgQKEwYl8KarJEY03oZoe\n" + "zbQGBxCXpDOtMs1vujzcl/iZbSzwEDF3g4la5U8q4MlmfGFKz9CJbvoxecqYA206\n" + "nNbW2XZsW/xMiQv6iAw5iP/LOR9HAyxcvXIsL790nfcgnTYLmyP254Dj4outc6R+\n" + "PA+f/c1FvkbUBTR5vJt2tsvHcNU218rY2hyOIhDmZeUWprqBO19sUk3scLbVPr3+\n" + "WEWEl2XaCekKuPtAnMgVQuFsocXGyiuIhkOe5Z4=\n" + "-----END CERTIFICATE-----\n"; + +CString g_c_strCAPemCertFileOrPath; +CString g_c_strPemCertFile; +CString g_c_strPemKeyFile; + +CString g_s_strCAPemCertFileOrPath; +CString g_s_strPemCertFile; +CString g_s_strPemKeyFile; + +int g_c_iVerifyMode = SSL_VM_PEER; +LPCTSTR g_c_lpszCAPemCertFileOrPath = _T("ca.crt"); +LPCTSTR g_c_lpszPemCertFile = _T("client.cer"); +LPCTSTR g_c_lpszPemKeyFile = _T("client.key"); +LPCTSTR g_c_lpszKeyPasswod = _T("123456"); + +int g_s_iVerifyMode = SSL_VM_PEER | SSL_VM_FAIL_IF_NO_PEER_CERT; +LPCTSTR g_s_lpszCAPemCertFileOrPath = _T("ca.crt"); +LPCTSTR g_s_lpszPemCertFile = _T("server.cer"); +LPCTSTR g_s_lpszPemKeyFile = _T("server.key"); +LPCTSTR g_s_lpszKeyPasswod = _T("123456"); + +CString g_c_strCAPemCertFileOrPath2; +CString g_c_strPemCertFile2; +CString g_c_strPemKeyFile2; + +CString g_s_strCAPemCertFileOrPath2; +CString g_s_strPemCertFile2; +CString g_s_strPemKeyFile2; + +int g_c_iVerifyMode2 = SSL_VM_PEER; +LPCTSTR g_c_lpszCAPemCertFileOrPath2 = _T("ca2.crt"); +LPCTSTR g_c_lpszPemCertFile2 = _T("client2.cer"); +LPCTSTR g_c_lpszPemKeyFile2 = _T("client2.key"); +LPCTSTR g_c_lpszKeyPasswod2 = _T("123456"); + +int g_s_iVerifyMode2 = SSL_VM_PEER | SSL_VM_FAIL_IF_NO_PEER_CERT; +LPCTSTR g_s_lpszCAPemCertFileOrPath2 = _T("ca2.crt"); +LPCTSTR g_s_lpszPemCertFile2 = _T("server2.cer"); +LPCTSTR g_s_lpszPemKeyFile2 = _T("server2.key"); +LPCTSTR g_s_lpszKeyPasswod2 = _T("123456"); + +BOOL InitSSLParams(); +BOOL g_SSLParams = InitSSLParams(); + +BOOL InitSSLParams() +{ + ::SetCurrentPathToModulePath(); + + CString strCur = ::GetCurrentDirectory(); + CString strPath = strCur + SSL_CERT_RELATIVE_PATH_1; + + if(!CFile::IsDirectory(strPath)) + strPath = strCur + SSL_CERT_RELATIVE_PATH_2; + + if(g_c_lpszPemCertFile) + { + g_c_strPemCertFile = strPath + g_c_lpszPemCertFile; + g_c_lpszPemCertFile = g_c_strPemCertFile; + } + + if(g_c_lpszPemKeyFile) + { + g_c_strPemKeyFile = strPath + g_c_lpszPemKeyFile; + g_c_lpszPemKeyFile = g_c_strPemKeyFile; + } + + if(g_c_lpszCAPemCertFileOrPath) + { + g_c_strCAPemCertFileOrPath = strPath + g_c_lpszCAPemCertFileOrPath; + g_c_lpszCAPemCertFileOrPath = g_c_strCAPemCertFileOrPath; + } + + if(g_s_lpszPemCertFile) + { + g_s_strPemCertFile = strPath + g_s_lpszPemCertFile; + g_s_lpszPemCertFile = g_s_strPemCertFile; + } + + if(g_s_lpszPemKeyFile) + { + g_s_strPemKeyFile = strPath + g_s_lpszPemKeyFile; + g_s_lpszPemKeyFile = g_s_strPemKeyFile; + } + + if(g_s_lpszCAPemCertFileOrPath) + { + g_s_strCAPemCertFileOrPath = strPath + g_s_lpszCAPemCertFileOrPath; + g_s_lpszCAPemCertFileOrPath = g_s_strCAPemCertFileOrPath; + } + + if(g_c_lpszPemCertFile2) + { + g_c_strPemCertFile2 = strPath + g_c_lpszPemCertFile2; + g_c_lpszPemCertFile2 = g_c_strPemCertFile2; + } + + if(g_c_lpszPemKeyFile2) + { + g_c_strPemKeyFile2 = strPath + g_c_lpszPemKeyFile2; + g_c_lpszPemKeyFile2 = g_c_strPemKeyFile2; + } + + if(g_c_lpszCAPemCertFileOrPath2) + { + g_c_strCAPemCertFileOrPath2 = strPath + g_c_lpszCAPemCertFileOrPath2; + g_c_lpszCAPemCertFileOrPath2 = g_c_strCAPemCertFileOrPath2; + } + + if(g_s_lpszPemCertFile2) + { + g_s_strPemCertFile2 = strPath + g_s_lpszPemCertFile2; + g_s_lpszPemCertFile2 = g_s_strPemCertFile2; + } + + if(g_s_lpszPemKeyFile2) + { + g_s_strPemKeyFile2 = strPath + g_s_lpszPemKeyFile2; + g_s_lpszPemKeyFile2 = g_s_strPemKeyFile2; + } + + if(g_s_lpszCAPemCertFileOrPath2) + { + g_s_strCAPemCertFileOrPath2 = strPath + g_s_lpszCAPemCertFileOrPath2; + g_s_lpszCAPemCertFileOrPath2 = g_s_strCAPemCertFileOrPath2; + } + + return TRUE; +} + +#endif + +#ifdef _NEED_HTTP + +LPCSTR HTTP_WEB_SOCKET_CLOSE_FLAG = "$close"; +const BYTE HTTP_WEB_SOCKET_MASK_KEY[] = {0x1, 0x2, 0x3, 0x4}; + +CStringA& HttpVersionToString(EnHttpVersion enVersion, CStringA& strResult) +{ + strResult.Format("HTTP/%d.%d", LOBYTE(enVersion), HIBYTE(enVersion)); + return strResult; +} + +CStringA& MakeSecWebSocketAccept(LPCSTR lpszKey, CStringA& strAccept) +{ + CStringA strKey(lpszKey); + strKey.Append(HTTP_WEB_SOCKET_SEC_SALT); + + _SHA1_CTX ctx; + BYTE buf[SHA1_BLOCK_SIZE]; + + ::sha1_init(&ctx); + ::sha1_update(&ctx, (BYTE*)(LPCSTR)strKey, strKey.GetLength()); + ::sha1_final(&ctx, buf); + + BYTE* lpszAccept = (BYTE*)strAccept.GetBuffer(SHA1_BLOCK_SIZE * 4 / 3 + 4); + + int len = (int)::base64_encode(buf, lpszAccept, SHA1_BLOCK_SIZE, FALSE); + strAccept.ReleaseBufferSetLength(len); + + return strAccept; +} + +#endif diff --git a/global/helper.h b/global/helper.h new file mode 100644 index 0000000..dbe6e71 --- /dev/null +++ b/global/helper.h @@ -0,0 +1,604 @@ +#pragma once + +#include "common/GeneralHelper.h" + +#ifdef _USE_HP_LIB +#include +#else +#include "hpsocket/HPTypeDef.h" +#endif + +#include +#include +#include +#include +#include + +#define EVT_ON_SEND _T("OnSend") +#define EVT_ON_RECEIVE _T("OnReceive") +#define EVT_ON_CLOSE _T("OnClose") +#define EVT_ON_ERROR _T("OnError") +#define EVT_ON_PREPARE_CONNECT _T("OnPrepareConnect") +#define EVT_ON_PREPARE_LISTEN _T("OnPrepareListen") +#define EVT_ON_ACCEPT _T("OnAccept") +#define EVT_ON_CONNECT _T("OnConnect") +#define EVT_ON_HAND_SHAKE _T("OnHandShake") +#define EVT_ON_SHUTDOWN _T("OnShutdown") +#define EVT_ON_END_TEST _T("END TEST") +#define EVT_ON_STAT_TEST _T("STAT TEST") + +#define EVT_ON_MESSAGE_BEGIN _T("OnMessageBegin") +#define EVT_ON_REQUEST_LINE _T("OnRequestLine") +#define EVT_ON_STATUS_LINE _T("OnStatusLine") +#define EVT_ON_HEADER _T("OnHeader") +#define EVT_ON_HEADERS_COMPLETE _T("OnHeadersComplete") +#define EVT_ON_BODY _T("OnBody") +#define EVT_ON_CHUNK_HEADER _T("OnChunkHeader") +#define EVT_ON_CHUNK_COMPLETE _T("OnChunkComplete") +#define EVT_ON_MESSAGE_COMPLETE _T("OnMessageComplete") +#define EVT_ON_UPGRADE _T("OnUpgrade") +#define EVT_ON_PARSE_ERROR _T("OnParseError") + +#define EVT_ON_WS_MSG_HEADER _T("OnWSMessageHeader") +#define EVT_ON_WS_MSG_BODY _T("OnWSMessageBody") +#define EVT_ON_WS_MSG_COMPLETE _T("OnWSMessageComplete") + +#define EVT_ON_UNCOMPRESS_BODY _T("Uncompress Body") +#define EVT_ON_UNCOMPRESS_BODY_FAIL _T("Uncompress Body Fail") + +#define IPV4_LOOPBACK_ADDRESS _T("127.0.0.1") +#define IPV6_LOOPBACK_ADDRESS _T("::1") +#define IPV4_ANY_ADDRESS _T("0.0.0.0") +#define IPV6_ANY_ADDRESS _T("::") +#define DEF_MULTI_CAST_ADDRESS _T("233.0.0.1") +#define BROAD_CAST_ADDRESS _T("255.255.255.255") +#define DEF_TCP_UDP_PORT 5555 +#define DEF_HTTP_PORT 8080 +#define DEF_HTTPS_PORT 8443 + +#define TCP_KEEPALIVE_TIME (60 * 1000) +#define UDP_DETECT_ATTEMPTS 3 + +enum EnAppState +{ + ST_STARTING, ST_STARTED, ST_CONNECTING, ST_CONNECTED, ST_STOPPING, ST_STOPPED +}; + +struct app_arg +{ + static char OPTIONS[]; + + // -a + CString remote_addr; + // -p + USHORT port; + // -b + CString bind_addr; + // -d + USHORT local_port; + // -j + CString reject_addr; + // -n + bool async; + // -t + DWORD thread_count; + // -e + DWORD test_times; + // -i + DWORD test_interval; + // -c + DWORD conn_count; + // -l + DWORD data_length; + // -s + EnSendPolicy send_policy; + // -m + DWORD max_conn; + // -q + bool keep_alive; + + // -o + EnCastMode cast_mode; + // -r + EnReuseAddressPolicy reuse_addr; + // -u + bool ip_loop; + // -k + int ttl; + + // -x + USHORT http_port; + // -y + USHORT https_port; + // -z + bool http_use_cookie; + // -w + bool http_with_listener; + +public: + void ParseArgs(int argc, char* const argv[]); + void ShowPFMTestArgs(BOOL bAgent); + static void PrintUsage(); + static void PrintVersion(); +public: + app_arg(); + ~app_arg(); +}; + +extern app_arg g_app_arg; + +class CAppSignalHandler +{ +public: + + CAppSignalHandler(const vector&& signals, BOOL bIgnoreSigInt = TRUE) + : m_bIgnoreSigInt(bIgnoreSigInt) + { + sigset_t ss; + sigemptyset(&ss); + + for(size_t i = 0; i < signals.size(); i++) + sigaddset(&ss, signals[i]); + + sh.Setup(this, &CAppSignalHandler::handle_sig, &ss); + } + + int WaitForExit() + { + int rs = evt.Wait(); + + PRINTLN(" bye ~ bye ~"); + + return rs; + } + +private: + + void handle_sig(const siginfo_t* pSigInfo) + { + if(pSigInfo->si_signo == SIGINT) + m_bIgnoreSigInt || evt.Set(); + } + +private: + CSignalHandler sh; + CPipeEvent evt; + + BOOL m_bIgnoreSigInt; +}; + +class CTermAttrInitializer +{ +public: + CTermAttrInitializer(tcflag_t l_mask = 0) + { + tcgetattr(STDIN_FILENO, &old_attr); + + termios new_attr = old_attr; + + new_attr.c_lflag &= ~l_mask; + new_attr.c_cc[VERASE]= 0x08; + new_attr.c_cc[VTIME] = 0; + new_attr.c_cc[VMIN] = 1; + + tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_attr); + } + + ~CTermAttrInitializer() {tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_attr);} + +private: + termios old_attr; +}; + +class CCommandParser +{ +public: + using CMD_FUNC = void (*)(CCommandParser*); + + enum EnAppType {AT_SERVER, AT_AGENT, AT_CLIENT, AT_NODE}; + enum EnCmdType {CT_START = 0, CT_STOP, CT_STATUS, CT_CONNECT, CT_SEND, CT_SENDC, CT_PAUSE, CT_KICK, CT_KICK_L, CT_KICK_S, CT_STAT, CT_MAX}; + +protected: + + struct TCmdNameFunc + { + LPCTSTR name; + CMD_FUNC func; + }; + +public: + BOOL Run(); + void PrintStatus(EnServiceState enStatus, LPCTSTR lpszName = nullptr); + +protected: + virtual void ParseCmdArgs(EnCmdType type, LPTSTR lpszArg); + virtual void Reset(); + + void PrintUsage(); + virtual void PrintCmdUsage(); + virtual CString GetCmdUsage(EnCmdType type); + +private: + BOOL WaitForExit(); + void WorkerProc(PVOID pv); + void Parse(LPTSTR lpszLine, SSIZE_T nSize); + +public: + CCommandParser(EnAppType enAppType, CMD_FUNC fnCmds[CT_MAX]); + virtual ~CCommandParser() {} + +public: + BOOL m_bFlag; + CONNID m_dwConnID; + CString m_strMessage; + CString m_strRemoteAddr; + USHORT m_usRemotePort; + DWORD m_dwSeconds; + +protected: + EnAppType m_enAppType; + TCmdNameFunc m_szCmdNameFuncs[CT_MAX]; + + CThread m_thWorker; +}; + +#ifdef _NEED_HTTP + +class CHttpCommandParser : public CCommandParser +{ + using __super = CCommandParser; + +protected: + virtual void ParseCmdArgs(EnCmdType type, LPTSTR lpszArg) override; + virtual void Reset() override; + + virtual void PrintCmdUsage() override; + virtual CString GetCmdUsage(EnCmdType type) override; + +private: + BOOL ParseCmdOptions(LPCTSTR lpszArg, LPCTSTR lpszOptions); + +public: + CHttpCommandParser(EnAppType enAppType, CMD_FUNC fnCmds[CT_MAX]) + : __super(enAppType, fnCmds) {} + +public: + BOOL m_bHttps; + CString m_strPath; + CString m_strMethod; + CString m_strData; + CString m_strFilePath; + vector m_vtHeaders; +}; + +#endif + +struct server_statistics_info +{ + volatile LONG m_lClientCount; + volatile LONGLONG m_llTotalReceived; + volatile LONGLONG m_llTotalSent; + + CCriSec m_cs; + + void Reset(BOOL bResetClientCount = TRUE); + void CheckClientCount(); + void CheckStatistics(); + void AddTotalRecv(int iLength); + void AddTotalSend(int iLength); + +}; + +struct client_statistics_info +{ + volatile LONGLONG m_llTotalReceived; + volatile LONGLONG m_llTotalSent; + LONGLONG m_llExpectReceived; + DWORD m_dwBeginTickCount; + DWORD m_dwTimeconsuming; + + volatile int m_iConnected; + + void Reset(); + void StartTest(); + void CheckStatistics(BOOL bCheckSend = TRUE); + void AddTotalRecv(int iLength); + void AddTotalSend(int iLength); + + void TermConnected(); + void AddConnected(); + int GetConnected(); +}; + +struct info_msg +{ + LPCTSTR name; + CONNID connID; + LPCTSTR evt; + int contentLength; + LPCTSTR content; + + static info_msg* Construct(CONNID dwConnID, LPCTSTR lpszEvent, int iContentLength = 0, LPCTSTR lpszContent = nullptr, LPCTSTR lpszName = nullptr); + static void Destruct(info_msg* pMsg); + +private: + info_msg(CONNID dwConnID, LPCTSTR lpszEvent, int iContentLength = 0, LPCTSTR lpszContent = nullptr, LPCTSTR lpszName = nullptr); + ~info_msg(); +}; + +struct TPkgHeader +{ + DWORD seq; + int body_len; +}; + +struct TPkgBody +{ + char name[30]; + short age; + char desc[1]; +}; + +struct TPkgInfo +{ + bool is_header; + int length; + + TPkgInfo(bool header = true, int len = sizeof(TPkgHeader)) : is_header(header), length(len) {} + void Reset() {is_header = true, length = sizeof(TPkgHeader);} + ~TPkgInfo() {} +}; + +inline TPkgInfo* ConstructPkgInfo() +{ + return new TPkgInfo(true, sizeof(TPkgHeader)); +} + +inline void DestructPkgInfo(TPkgInfo* pInfo) +{ + delete pInfo; +} + +template>::value>> +inline TPkgInfo* CreatePkgInfo(T* pSender, CONNID dwConnID) +{ + TPkgInfo* pInfo = ConstructPkgInfo(); + pSender->SetConnectionExtra(dwConnID, pInfo); + + return pInfo; +} + +template>::value>> +inline TPkgInfo* FindPkgInfo(T* pSender, CONNID dwConnID) +{ + PVOID pInfo = nullptr; + pSender->GetConnectionExtra(dwConnID, &pInfo); + + return (TPkgInfo*)pInfo; +} + +template>::value>> +inline void RemovePkgInfo(T* pSender, CONNID dwConnID) +{ + TPkgInfo* pInfo = FindPkgInfo(pSender, dwConnID); + ASSERT(pInfo != nullptr); + + DestructPkgInfo(pInfo); +} + +CBufferPtr* GeneratePkgBuffer(DWORD seq, LPCTSTR lpszName, short age, LPCTSTR lpszDesc); +CBufferPtr* GeneratePkgBuffer(const TPkgHeader& header, const TPkgBody& body); + +BOOL SplitStr(LPCTSTR pszSrc, vector& vtItem, LPCTSTR pszSepectors = nullptr, LPCTSTR pszQMarks = nullptr); + +sa_family_t GuessAddrFamily(LPCTSTR lpszAddress); +LPCTSTR GetLoopbackAddress(LPCTSTR lpszLikeAddress); +LPCTSTR GetAnyAddress(LPCTSTR lpszLikeAddress); + +void LogServerStart(LPCTSTR lpszAddress, USHORT port, LPCTSTR lpszName = nullptr); +void LogServerStartFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogServerStop(LPCTSTR lpszName = nullptr); +void LogServerStopFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogAgentStart(LPCTSTR lpszAddress, BOOL bAsync, LPCTSTR lpszName = nullptr); +void LogAgentStarting(LPCTSTR lpszAddress, BOOL bAsync, LPCTSTR lpszName = nullptr); +void LogAgentStartFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogAgentStopping(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogAgentStop(LPCTSTR lpszName = nullptr); +void LogAgentStopFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogAgentSendFail(int iSequence, int iSocketIndex, DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogClientStart(LPCTSTR lpszAddress, USHORT port, LPCTSTR lpszName = nullptr); +void LogClientStarting(LPCTSTR lpszAddress, USHORT port, LPCTSTR lpszName = nullptr); +void LogClientStartFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogClientStopping(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogClientStop(LPCTSTR lpszName = nullptr); +void LogClientStopFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogClientSendFail(int iSequence, int iSocketIndex, DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogStart(LPCTSTR lpszAddress, USHORT port, LPCTSTR lpszName = nullptr); +void LogStartFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogStop(LPCTSTR lpszName = nullptr); +void LogSend(LPCTSTR lpszContent, LPCTSTR lpszName = nullptr); +void LogSend(CONNID dwConnID, LPCTSTR lpszContent, LPCTSTR lpszName = nullptr); +void LogSending(CONNID dwConnID, LPCTSTR lpszContent, LPCTSTR lpszName = nullptr); +void LogSendFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogSendFail(CONNID dwConnID, DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogDisconnect(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogDisconnectFail(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogDisconnect2(CONNID dwConnID, BOOL bForce, LPCTSTR lpszName = nullptr); +void LogDisconnectFail2(CONNID dwConnID, BOOL bForce, LPCTSTR lpszName = nullptr); +void LogDisconnectLong(DWORD dwSeconds, BOOL bForce, LPCTSTR lpszName = nullptr); +void LogDisconnectFailLong(DWORD dwSeconds, BOOL bForce, LPCTSTR lpszName = nullptr); +void LogPause(CONNID dwConnID, BOOL bPause, LPCTSTR lpszName = nullptr); +void LogPauseFail(CONNID dwConnID, BOOL bPause, LPCTSTR lpszName = nullptr); +void LogConnect(LPCTSTR lpszAddress, USHORT usPort, LPCTSTR lpszName = nullptr); +void LogConnectFail(DWORD code, LPCTSTR lpszDesc, LPCTSTR lpszName = nullptr); +void LogRelease(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogReleaseFail(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogDetect(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogDetectFail(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogOnConnect(CONNID dwConnID, const CString& strAddress, USHORT usPort, LPCTSTR lpszName = nullptr); +void LogOnConnect2(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogOnConnect3(CONNID dwConnID, const CString& strAddress, USHORT usPort, LPCTSTR lpszName = nullptr); +void LogOnHandShake2(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void LogOnClose(CONNID dwConnID, LPCTSTR lpszName = nullptr); + +void PostOnSend(CONNID dwConnID, const BYTE* pData, int iLength, LPCTSTR lpszName = nullptr); +void PostOnSendTo(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, const BYTE* pData, int iLength, LPCTSTR lpszName = nullptr); +void PostOnReceive(CONNID dwConnID, const BYTE* pData, int iLength, LPCTSTR lpszName = nullptr); +void PostOnReceiveFrom(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, const BYTE* pData, int iLength, LPCTSTR lpszName = nullptr); +void PostOnReceiveCast(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, const BYTE* pData, int iLength, LPCTSTR lpszName = nullptr); +void PostOnClose(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void PostOnError(CONNID dwConnID, int enOperation, int iErrorCode, LPCTSTR lpszName = nullptr); +void PostOnError2(CONNID dwConnID, int enOperation, int iErrorCode, LPCTSTR lpszAddress, USHORT usPort, const BYTE* pBuffer, int iLength, LPCTSTR lpszName = nullptr); +void PostOnAccept(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, BOOL bPass, LPCTSTR lpszName = nullptr); +void PostOnAccept2(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void PostOnHandShake(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void PostOnPrepareListen(LPCTSTR lpszAddress, USHORT usPort, LPCTSTR lpszName = nullptr); +void PostOnPrepareConnect(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void PostOnConnect(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, LPCTSTR lpszName = nullptr); +void PostOnConnect2(CONNID dwConnID, LPCTSTR lpszAddress, USHORT usPort, LPCTSTR lpszName = nullptr); +void PostOnConnect3(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void PostOnShutdown(LPCTSTR lpszName = nullptr); +void PostServerStatics(const LONGLONG& llTotalSent, const LONGLONG& llTotalReceived, LPCTSTR lpszName = nullptr); +void PostServerTemporaryStatics(const LONGLONG& llTotalSent, const LONGLONG& llTotalReceived, LPCTSTR lpszName = nullptr); +void PostTimeConsuming(DWORD dwTickCount, LPCTSTR lpszName = nullptr); + +#ifdef _NEED_HTTP +void PostOnMessageBegin(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void PostOnRequestLine(CONNID dwConnID, LPCSTR lpszMethod, USHORT usUrlFieldSet, LPCSTR lpszUrl, LPCTSTR lpszName = nullptr); +void PostOnStatusLine(CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc, LPCTSTR lpszName = nullptr); +void PostOnHeader(CONNID dwConnID, LPCSTR lpszHeaderName, LPCSTR lpszHeaderValue, LPCTSTR lpszName = nullptr); +void PostOnHeadersComplete(CONNID dwConnID, LPCSTR lpszSummary, LPCTSTR lpszName = nullptr); +void PostOnBody(CONNID dwConnID, const BYTE* pData, int iLength, LPCTSTR lpszName = nullptr); +void PostOnChunkHeader(CONNID dwConnID, int iLength, LPCTSTR lpszName = nullptr); +void PostOnChunkComplete(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void PostOnMessageComplete(CONNID dwConnID, LPCTSTR lpszName = nullptr); +void PostOnUpgrade(CONNID dwConnID, EnHttpUpgradeType enUpgradeType, LPCTSTR lpszName = nullptr); +void PostOnParseError(CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc, LPCTSTR lpszName = nullptr); + +void PostOnWSMessageHeader(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen, LPCTSTR lpszName = nullptr); +void PostOnWSMessageBody(CONNID dwConnID, const BYTE* pData, int iLength, LPCTSTR lpszName = nullptr); +void PostOnWSMessageComplete(CONNID dwConnID, LPCTSTR lpszName = nullptr); + +void PostUncompressBody(CONNID dwConnID, int iLength, LPCTSTR lpszName = nullptr); +void PostUncompressBodyFail(CONNID dwConnID, int iResult, LPCTSTR lpszName = nullptr); +#endif + +void PostInfoMsg(info_msg* msg); +void LogInfoMsg(info_msg* pInfoMsg); +void LogMsg(const CString& msg); + +extern LPCTSTR g_lpszDefaultCookieFile; + +LPCTSTR GetDefaultCookieFile(); + +#ifdef _NEED_SSL + +extern int g_c_iVerifyMode; +extern BOOL g_c_bNeedClientVerification; +extern LPCSTR g_c_lpszPemCert; +extern LPCSTR g_c_lpszPemKey; +extern LPCSTR g_c_lpszCAPemCert; +extern LPCSTR g_s_lpszPemCert; +extern LPCSTR g_s_lpszPemKey; +extern LPCSTR g_s_lpszCAPemCert; +extern LPCTSTR g_c_lpszCAPemCertFileOrPath; +extern LPCTSTR g_c_lpszPemCertFile; +extern LPCTSTR g_c_lpszPemKeyFile; +extern LPCTSTR g_c_lpszKeyPasswod; + +extern int g_s_iVerifyMode; +extern BOOL g_s_bNeedClientVerification; +extern LPCTSTR g_s_lpszCAPemCertFileOrPath; +extern LPCTSTR g_s_lpszPemCertFile; +extern LPCTSTR g_s_lpszPemKeyFile; +extern LPCTSTR g_s_lpszKeyPasswod; + +extern int g_c_iVerifyMode2; +extern BOOL g_c_bNeedClientVerification2; +extern LPCTSTR g_c_lpszCAPemCertFileOrPath2; +extern LPCTSTR g_c_lpszPemCertFile2; +extern LPCTSTR g_c_lpszPemKeyFile2; +extern LPCTSTR g_c_lpszKeyPasswod2; + +extern int g_s_iVerifyMode2; +extern BOOL g_s_bNeedClientVerification2; +extern LPCTSTR g_s_lpszCAPemCertFileOrPath2; +extern LPCTSTR g_s_lpszPemCertFile2; +extern LPCTSTR g_s_lpszPemKeyFile2; +extern LPCTSTR g_s_lpszKeyPasswod2; + +#endif + +#ifdef _NEED_HTTP + +#include "Crypto.h" + +#define HTTP_NAME _T("http") +#define HTTPS_NAME _T("https") +#define STR_HTTP_SCHEMA "http://" +#define STR_HTTPS_SCHEMA "https://" +#define CRLF "\r\n" +#define NV_SEPARATOR_CHAR '=' +#define HEADER_SEPARATOR ": " +#define COOKIE_TOKENIZE "; " +#define STR_HTTP_1_0 "HTTP/1.0" +#define STR_HTTP_1_1 "HTTP/1.1" +#define HOST_HEADER "Host" +#define COOKIE_HEADER "Cookie" +#define SET_COOKIE_HEADER "Set-Cookie" +#define CONTENT_TYPE_HEADER "Content-Type" +#define CONTENT_LENGTH_HEADER "Content-Length" +#define TRANSFER_ENCODING_HEADER "Transfer-Encoding" +#define UPGRADE_HEADER "Upgrade" +#define WEB_SOCKET_HEADER_VALUE "WebSocket" + +#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_WEB_SOCKET_SEC_SALT "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + +extern LPCSTR HTTP_WEB_SOCKET_CLOSE_FLAG; +extern const BYTE HTTP_WEB_SOCKET_MASK_KEY[]; + + +struct THttpHeader +{ + CStringA strName; + CStringA strValue; + + THttpHeader(LPCTSTR lpszName, LPCTSTR lpszValue) : strName(lpszName), strValue(lpszValue) {} + + struct hash + { + size_t operator() (const CStringA& str) const + { + return hash_value(str); + } + }; + + struct equal_to + { + bool operator () (const CStringA& strA, const CStringA& strB) const + { + return strA == strB; + } + }; + +}; + +typedef unordered_multimap THttpHeaderMap; +typedef THttpHeaderMap::const_iterator THttpHeaderMapCI; +typedef THttpHeaderMap::iterator THttpHeaderMapI; + +CStringA& HttpVersionToString(EnHttpVersion enVersion, CStringA& strResult); +CStringA& MakeSecWebSocketAccept(LPCSTR lpszKey, CStringA& strAccept); + +#endif diff --git a/include/hpsocket/GlobalDef.h b/include/hpsocket/GlobalDef.h new file mode 100644 index 0000000..abca8ed --- /dev/null +++ b/include/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/include/hpsocket/GlobalErrno.h b/include/hpsocket/GlobalErrno.h new file mode 100644 index 0000000..4e1eed9 --- /dev/null +++ b/include/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/include/hpsocket/HPSocket-SSL.h b/include/hpsocket/HPSocket-SSL.h new file mode 100644 index 0000000..966a1a1 --- /dev/null +++ b/include/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/include/hpsocket/HPSocket.h b/include/hpsocket/HPSocket.h new file mode 100644 index 0000000..4f96948 --- /dev/null +++ b/include/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/include/hpsocket/HPTypeDef.h b/include/hpsocket/HPTypeDef.h new file mode 100644 index 0000000..b135946 --- /dev/null +++ b/include/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 7 // 修正版本号 +#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/include/hpsocket/SocketInterface.h b/include/hpsocket/SocketInterface.h new file mode 100644 index 0000000..da02063 --- /dev/null +++ b/include/hpsocket/SocketInterface.h @@ -0,0 +1,3123 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + + /* 设置是否开启 IPv4/IPv6 双栈(默认:TRUE) */ + virtual void SetDualStack(BOOL bDualStack) = 0; + /* 检测是否开启 IPv4/IPv6 双栈 */ + virtual BOOL IsDualStack() = 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; + /* 设置是否开启 IPv4/IPv6 双栈(默认:TRUE) */ + virtual void SetDualStack(BOOL bDualStack) = 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; + /* 检测是否开启 IPv4/IPv6 双栈 */ + virtual BOOL IsDualStack() = 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/server/server.cpp b/server/server.cpp new file mode 100644 index 0000000..242c26c --- /dev/null +++ b/server/server.cpp @@ -0,0 +1,165 @@ +#include "helper.h" +#include "TcpServer.h" + +class CListenerImpl : public CTcpServerListener +{ + +public: + virtual EnHandleResult OnPrepareListen(ITcpServer* pSender, SOCKET soListen) override + { + TCHAR szAddress[100]; + int iAddressLen = sizeof(szAddress) / sizeof(TCHAR); + USHORT usPort; + + pSender->GetListenAddress(szAddress, iAddressLen, usPort); + ::PostOnPrepareListen(szAddress, usPort); + + return HR_OK; + } + + virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient) override + { + BOOL bPass = TRUE; + TCHAR szAddress[100]; + int iAddressLen = sizeof(szAddress) / sizeof(TCHAR); + USHORT usPort; + + pSender->GetRemoteAddress(dwConnID, szAddress, iAddressLen, usPort); + + if(!g_app_arg.reject_addr.IsEmpty()) + { + if(g_app_arg.reject_addr.CompareNoCase(szAddress) == 0) + bPass = FALSE; + } + + ::PostOnAccept(dwConnID, szAddress, usPort, bPass); + + return bPass ? HR_OK : HR_ERROR; + } + + virtual EnHandleResult OnHandShake(ITcpServer* pSender, CONNID dwConnID) override + { + return HR_OK; + } + + virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override + { + ::PostOnReceive(dwConnID, pData, iLength); + + if(pSender->Send(dwConnID, pData, iLength)) + return HR_OK; + + return HR_ERROR; + } + + virtual EnHandleResult OnSend(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override + { + ::PostOnSend(dwConnID, pData, iLength); + return HR_OK; + } + + virtual EnHandleResult OnClose(ITcpServer* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode) override + { + iErrorCode == SE_OK ? ::PostOnClose(dwConnID) : + ::PostOnError(dwConnID, enOperation, iErrorCode); + + return HR_OK; + } + + virtual EnHandleResult OnShutdown(ITcpServer* pSender) override + { + ::PostOnShutdown(); + return HR_OK; + } + +}; + +CListenerImpl s_listener; +CTcpServer s_server(&s_listener); + +void OnCmdStart(CCommandParser* pParser) +{ + if(s_server.Start(g_app_arg.bind_addr, g_app_arg.port)) + ::LogServerStart(g_app_arg.bind_addr, g_app_arg.port); + else + ::LogServerStartFail(s_server.GetLastError(), s_server.GetLastErrorDesc()); +} + +void OnCmdStop(CCommandParser* pParser) +{ + if(s_server.Stop()) + ::LogServerStop(); + else + ::LogServerStopFail(s_server.GetLastError(), s_server.GetLastErrorDesc()); +} + +void OnCmdStatus(CCommandParser* pParser) +{ + pParser->PrintStatus(s_server.GetState()); +} + +void OnCmdSend(CCommandParser* pParser) +{ + if(s_server.Send(pParser->m_dwConnID, (LPBYTE)(LPCTSTR)pParser->m_strMessage, pParser->m_strMessage.GetLength())) + ::LogSend(pParser->m_dwConnID, pParser->m_strMessage); + else + ::LogSendFail(pParser->m_dwConnID, ::GetLastError(), ::GetLastErrorStr()); +} + +void OnCmdPause(CCommandParser* pParser) +{ + if(s_server.PauseReceive(pParser->m_dwConnID, pParser->m_bFlag)) + ::LogPause(pParser->m_dwConnID, pParser->m_bFlag); + else + ::LogPauseFail(pParser->m_dwConnID, pParser->m_bFlag); +} + +void OnCmdKick(CCommandParser* pParser) +{ + if(s_server.Disconnect(pParser->m_dwConnID, pParser->m_bFlag)) + ::LogDisconnect2(pParser->m_dwConnID, pParser->m_bFlag); + else + ::LogDisconnectFail2(pParser->m_dwConnID, pParser->m_bFlag); +} + +void OnCmdKickLong(CCommandParser* pParser) +{ + if(s_server.DisconnectLongConnections(pParser->m_dwSeconds * 1000, pParser->m_bFlag)) + ::LogDisconnectLong(pParser->m_dwSeconds, pParser->m_bFlag); + else + ::LogDisconnectFailLong(pParser->m_dwSeconds, pParser->m_bFlag); +} + +void OnCmdKickSilence(CCommandParser* pParser) +{ + if(s_server.DisconnectSilenceConnections(pParser->m_dwSeconds * 1000, pParser->m_bFlag)) + ::LogDisconnectLong(pParser->m_dwSeconds, pParser->m_bFlag); + else + ::LogDisconnectFailLong(pParser->m_dwSeconds, pParser->m_bFlag); +} + +int main(int argc, char* const argv[]) +{ + CTermAttrInitializer term_attr; + CAppSignalHandler s_signal_handler({SIGTTOU, SIGINT}); + + g_app_arg.ParseArgs(argc, argv); + + s_server.SetKeepAliveTime(g_app_arg.keep_alive ? TCP_KEEPALIVE_TIME : 0); + + CCommandParser::CMD_FUNC fnCmds[CCommandParser::CT_MAX] = {0}; + + fnCmds[CCommandParser::CT_START] = OnCmdStart; + fnCmds[CCommandParser::CT_STOP] = OnCmdStop; + fnCmds[CCommandParser::CT_STATUS] = OnCmdStatus; + fnCmds[CCommandParser::CT_SEND] = OnCmdSend; + fnCmds[CCommandParser::CT_PAUSE] = OnCmdPause; + fnCmds[CCommandParser::CT_KICK] = OnCmdKick; + fnCmds[CCommandParser::CT_KICK_L] = OnCmdKickLong; + fnCmds[CCommandParser::CT_KICK_S] = OnCmdKickSilence; + + CCommandParser s_cmd_parser(CCommandParser::AT_SERVER, fnCmds); + s_cmd_parser.Run(); + + return EXIT_CODE_OK; +} diff --git a/src/ArqHelper.cpp b/src/ArqHelper.cpp new file mode 100644 index 0000000..29cc297 --- /dev/null +++ b/src/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/src/ArqHelper.h b/src/ArqHelper.h new file mode 100644 index 0000000..ff3eaf9 --- /dev/null +++ b/src/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 "../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/src/HPSocket-SSL.cpp b/src/HPSocket-SSL.cpp new file mode 100644 index 0000000..82b1768 --- /dev/null +++ b/src/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 "../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/src/HPSocket.cpp b/src/HPSocket.cpp new file mode 100644 index 0000000..f9706fc --- /dev/null +++ b/src/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 "../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/src/HPThreadPool.cpp b/src/HPThreadPool.cpp new file mode 100644 index 0000000..0cbed1a --- /dev/null +++ b/src/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/src/HPThreadPool.h b/src/HPThreadPool.h new file mode 100644 index 0000000..7ae2695 --- /dev/null +++ b/src/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 "../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/src/HttpAgent.cpp b/src/HttpAgent.cpp new file mode 100644 index 0000000..a3daf33 --- /dev/null +++ b/src/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/src/HttpAgent.h b/src/HttpAgent.h new file mode 100644 index 0000000..ab79bca --- /dev/null +++ b/src/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/src/HttpClient.cpp b/src/HttpClient.cpp new file mode 100644 index 0000000..0da8ce6 --- /dev/null +++ b/src/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/src/HttpClient.h b/src/HttpClient.h new file mode 100644 index 0000000..9cb12a7 --- /dev/null +++ b/src/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/src/HttpCookie.cpp b/src/HttpCookie.cpp new file mode 100644 index 0000000..4b6cf7b --- /dev/null +++ b/src/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/src/HttpCookie.h b/src/HttpCookie.h new file mode 100644 index 0000000..9a61922 --- /dev/null +++ b/src/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 "../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/src/HttpHelper.cpp b/src/HttpHelper.cpp new file mode 100644 index 0000000..9787494 --- /dev/null +++ b/src/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/src/HttpHelper.h b/src/HttpHelper.h new file mode 100644 index 0000000..26ff756 --- /dev/null +++ b/src/HttpHelper.h @@ -0,0 +1,1387 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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, + nullptr, // on_protocol + 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, + nullptr, // on_protocol_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/src/HttpServer.cpp b/src/HttpServer.cpp new file mode 100644 index 0000000..3ea028f --- /dev/null +++ b/src/HttpServer.cpp @@ -0,0 +1,611 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) + { + KillDyingConnection(); + continue; + } + + VERIFY(rs == 1); + m_evCleaner.Reset(); + + break; + } + + 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/src/HttpServer.h b/src/HttpServer.h new file mode 100644 index 0000000..6decd32 --- /dev/null +++ b/src/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/src/InternalDef.h b/src/InternalDef.h new file mode 100644 index 0000000..7fc3327 --- /dev/null +++ b/src/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 "../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/src/MiscHelper.cpp b/src/MiscHelper.cpp new file mode 100644 index 0000000..5a238ec --- /dev/null +++ b/src/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/src/MiscHelper.h b/src/MiscHelper.h new file mode 100644 index 0000000..4f6beae --- /dev/null +++ b/src/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/src/SSLAgent.cpp b/src/SSLAgent.cpp new file mode 100644 index 0000000..4b3dd7d --- /dev/null +++ b/src/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/src/SSLAgent.h b/src/SSLAgent.h new file mode 100644 index 0000000..901c9ba --- /dev/null +++ b/src/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/src/SSLClient.cpp b/src/SSLClient.cpp new file mode 100644 index 0000000..7dc2ece --- /dev/null +++ b/src/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/src/SSLClient.h b/src/SSLClient.h new file mode 100644 index 0000000..0f68fd8 --- /dev/null +++ b/src/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/src/SSLHelper.cpp b/src/SSLHelper.cpp new file mode 100644 index 0000000..27be3a6 --- /dev/null +++ b/src/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/src/SSLHelper.h b/src/SSLHelper.h new file mode 100644 index 0000000..7cd3a8f --- /dev/null +++ b/src/SSLHelper.h @@ -0,0 +1,500 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "../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; + } + + if(!pSession->WriteSendChannel(pBuffers, iCount)) + { + ::SetLastError(ERROR_WRITE_FAULT); + return FALSE; + } + + 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/src/SSLServer.cpp b/src/SSLServer.cpp new file mode 100644 index 0000000..66dbafc --- /dev/null +++ b/src/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/src/SSLServer.h b/src/SSLServer.h new file mode 100644 index 0000000..70cb89b --- /dev/null +++ b/src/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/src/SocketHelper.cpp b/src/SocketHelper.cpp new file mode 100644 index 0000000..b6f5ade --- /dev/null +++ b/src/SocketHelper.cpp @@ -0,0 +1,1676 @@ +/* +* Copyright: JessMA Open Source (ldcsaa@gmail.com) +* +* Author : Bruce Liang +* Website : https://github.com/ldcsaa +* Project : https://github.com/ldcsaa/HP-Socket +* Blog : http://www.cnblogs.com/ldcsaa +* Wiki : http://www.oschina.net/p/hp-socket +* QQ Group : 44636872, 75375912 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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 + +#include "common/PollHelper.h" + +#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, ADDRESS_FAMILY af) +{ + addr.family = DetermineAddrFamily(lpszHost); + + if(addr.family == AF_UNSPEC) + { + addr.family = af; + return GetSockAddrByHostNameDirectly(lpszHost, usPort, addr); + } + + if(addr.family != af && af != AF_UNSPEC) + { + ::WSASetLastError(ERROR_AFNOSUPPORT); + return FALSE; + } + + return GetSockAddr(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 = (AI_V4MAPPED | AI_ADDRCONFIG); +#endif + hints.ai_family = addr.family; + + 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) +{ + pollfd pfd = {sock, _POLL_WRITE_EVENTS | _POLL_ALL_ERROR_EVENTS}; + int rs = (int)::PollForSingleObject(pfd, dwTimeout); + + if(rs == TIMEOUT) + return ERROR_TIMEOUT; + else if(rs < TIMEOUT) + return ENSURE_ERROR(ERROR_CANT_WAIT); + else if(pfd.revents & _POLL_ALL_ERROR_EVENTS) + { + rs = SSO_GetError(sock); + return ((rs != NO_ERROR && rs != SOCKET_ERROR) ? rs : ENSURE_ERROR(ERROR_CANT_WAIT)); + } + + 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_DualStack(SOCKET sock, BOOL bDualStack) +{ + int val = bDualStack ? 0 : 1; + return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &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/src/SocketHelper.h b/src/SocketHelper.h new file mode 100644 index 0000000..b4a9494 --- /dev/null +++ b/src/SocketHelper.h @@ -0,0 +1,1053 @@ +/* +* Copyright: JessMA Open Source (ldcsaa@gmail.com) +* +* Author : Bruce Liang +* Website : https://github.com/ldcsaa +* Project : https://github.com/ldcsaa/HP-Socket +* Blog : http://www.cnblogs.com/ldcsaa +* Wiki : http://www.oschina.net/p/hp-socket +* QQ Group : 44636872, 75375912 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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 "../include/hpsocket/HPTypeDef.h" +#include "../include/hpsocket/SocketInterface.h" +#include "common/Event.h" +#include "common/Thread.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 + +#undef _BROTLI_SUPPORT + +#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(); + pSocketObj->Decrement(); + } + + 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(1); + + 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; + } + +}; + +/* 真实垃圾回收线程 */ +template class _CRealGCThreadT +{ +public: + BOOL Start(long lGCCheckInterval = GC_CHECK_INTERVAL) + { + m_lGCCheckInterval = lGCCheckInterval; + return m_thGC.Start(this, &_CRealGCThreadT::GCThreadProc); + } + + BOOL Stop() + { + if(m_thGC.IsRunning()) + { + m_evGC.Set(); + m_thGC.Join(); + m_evGC.Reset(); + } + + return TRUE; + } + +public: + _CRealGCThreadT(T* pOwner) : m_pOwner(pOwner), m_lGCCheckInterval(GC_CHECK_INTERVAL) {} + ~_CRealGCThreadT() {Stop();} + + DECLARE_NO_COPY_CLASS(_CRealGCThreadT) + +private: + UINT GCThreadProc(PVOID pv = nullptr) + { + TRACE("---------------> GC Thread 0x%08X started <---------------", SELF_THREAD_ID); + + while(TRUE) + { + int rs = (int)m_evGC.Wait(m_lGCCheckInterval); + ASSERT(rs >= TIMEOUT); + + if(rs == TIMEOUT) + { + m_pOwner->ReleaseGCSocketObj(FALSE); + continue; + } + + VERIFY(rs == 1); + m_evGC.Reset(); + + break; + } + + TRACE("---------------> GC Thread 0x%08X stoped <---------------", SELF_THREAD_ID); + + return 0; + } + +private: + long m_lGCCheckInterval; + T* m_pOwner; + CEvt m_evGC; + CThread<_CRealGCThreadT, VOID, UINT> m_thGC; +}; + + +/* 虚拟垃圾回收线程 */ +template class _CFakeGCThreadT +{ +public: + BOOL Start(long lGCCheckInterval = GC_CHECK_INTERVAL) + { + return TRUE; + } + + BOOL Stop() + { + return TRUE; + } + +public: + _CFakeGCThreadT(T* pOwner) {} +}; + +/* 垃圾回收 GC Thread */ +#ifdef USE_EXTERNAL_GC + #define CGCThreadT _CRealGCThreadT +#else + #define CGCThreadT _CFakeGCThreadT +#endif + +/*****************************************************************************************************/ +/******************************************** 公共帮助方法 ********************************************/ +/*****************************************************************************************************/ + +/* 默认工作线程前缀 */ +#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, ADDRESS_FAMILY af = AF_UNSPEC); +/* 通过主机名获取 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_DualStack (SOCKET sock, BOOL bDualStack = 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/src/SocketObject4C.h b/src/SocketObject4C.h new file mode 100644 index 0000000..421fe82 --- /dev/null +++ b/src/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 "../include/hpsocket/HPSocket4C.h" +#include "../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/src/TcpAgent.cpp b/src/TcpAgent.cpp new file mode 100644 index 0000000..1fc6dbe --- /dev/null +++ b/src/TcpAgent.cpp @@ -0,0 +1,1277 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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() +{ + if(!m_ioDispatcher.Start(this, DEFAULT_WORKER_MAX_EVENT_COUNT, m_dwWorkerThreadCount)) + { + SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + if(!m_thGC.Start()) + { + SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + 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(); + m_thGC.Stop(); +} + +void CTcpAgent::ReleaseClientSocket() +{ + VERIFY(m_bfActiveSockets.IsEmpty()); + m_bfActiveSockets.Reset(); +} + +void CTcpAgent::ReleaseFreeSocket() +{ + m_lsFreeSocket.Clear(); + 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) +{ + HP_SOCKADDR* lpBindAddr = &m_soAddr; + + if(::IsStrNotEmpty(lpszLocalAddress)) + { + lpBindAddr = CreateLocalObject(HP_SOCKADDR); + + if(!::sockaddr_A_2_IN(lpszLocalAddress, 0, *lpBindAddr)) + return ::WSAGetLastError(); + } + + if(!::GetSockAddrByHostName(lpszRemoteAddress, usPort, addr, lpBindAddr->family)) + return ::WSAGetLastError(); + + int result = NO_ERROR; + BOOL bBind = lpBindAddr->IsSpecified(); + 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) +{ + 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/src/TcpAgent.h b/src/TcpAgent.h new file mode 100644 index 0000000..c1d76f1 --- /dev/null +++ b/src/TcpAgent.h @@ -0,0 +1,309 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +{ + using CGCThread = CGCThreadT; + friend class CGCThreadT; + +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_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) + , m_thGC (this) + { + 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; + + CGCThread m_thGC; + + TAgentSocketObjPtrPool m_bfActiveSockets; + + TAgentSocketObjPtrList m_lsFreeSocket; + TAgentSocketObjPtrQueue m_lsGCSocket; + + CIODispatcher m_ioDispatcher; +}; diff --git a/src/TcpClient.cpp b/src/TcpClient.cpp new file mode 100644 index 0000000..7f0f5e8 --- /dev/null +++ b/src/TcpClient.cpp @@ -0,0 +1,717 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ + if(::IsStrNotEmpty(lpszBindAddress)) + { + if(!::sockaddr_A_2_IN(lpszBindAddress, 0, addrBind)) + return FALSE; + } + + HP_SCOPE_HOST host(lpszRemoteAddress); + + if(!::GetSockAddrByHostName(host.addr, usPort, addrRemote, addrBind.family)) + 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/src/TcpClient.h b/src/TcpClient.h new file mode 100644 index 0000000..bc19b15 --- /dev/null +++ b/src/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/src/TcpPackAgent.cpp b/src/TcpPackAgent.cpp new file mode 100644 index 0000000..bfe5530 --- /dev/null +++ b/src/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/src/TcpPackAgent.h b/src/TcpPackAgent.h new file mode 100644 index 0000000..ad36e91 --- /dev/null +++ b/src/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/src/TcpPackClient.cpp b/src/TcpPackClient.cpp new file mode 100644 index 0000000..b78d7a3 --- /dev/null +++ b/src/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/src/TcpPackClient.h b/src/TcpPackClient.h new file mode 100644 index 0000000..6b1d904 --- /dev/null +++ b/src/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/src/TcpPackServer.cpp b/src/TcpPackServer.cpp new file mode 100644 index 0000000..68ddd7b --- /dev/null +++ b/src/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/src/TcpPackServer.h b/src/TcpPackServer.h new file mode 100644 index 0000000..b7dd786 --- /dev/null +++ b/src/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/src/TcpPullAgent.cpp b/src/TcpPullAgent.cpp new file mode 100644 index 0000000..8efdd2c --- /dev/null +++ b/src/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/src/TcpPullAgent.h b/src/TcpPullAgent.h new file mode 100644 index 0000000..068a004 --- /dev/null +++ b/src/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/src/TcpPullClient.cpp b/src/TcpPullClient.cpp new file mode 100644 index 0000000..cd79d57 --- /dev/null +++ b/src/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/src/TcpPullClient.h b/src/TcpPullClient.h new file mode 100644 index 0000000..e0118c8 --- /dev/null +++ b/src/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/src/TcpPullServer.cpp b/src/TcpPullServer.cpp new file mode 100644 index 0000000..8ed7731 --- /dev/null +++ b/src/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/src/TcpPullServer.h b/src/TcpPullServer.h new file mode 100644 index 0000000..19213b9 --- /dev/null +++ b/src/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/src/TcpServer.cpp b/src/TcpServer.cpp new file mode 100644 index 0000000..d0c99fe --- /dev/null +++ b/src/TcpServer.cpp @@ -0,0 +1,1164 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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))); + VERIFY(addr.IsIPv4() || IS_NO_ERROR(::SSO_DualStack(soListen, m_bDualStack))); + + 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() +{ + if(!m_ioDispatcher.Start(this, m_dwAcceptSocketCount, m_dwWorkerThreadCount)) + { + SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + if(!m_thGC.Start()) + { + SetLastError(SE_GC_START, __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; + } + } + + 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(); + m_thGC.Stop(); +} + +void CTcpServer::ReleaseClientSocket() +{ + VERIFY(m_bfActiveSockets.IsEmpty()); + m_bfActiveSockets.Reset(); +} + +void CTcpServer::ReleaseFreeSocket() +{ + m_lsFreeSocket.Clear(); + 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; + } + + 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/src/TcpServer.h b/src/TcpServer.h new file mode 100644 index 0000000..c47cf90 --- /dev/null +++ b/src/TcpServer.h @@ -0,0 +1,313 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +{ + using CGCThread = CGCThreadT; + friend class CGCThreadT; + +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 void SetDualStack (BOOL bDualStack) {ENSURE_HAS_STOPPED(); m_bDualStack = bDualStack;} + + 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;} + virtual BOOL IsDualStack () {return m_bDualStack;} + +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_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) + , m_bDualStack (TRUE) + , m_thGC (this) + { + 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; + BOOL m_bDualStack; + +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; + + CGCThread m_thGC; + + TSocketObjPtrPool m_bfActiveSockets; + + TSocketObjPtrList m_lsFreeSocket; + TSocketObjPtrQueue m_lsGCSocket; + + CIODispatcher m_ioDispatcher; +}; diff --git a/src/UdpArqClient.cpp b/src/UdpArqClient.cpp new file mode 100644 index 0000000..9b6aa65 --- /dev/null +++ b/src/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/src/UdpArqClient.h b/src/UdpArqClient.h new file mode 100644 index 0000000..d9e8cb2 --- /dev/null +++ b/src/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/src/UdpArqServer.cpp b/src/UdpArqServer.cpp new file mode 100644 index 0000000..6eac8d4 --- /dev/null +++ b/src/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/src/UdpArqServer.h b/src/UdpArqServer.h new file mode 100644 index 0000000..2572606 --- /dev/null +++ b/src/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/src/UdpCast.cpp b/src/UdpCast.cpp new file mode 100644 index 0000000..997c662 --- /dev/null +++ b/src/UdpCast.cpp @@ -0,0 +1,696 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ + if(::IsStrNotEmpty(lpszBindAddress)) + { + if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, bindAddr)) + return FALSE; + } + + 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, bindAddr.family)) + return FALSE; + + if(!bindAddr.IsSpecified()) + { + bindAddr.family = m_castAddr.family; + bindAddr.SetPort(usPort); + } + + if(m_enCastMode == CM_BROADCAST && bindAddr.IsIPv6()) + { + ::WSASetLastError(ERROR_PFNOSUPPORT); + 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/src/UdpCast.h b/src/UdpCast.h new file mode 100644 index 0000000..6ee3b91 --- /dev/null +++ b/src/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/src/UdpClient.cpp b/src/UdpClient.cpp new file mode 100644 index 0000000..3c3fb83 --- /dev/null +++ b/src/UdpClient.cpp @@ -0,0 +1,824 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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) +{ + if(::IsStrNotEmpty(lpszBindAddress)) + { + if(!::sockaddr_A_2_IN(lpszBindAddress, 0, addrBind)) + return FALSE; + } + + HP_SCOPE_HOST host(lpszRemoteAddress); + + if(!::GetSockAddrByHostName(host.addr, usPort, addrRemote, addrBind.family)) + 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/src/UdpClient.h b/src/UdpClient.h new file mode 100644 index 0000000..6c5b40b --- /dev/null +++ b/src/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/src/UdpNode.cpp b/src/UdpNode.cpp new file mode 100644 index 0000000..6b6120d --- /dev/null +++ b/src/UdpNode.cpp @@ -0,0 +1,758 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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))); + VERIFY(bindAddr.IsIPv4() || IS_NO_ERROR(::SSO_DualStack(soListen, m_bDualStack))); + + 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/src/UdpNode.h b/src/UdpNode.h new file mode 100644 index 0000000..8eec3e9 --- /dev/null +++ b/src/UdpNode.h @@ -0,0 +1,202 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 SetDualStack (BOOL bDualStack) {ENSURE_HAS_STOPPED(); m_bDualStack = bDualStack;} + 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 BOOL IsDualStack () {return m_bDualStack;} + 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_bDualStack (TRUE) + , 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; + BOOL m_bDualStack; + +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/src/UdpServer.cpp b/src/UdpServer.cpp new file mode 100644 index 0000000..84df40c --- /dev/null +++ b/src/UdpServer.cpp @@ -0,0 +1,1198 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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))); + VERIFY(addr.IsIPv4() || IS_NO_ERROR(::SSO_DualStack(soListen, m_bDualStack))); + + 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() +{ + if(!m_ioDispatcher.Start(this, m_dwPostReceiveCount, m_dwWorkerThreadCount)) + { + SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + if(!m_thGC.Start()) + { + SetLastError(SE_GC_START, __FUNCTION__, ::WSAGetLastError()); + return FALSE; + } + + 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; + } + } + + 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(); + m_thGC.Stop(); +} + +void CUdpServer::ReleaseClientSocket() +{ + VERIFY(m_bfActiveSockets.IsEmpty()); + m_bfActiveSockets.Reset(); + + CWriteLock locallock(m_csClientSocket); + m_mpClientAddr.clear(); +} + +void CUdpServer::ReleaseFreeSocket() +{ + m_lsFreeSocket.Clear(); + 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; + + 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/src/UdpServer.h b/src/UdpServer.h new file mode 100644 index 0000000..4ecd17d --- /dev/null +++ b/src/UdpServer.h @@ -0,0 +1,308 @@ +/* + * Copyright: JessMA Open Source (ldcsaa@gmail.com) + * + * Author : Bruce Liang + * Website : https://github.com/ldcsaa + * Project : https://github.com/ldcsaa/HP-Socket + * Blog : http://www.cnblogs.com/ldcsaa + * Wiki : http://www.oschina.net/p/hp-socket + * QQ Group : 44636872, 75375912 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + using CGCThread = CGCThreadT; + + friend class CGCThreadT; + +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 void SetDualStack (BOOL bDualStack) {ENSURE_HAS_STOPPED(); m_bDualStack = bDualStack;} + + 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;} + virtual BOOL IsDualStack () {return m_bDualStack;} + +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_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) + , m_bDualStack (TRUE) + , m_thGC (this) + { + 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; + BOOL m_bDualStack; + +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; + + CGCThread m_thGC; + + 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/src/common/BufferPool.cpp b/src/common/BufferPool.cpp new file mode 100644 index 0000000..447b698 --- /dev/null +++ b/src/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/src/common/BufferPool.h b/src/common/BufferPool.h new file mode 100644 index 0000000..9e2d48f --- /dev/null +++ b/src/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 "../../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/src/common/BufferPtr.h b/src/common/BufferPtr.h new file mode 100644 index 0000000..a4af205 --- /dev/null +++ b/src/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 "../../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/src/common/CriSec.h b/src/common/CriSec.h new file mode 100644 index 0000000..0bec7b4 --- /dev/null +++ b/src/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 "../../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(T iCount = 0) {return SetCount(iCount);} + 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(T iCount = 0) {return SetCount(iCount);} + 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/src/common/Event.cpp b/src/common/Event.cpp new file mode 100644 index 0000000..9cc69fe --- /dev/null +++ b/src/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/src/common/Event.h b/src/common/Event.h new file mode 100644 index 0000000..ab57099 --- /dev/null +++ b/src/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 "../../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/src/common/FileHelper.cpp b/src/common/FileHelper.cpp new file mode 100644 index 0000000..a8154ca --- /dev/null +++ b/src/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/src/common/FileHelper.h b/src/common/FileHelper.h new file mode 100644 index 0000000..4f71c8e --- /dev/null +++ b/src/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/src/common/FuncHelper.cpp b/src/common/FuncHelper.cpp new file mode 100644 index 0000000..85c24e3 --- /dev/null +++ b/src/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/src/common/FuncHelper.h b/src/common/FuncHelper.h new file mode 100644 index 0000000..6201ace --- /dev/null +++ b/src/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 "../../include/hpsocket/GlobalDef.h" +#include "../../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/src/common/GeneralHelper.h b/src/common/GeneralHelper.h new file mode 100644 index 0000000..628e1c8 --- /dev/null +++ b/src/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 "../../include/hpsocket/GlobalDef.h" +#include "../../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/src/common/IODispatcher.cpp b/src/common/IODispatcher.cpp new file mode 100644 index 0000000..c499f30 --- /dev/null +++ b/src/common/IODispatcher.cpp @@ -0,0 +1,350 @@ +/* +* Copyright: JessMA Open Source (ldcsaa@gmail.com) +* +* Author : Bruce Liang +* Website : https://github.com/ldcsaa +* Project : https://github.com/ldcsaa/HP-Socket +* Blog : http://www.cnblogs.com/ldcsaa +* Wiki : http://www.oschina.net/p/hp-socket +* QQ Group : 44636872, 75375912 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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/src/common/IODispatcher.h b/src/common/IODispatcher.h new file mode 100644 index 0000000..0a1ce10 --- /dev/null +++ b/src/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 "../../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/src/common/PollHelper.cpp b/src/common/PollHelper.cpp new file mode 100644 index 0000000..57b3cf8 --- /dev/null +++ b/src/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/src/common/PollHelper.h b/src/common/PollHelper.h new file mode 100644 index 0000000..178e67b --- /dev/null +++ b/src/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 "../../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/src/common/PrivateHeap.h b/src/common/PrivateHeap.h new file mode 100644 index 0000000..0be0713 --- /dev/null +++ b/src/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 "../../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/src/common/RWLock.cpp b/src/common/RWLock.cpp new file mode 100644 index 0000000..e7a0687 --- /dev/null +++ b/src/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/src/common/RWLock.h b/src/common/RWLock.h new file mode 100644 index 0000000..8e9dd74 --- /dev/null +++ b/src/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 "../../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/src/common/RingBuffer.h b/src/common/RingBuffer.h new file mode 100644 index 0000000..da4a226 --- /dev/null +++ b/src/common/RingBuffer.h @@ -0,0 +1,1727 @@ +/* +* Copyright: JessMA Open Source (ldcsaa@gmail.com) +* +* Author : Bruce Liang +* Website : https://github.com/ldcsaa +* Project : https://github.com/ldcsaa/HP-Socket +* Blog : http://www.cnblogs.com/ldcsaa +* Wiki : http://www.oschina.net/p/hp-socket +* QQ Group : 44636872, 75375912 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT 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 "../../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); + } + + DECLARE_NO_COPY_CLASS(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); + } + + DECLARE_NO_COPY_CLASS(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/src/common/STLHelper.h b/src/common/STLHelper.h new file mode 100644 index 0000000..cf81106 --- /dev/null +++ b/src/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 "../../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/src/common/Semaphore.h b/src/common/Semaphore.h new file mode 100644 index 0000000..cf0fb86 --- /dev/null +++ b/src/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 "../../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/src/common/SignalHandler.h b/src/common/SignalHandler.h new file mode 100644 index 0000000..f49e9a5 --- /dev/null +++ b/src/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 "../../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/src/common/Singleton.h b/src/common/Singleton.h new file mode 100644 index 0000000..0915a12 --- /dev/null +++ b/src/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 "../../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/src/common/StringT.h b/src/common/StringT.h new file mode 100644 index 0000000..f4b916a --- /dev/null +++ b/src/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) + memmove((PXSTR)lpszBegin, lpszEnd, iNewLength * 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) + memmove((PXSTR)lpszBegin, lpszEnd, iNewLength * 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/src/common/SysHelper.cpp b/src/common/SysHelper.cpp new file mode 100644 index 0000000..e12cbad --- /dev/null +++ b/src/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/src/common/SysHelper.h b/src/common/SysHelper.h new file mode 100644 index 0000000..3e2e2f3 --- /dev/null +++ b/src/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 "../../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/src/common/Thread.cpp b/src/common/Thread.cpp new file mode 100644 index 0000000..059d607 --- /dev/null +++ b/src/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/src/common/Thread.h b/src/common/Thread.h new file mode 100644 index 0000000..beb7850 --- /dev/null +++ b/src/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 "../../include/hpsocket/GlobalDef.h" +#include "../../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/src/common/http/Readme.txt b/src/common/http/Readme.txt new file mode 100644 index 0000000..375eb16 --- /dev/null +++ b/src/common/http/Readme.txt @@ -0,0 +1,14 @@ +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" +2. llhttp_url.c ignore warning: "-Wconversion", "-Wsign-conversion" +3. llhttp_internal.c ignore warning: "-Wconversion", "-Wsign-conversion" "-Wunused-variable" "-Wunreachable-code" +4. llhttp_url.h : LLHTTP_STRICT_MODE set default value 1 +5. llhttp.h : delete codes below + #elif defined(_WIN32) + #define LLHTTP_EXPORT __declspec(dllexport) \ No newline at end of file diff --git a/src/common/http/llhttp.h b/src/common/http/llhttp.h new file mode 100644 index 0000000..a62c62a --- /dev/null +++ b/src/common/http/llhttp.h @@ -0,0 +1,907 @@ + +#ifndef INCLUDE_LLHTTP_H_ +#define INCLUDE_LLHTTP_H_ + +#define LLHTTP_VERSION_MAJOR 9 +#define LLHTTP_VERSION_MINOR 3 +#define LLHTTP_VERSION_PATCH 0 + +#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, + HPE_CB_PROTOCOL_COMPLETE = 38 +}; +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) \ + XX(38, CB_PROTOCOL_COMPLETE, CB_PROTOCOL_COMPLETE) \ + + +#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_protocol; + 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_protocol_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/src/common/http/llhttp_api.c b/src/common/http/llhttp_api.c new file mode 100644 index 0000000..408348e --- /dev/null +++ b/src/common/http/llhttp_api.c @@ -0,0 +1,519 @@ +#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 = { + .on_message_begin = wasm_on_message_begin, + .on_url = wasm_on_url, + .on_status = wasm_on_status, + .on_header_field = wasm_on_header_field, + .on_header_value = wasm_on_header_value, + .on_headers_complete = wasm_on_headers_complete_wrap, + .on_body = wasm_on_body, + .on_message_complete = wasm_on_message_complete, +}; + + +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_protocol(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_protocol, p, endp - p); + return err; +} + + +int llhttp__on_protocol_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_protocol_complete); + 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/src/common/http/llhttp_internal.c b/src/common/http/llhttp_internal.c new file mode 100644 index 0000000..77069cb --- /dev/null +++ b/src/common/http/llhttp_internal.c @@ -0,0 +1,10111 @@ +#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 __ARM_NEON__ + #include +#endif /* __ARM_NEON__ */ + +#ifdef __wasm__ + #include +#endif /* __wasm__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) + #define UNREACHABLE __assume(0) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) + #define UNREACHABLE __builtin_unreachable() +#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[] = { + 'T', 'T', 'P' +}; +static const unsigned char llparse_blob59[] = { + 'C', 'E' +}; +static const unsigned char llparse_blob60[] = { + 'T', 'S', 'P' +}; +static const unsigned char llparse_blob61[] = { + 'A', 'D' +}; +static const unsigned char llparse_blob62[] = { + '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_67, + s_n_llhttp__internal__n_error_74, + s_n_llhttp__internal__n_req_http_minor, + s_n_llhttp__internal__n_error_75, + s_n_llhttp__internal__n_req_http_dot, + s_n_llhttp__internal__n_error_76, + s_n_llhttp__internal__n_req_http_major, + s_n_llhttp__internal__n_span_start_llhttp__on_version, + s_n_llhttp__internal__n_req_after_protocol, + s_n_llhttp__internal__n_invoke_load_method, + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete, + s_n_llhttp__internal__n_error_82, + s_n_llhttp__internal__n_req_after_http_start_1, + s_n_llhttp__internal__n_invoke_load_method_2, + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1, + s_n_llhttp__internal__n_req_after_http_start_2, + s_n_llhttp__internal__n_invoke_load_method_3, + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2, + s_n_llhttp__internal__n_req_after_http_start_3, + s_n_llhttp__internal__n_req_after_http_start, + s_n_llhttp__internal__n_span_start_llhttp__on_protocol, + 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_93, + s_n_llhttp__internal__n_error_107, + s_n_llhttp__internal__n_res_http_minor, + s_n_llhttp__internal__n_error_108, + s_n_llhttp__internal__n_res_http_dot, + s_n_llhttp__internal__n_error_109, + s_n_llhttp__internal__n_res_http_major, + s_n_llhttp__internal__n_span_start_llhttp__on_version_1, + s_n_llhttp__internal__n_res_after_protocol, + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3, + s_n_llhttp__internal__n_error_115, + s_n_llhttp__internal__n_res_after_start_1, + s_n_llhttp__internal__n_res_after_start_2, + s_n_llhttp__internal__n_res_after_start_3, + s_n_llhttp__internal__n_res_after_start, + s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1, + 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_protocol( + 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__on_protocol_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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 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__ */ + #ifdef __ARM_NEON__ + while (endp - p >= 16) { + uint8x16_t input; + uint8x16_t single; + uint8x16_t mask; + uint8x8_t narrow; + uint64_t match_mask; + int match_len; + + /* Load input */ + input = vld1q_u8(p); + /* Find first character that does not match `ranges` */ + single = vceqq_u8(input, vdupq_n_u8(0x9)); + mask = single; + single = vandq_u16( + vcgeq_u8(input, vdupq_n_u8(' ')), + vcleq_u8(input, vdupq_n_u8('~')) + ); + mask = vorrq_u16(mask, single); + single = vandq_u16( + vcgeq_u8(input, vdupq_n_u8(0x80)), + vcleq_u8(input, vdupq_n_u8(0xff)) + ); + mask = vorrq_u16(mask, single); + narrow = vshrn_n_u16(mask, 4); + match_mask = ~vget_lane_u64(vreinterpret_u64_u8(narrow), 0); + match_len = __builtin_ctzll(match_mask) >> 2; + if (match_len != 16) { + p += match_len; + goto s_n_llhttp__internal__n_header_value_otherwise; + } + p += 16; + } + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #endif /* __ARM_NEON__ */ + #ifdef __wasm_simd128__ + while (endp - p >= 16) { + v128_t input; + v128_t mask; + v128_t single; + int match_len; + + /* Load input */ + input = wasm_v128_load(p); + /* Find first character that does not match `ranges` */ + single = wasm_i8x16_eq(input, wasm_u8x16_const_splat(0x9)); + mask = single; + single = wasm_v128_and( + wasm_i8x16_ge(input, wasm_u8x16_const_splat(' ')), + wasm_i8x16_le(input, wasm_u8x16_const_splat('~')) + ); + mask = wasm_v128_or(mask, single); + single = wasm_v128_and( + wasm_i8x16_ge(input, wasm_u8x16_const_splat(0x80)), + wasm_i8x16_le(input, wasm_u8x16_const_splat(0xff)) + ); + mask = wasm_v128_or(mask, single); + match_len = __builtin_ctz( + ~wasm_i8x16_bitmask(mask) + ); + if (match_len != 16) { + p += match_len; + goto s_n_llhttp__internal__n_header_value_otherwise; + } + p += 16; + } + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #endif /* __wasm_simd128__ */ + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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_72; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_pri_upgrade; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_73; + } + } + UNREACHABLE; + } + 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; + } + 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_71; + } + } + UNREACHABLE; + } + 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; + } + 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_68; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_67: + s_n_llhttp__internal__n_error_67: { + 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; + } + case s_n_llhttp__internal__n_error_74: + s_n_llhttp__internal__n_error_74: { + 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; + } + 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; + } + case s_n_llhttp__internal__n_error_75: + s_n_llhttp__internal__n_error_75: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + 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; + } + case s_n_llhttp__internal__n_error_76: + s_n_llhttp__internal__n_error_76: { + 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; + } + 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; + } + 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; + } + case s_n_llhttp__internal__n_req_after_protocol: + s_n_llhttp__internal__n_req_after_protocol: { + if (p == endp) { + return s_n_llhttp__internal__n_req_after_protocol; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + } + default: { + goto s_n_llhttp__internal__n_error_77; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_method: + 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_req_after_protocol; + case 1: + goto s_n_llhttp__internal__n_req_after_protocol; + case 2: + goto s_n_llhttp__internal__n_req_after_protocol; + case 3: + goto s_n_llhttp__internal__n_req_after_protocol; + case 4: + goto s_n_llhttp__internal__n_req_after_protocol; + case 5: + goto s_n_llhttp__internal__n_req_after_protocol; + case 6: + goto s_n_llhttp__internal__n_req_after_protocol; + case 7: + goto s_n_llhttp__internal__n_req_after_protocol; + case 8: + goto s_n_llhttp__internal__n_req_after_protocol; + case 9: + goto s_n_llhttp__internal__n_req_after_protocol; + case 10: + goto s_n_llhttp__internal__n_req_after_protocol; + case 11: + goto s_n_llhttp__internal__n_req_after_protocol; + case 12: + goto s_n_llhttp__internal__n_req_after_protocol; + case 13: + goto s_n_llhttp__internal__n_req_after_protocol; + case 14: + goto s_n_llhttp__internal__n_req_after_protocol; + case 15: + goto s_n_llhttp__internal__n_req_after_protocol; + case 16: + goto s_n_llhttp__internal__n_req_after_protocol; + case 17: + goto s_n_llhttp__internal__n_req_after_protocol; + case 18: + goto s_n_llhttp__internal__n_req_after_protocol; + case 19: + goto s_n_llhttp__internal__n_req_after_protocol; + case 20: + goto s_n_llhttp__internal__n_req_after_protocol; + case 21: + goto s_n_llhttp__internal__n_req_after_protocol; + case 22: + goto s_n_llhttp__internal__n_req_after_protocol; + case 23: + goto s_n_llhttp__internal__n_req_after_protocol; + case 24: + goto s_n_llhttp__internal__n_req_after_protocol; + case 25: + goto s_n_llhttp__internal__n_req_after_protocol; + case 26: + goto s_n_llhttp__internal__n_req_after_protocol; + case 27: + goto s_n_llhttp__internal__n_req_after_protocol; + case 28: + goto s_n_llhttp__internal__n_req_after_protocol; + case 29: + goto s_n_llhttp__internal__n_req_after_protocol; + case 30: + goto s_n_llhttp__internal__n_req_after_protocol; + case 31: + goto s_n_llhttp__internal__n_req_after_protocol; + case 32: + goto s_n_llhttp__internal__n_req_after_protocol; + case 33: + goto s_n_llhttp__internal__n_req_after_protocol; + case 34: + goto s_n_llhttp__internal__n_req_after_protocol; + case 46: + goto s_n_llhttp__internal__n_req_after_protocol; + default: + goto s_n_llhttp__internal__n_error_66; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete: { + switch (llhttp__on_protocol_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method; + case 21: + goto s_n_llhttp__internal__n_pause_22; + default: + goto s_n_llhttp__internal__n_error_65; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_82: + s_n_llhttp__internal__n_error_82: { + state->error = 0x8; + state->reason = "Expected HTTP/, RTSP/ or ICE/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_http_start_1: + s_n_llhttp__internal__n_req_after_http_start_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_after_http_start_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_after_http_start_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_method_2: + 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_req_after_protocol; + default: + goto s_n_llhttp__internal__n_error_79; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1: { + switch (llhttp__on_protocol_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method_2; + case 21: + goto s_n_llhttp__internal__n_pause_23; + default: + goto s_n_llhttp__internal__n_error_78; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_http_start_2: + s_n_llhttp__internal__n_req_after_http_start_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_after_http_start_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_after_http_start_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_method_3: + 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_req_after_protocol; + case 3: + goto s_n_llhttp__internal__n_req_after_protocol; + case 6: + goto s_n_llhttp__internal__n_req_after_protocol; + case 35: + goto s_n_llhttp__internal__n_req_after_protocol; + case 36: + goto s_n_llhttp__internal__n_req_after_protocol; + case 37: + goto s_n_llhttp__internal__n_req_after_protocol; + case 38: + goto s_n_llhttp__internal__n_req_after_protocol; + case 39: + goto s_n_llhttp__internal__n_req_after_protocol; + case 40: + goto s_n_llhttp__internal__n_req_after_protocol; + case 41: + goto s_n_llhttp__internal__n_req_after_protocol; + case 42: + goto s_n_llhttp__internal__n_req_after_protocol; + case 43: + goto s_n_llhttp__internal__n_req_after_protocol; + case 44: + goto s_n_llhttp__internal__n_req_after_protocol; + case 45: + goto s_n_llhttp__internal__n_req_after_protocol; + default: + goto s_n_llhttp__internal__n_error_81; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2: { + switch (llhttp__on_protocol_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method_3; + case 21: + goto s_n_llhttp__internal__n_pause_24; + default: + goto s_n_llhttp__internal__n_error_80; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_http_start_3: + s_n_llhttp__internal__n_req_after_http_start_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_after_http_start_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_after_http_start_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_http_start: + s_n_llhttp__internal__n_req_after_http_start: { + if (p == endp) { + return s_n_llhttp__internal__n_req_after_http_start; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_after_http_start_1; + } + case 'I': { + p++; + goto s_n_llhttp__internal__n_req_after_http_start_2; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_req_after_http_start_3; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_protocol: + s_n_llhttp__internal__n_span_start_llhttp__on_protocol: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_protocol; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_protocol; + goto s_n_llhttp__internal__n_req_after_http_start; + UNREACHABLE; + } + 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; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol; + } + } + UNREACHABLE; + } + 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; + } + 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; + } + 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_83; + } + } + UNREACHABLE; + } + 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; + } + 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_84; + } + } + UNREACHABLE; + } + 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_85; + } + } + UNREACHABLE; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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_86; + } + default: { + goto s_n_llhttp__internal__n_error_87; + } + } + UNREACHABLE; + } + 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_88; + } + } + UNREACHABLE; + } + 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_89; + } + } + UNREACHABLE; + } + 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_89; + } + } + UNREACHABLE; + } + 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; + } + 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_90; + } + } + UNREACHABLE; + } + 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_91; + } + } + UNREACHABLE; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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_92; + } + } + UNREACHABLE; + } + 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_29; + default: + goto s_n_llhttp__internal__n_error_111; + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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_112; + } + } + UNREACHABLE; + } + 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; + } + 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; + } + 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_98; + } + UNREACHABLE; + } + 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; + } + 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; + } + 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_99; + } + } + UNREACHABLE; + } + 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_101; + } + } + UNREACHABLE; + } + 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_103; + } + } + UNREACHABLE; + } + 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_105; + } + } + UNREACHABLE; + } + 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_106; + } + } + UNREACHABLE; + } + 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_28; + default: + goto s_n_llhttp__internal__n_error_94; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_93: + s_n_llhttp__internal__n_error_93: { + 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; + } + case s_n_llhttp__internal__n_error_107: + s_n_llhttp__internal__n_error_107: { + 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; + } + 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; + } + case s_n_llhttp__internal__n_error_108: + s_n_llhttp__internal__n_error_108: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + 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; + } + case s_n_llhttp__internal__n_error_109: + s_n_llhttp__internal__n_error_109: { + 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; + } + 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; + } + 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; + } + case s_n_llhttp__internal__n_res_after_protocol: + s_n_llhttp__internal__n_res_after_protocol: { + if (p == endp) { + return s_n_llhttp__internal__n_res_after_protocol; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + default: { + goto s_n_llhttp__internal__n_error_114; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3: + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3: { + switch (llhttp__on_protocol_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_res_after_protocol; + case 21: + goto s_n_llhttp__internal__n_pause_30; + default: + goto s_n_llhttp__internal__n_error_113; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_115: + s_n_llhttp__internal__n_error_115: { + state->error = 0x8; + state->reason = "Expected HTTP/, RTSP/ or ICE/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_start_1: + s_n_llhttp__internal__n_res_after_start_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_res_after_start_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4; + } + case kMatchPause: { + return s_n_llhttp__internal__n_res_after_start_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_start_2: + s_n_llhttp__internal__n_res_after_start_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_res_after_start_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4; + } + case kMatchPause: { + return s_n_llhttp__internal__n_res_after_start_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_start_3: + s_n_llhttp__internal__n_res_after_start_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_res_after_start_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_protocol_4; + } + case kMatchPause: { + return s_n_llhttp__internal__n_res_after_start_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_start: + s_n_llhttp__internal__n_res_after_start: { + if (p == endp) { + return s_n_llhttp__internal__n_res_after_start; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_res_after_start_1; + } + case 'I': { + p++; + goto s_n_llhttp__internal__n_res_after_start_2; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_res_after_start_3; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1: + s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_protocol; + goto s_n_llhttp__internal__n_res_after_start; + UNREACHABLE; + } + 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_26; + default: + goto s_n_llhttp__internal__n_error_1; + } + UNREACHABLE; + } + 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_blob61, 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_110; + } + } + UNREACHABLE; + } + 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; + } + 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_blob62, 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_110; + } + } + UNREACHABLE; + } + 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_110; + } + } + UNREACHABLE; + } + 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_110; + } + } + UNREACHABLE; + } + 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; + } + 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; + } + 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_span_start_llhttp__on_protocol_1; + default: + goto s_n_llhttp__internal__n_start_req_or_res; + } + UNREACHABLE; + } + 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; + } + 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; + } + default: + UNREACHABLE; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + s_n_llhttp__internal__n_error_72: { + 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; + } + s_n_llhttp__internal__n_error_73: { + 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; + } + s_n_llhttp__internal__n_error_70: { + 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; + } + 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_70; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_69: { + 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; + } + 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_69; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_71: { + 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; + } + 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; + } + s_n_llhttp__internal__n_error_68: { + 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; + } + 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; + } + 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_67; + return s_error; + } + goto s_n_llhttp__internal__n_error_67; + UNREACHABLE; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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_74; + return s_error; + } + goto s_n_llhttp__internal__n_error_74; + UNREACHABLE; + } + 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_75; + return s_error; + } + goto s_n_llhttp__internal__n_error_75; + UNREACHABLE; + } + 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; + } + 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_76; + return s_error; + } + goto s_n_llhttp__internal__n_error_76; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_77: { + state->error = 0x8; + state->reason = "Expected HTTP/, RTSP/ or ICE/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_66: { + 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; + } + s_n_llhttp__internal__n_pause_22: { + state->error = 0x15; + state->reason = "on_protocol_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_65: { + state->error = 0x26; + state->reason = "`on_protocol_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(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_protocol_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(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_82; + return s_error; + } + goto s_n_llhttp__internal__n_error_82; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_79: { + 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; + } + s_n_llhttp__internal__n_pause_23: { + state->error = 0x15; + state->reason = "on_protocol_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_2; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_78: { + state->error = 0x26; + state->reason = "`on_protocol_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(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_protocol_complete_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_81: { + 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; + } + s_n_llhttp__internal__n_pause_24: { + state->error = 0x15; + state->reason = "on_protocol_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_3; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_80: { + state->error = 0x26; + state->reason = "`on_protocol_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(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_protocol_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_25: { + 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; + } + 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; + } + 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_25; + default: + goto s_n_llhttp__internal__n_error_64; + } + UNREACHABLE; + } + 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; + } + 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; + } + 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; + } + 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; + } + s_n_llhttp__internal__n_error_83: { + 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; + } + 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; + } + 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; + } + 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; + } + s_n_llhttp__internal__n_error_84: { + 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; + } + s_n_llhttp__internal__n_error_85: { + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + s_n_llhttp__internal__n_error_86: { + 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; + } + s_n_llhttp__internal__n_error_87: { + 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; + } + s_n_llhttp__internal__n_error_88: { + 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; + } + s_n_llhttp__internal__n_error_89: { + 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; + } + s_n_llhttp__internal__n_error_90: { + 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; + } + s_n_llhttp__internal__n_error_91: { + 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; + } + 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; + } + s_n_llhttp__internal__n_error_92: { + 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; + } + s_n_llhttp__internal__n_pause_29: { + 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; + } + s_n_llhttp__internal__n_error_111: { + 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; + } + 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; + } + 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; + } + s_n_llhttp__internal__n_error_112: { + 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; + } + s_n_llhttp__internal__n_error_104: { + 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; + } + s_n_llhttp__internal__n_error_102: { + 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; + } + 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; + } + s_n_llhttp__internal__n_pause_27: { + 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; + } + s_n_llhttp__internal__n_error_96: { + 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; + } + 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_27; + default: + goto s_n_llhttp__internal__n_error_96; + } + UNREACHABLE; + } + 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; + } + 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_95; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_97: { + 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; + } + 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_97; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_98: { + 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; + } + 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; + } + 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; + } + s_n_llhttp__internal__n_error_99: { + 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; + } + 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_100; + default: + goto s_n_llhttp__internal__n_res_status_code_otherwise; + } + UNREACHABLE; + } + 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; + } + 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_102; + default: + goto s_n_llhttp__internal__n_res_status_code_digit_3; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_103: { + 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; + } + 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_104; + default: + goto s_n_llhttp__internal__n_res_status_code_digit_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_105: { + 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; + } + 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; + } + s_n_llhttp__internal__n_error_106: { + 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; + } + s_n_llhttp__internal__n_pause_28: { + 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; + } + s_n_llhttp__internal__n_error_94: { + 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; + } + 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; + } + 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_93; + return s_error; + } + goto s_n_llhttp__internal__n_error_93; + UNREACHABLE; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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_107; + return s_error; + } + goto s_n_llhttp__internal__n_error_107; + UNREACHABLE; + } + 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_108; + return s_error; + } + goto s_n_llhttp__internal__n_error_108; + UNREACHABLE; + } + 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; + } + 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_109; + return s_error; + } + goto s_n_llhttp__internal__n_error_109; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_114: { + state->error = 0x8; + state->reason = "Expected HTTP/, RTSP/ or ICE/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_30: { + state->error = 0x15; + state->reason = "on_protocol_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_protocol; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_113: { + state->error = 0x26; + state->reason = "`on_protocol_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(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_protocol_complete_3; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(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_115; + return s_error; + } + goto s_n_llhttp__internal__n_error_115; + UNREACHABLE; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + s_n_llhttp__internal__n_error_110: { + 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; + } + 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; + } + 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; + } + s_n_llhttp__internal__n_pause_31: { + 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; + } + 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; + } + 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_31; + default: + goto s_n_llhttp__internal__n_error; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_32: { + 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; + } + s_n_llhttp__internal__n_error_116: { + 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; + } + 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_32; + default: + goto s_n_llhttp__internal__n_error_116; + } + UNREACHABLE; + } + 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; + } +} + +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/src/common/http/llhttp_support.c b/src/common/http/llhttp_support.c new file mode 100644 index 0000000..1ab91a5 --- /dev/null +++ b/src/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/src/common/http/llhttp_url.c b/src/common/http/llhttp_url.c new file mode 100644 index 0000000..c0ac411 --- /dev/null +++ b/src/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/src/common/http/llhttp_url.h b/src/common/http/llhttp_url.h new file mode 100644 index 0000000..146633f --- /dev/null +++ b/src/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/src/common/kcp/Readme.txt b/src/common/kcp/Readme.txt new file mode 100644 index 0000000..01d8c7c --- /dev/null +++ b/src/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/src/common/kcp/ikcp.c b/src/common/kcp/ikcp.c new file mode 100644 index 0000000..7d54f0e --- /dev/null +++ b/src/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/src/common/kcp/ikcp.h b/src/common/kcp/ikcp.h new file mode 100644 index 0000000..b8c337f --- /dev/null +++ b/src/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 + +