├── .bazelrc ├── .clang-format ├── .gitattributes ├── .github └── workflows │ ├── CI.yml │ └── benchmark.yml ├── .gitignore ├── .travis.yml ├── BUILD.bazel ├── BUILD.md ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── Makefile.in ├── Makefile.vars ├── README-CN.md ├── README.md ├── TREE.md ├── WORKSPACE.bazel ├── base ├── README.md ├── array.h ├── hatomic.h ├── hbase.c ├── hbase.h ├── hbuf.h ├── hdef.h ├── heap.h ├── hendian.h ├── herr.c ├── herr.h ├── hlog.c ├── hlog.h ├── hmain.c ├── hmain.h ├── hmath.h ├── hmutex.h ├── hplatform.h ├── hproc.h ├── hsocket.c ├── hsocket.h ├── hsysinfo.h ├── hthread.h ├── htime.c ├── htime.h ├── hversion.c ├── hversion.h ├── list.h ├── netinet.h ├── queue.h ├── rbtree.c └── rbtree.h ├── cert ├── cacert.pem ├── gen.sh ├── server.crt └── server.key ├── cmake ├── ios.toolchain.cmake ├── libhvConfig.cmake ├── utils.cmake └── vars.cmake ├── config.ini ├── config.mk ├── configure ├── cpputil ├── RAII.cpp ├── README.md ├── ThreadLocalStorage.cpp ├── ThreadLocalStorage.h ├── hasync.cpp ├── hasync.h ├── hdir.cpp ├── hdir.h ├── hfile.h ├── hmap.h ├── hobjectpool.h ├── hpath.cpp ├── hpath.h ├── hscope.h ├── hstring.cpp ├── hstring.h ├── hthreadpool.h ├── hurl.cpp ├── hurl.h ├── ifconfig.cpp ├── ifconfig.h ├── iniparser.cpp ├── iniparser.h ├── json.hpp └── singleton.h ├── docs ├── API.md ├── PLAN.md └── cn │ ├── Channel.md │ ├── EventLoop.md │ ├── HttpClient.md │ ├── HttpContext.md │ ├── HttpMessage.md │ ├── HttpServer.md │ ├── README.md │ ├── TcpClient.md │ ├── TcpServer.md │ ├── UdpClient.md │ ├── UdpServer.md │ ├── WebSocketClient.md │ ├── WebSocketServer.md │ ├── hbase.md │ ├── hlog.md │ └── hloop.md ├── echo-servers ├── asio_echo.cpp ├── benchmark.sh ├── build.sh ├── libev_echo.c ├── libevent_echo.c ├── libhv_echo.c ├── libuv_echo.c ├── muduo_echo.cpp ├── pingpong_client.cpp └── poco_echo.cpp ├── etc ├── hmain_test.conf ├── httpd.conf └── nginx.conf ├── event ├── README.md ├── epoll.c ├── evport.c ├── hevent.c ├── hevent.h ├── hloop.c ├── hloop.h ├── iocp.c ├── iowatcher.h ├── kcp │ ├── LICENSE │ ├── hkcp.c │ ├── hkcp.h │ ├── ikcp.c │ └── ikcp.h ├── kqueue.c ├── nio.c ├── nlog.c ├── nlog.h ├── noevent.c ├── overlapio.c ├── overlapio.h ├── poll.c ├── rudp.c ├── rudp.h ├── select.c ├── unpack.c ├── unpack.h └── wepoll │ ├── LICENSE │ ├── README.md │ ├── wepoll.c │ └── wepoll.h ├── evpp ├── Buffer.h ├── Channel.h ├── Event.h ├── EventLoop.h ├── EventLoopThread.h ├── EventLoopThreadPool.h ├── EventLoopThreadPool_test.cpp ├── EventLoopThread_test.cpp ├── EventLoop_test.cpp ├── README.md ├── Status.h ├── TcpClient.h ├── TcpClientEventLoop_test.cpp ├── TcpClient_test.cpp ├── TcpServer.h ├── TcpServer_test.cpp ├── TimerThread.h ├── TimerThread_test.cpp ├── UdpClient.h ├── UdpClient_test.cpp ├── UdpServer.h ├── UdpServer_test.cpp └── build_test.sh ├── examples ├── BUILD.bazel ├── CMakeLists.txt ├── README.md ├── consul │ ├── consul.cpp │ ├── consul.h │ └── main.cpp ├── curl.cpp ├── hloop_test.c ├── hmain_test.cpp ├── htimer_test.c ├── http_client_test.cpp ├── http_server_test.cpp ├── httpd │ ├── handler.cpp │ ├── handler.h │ ├── httpd.cpp │ ├── router.cpp │ └── router.h ├── jsonrpc │ ├── cJSON.c │ ├── cJSON.h │ ├── handler.h │ ├── jsonrpc_client.c │ ├── jsonrpc_server.c │ └── router.h ├── kcptun │ ├── README.md │ ├── client │ │ └── main.cpp │ ├── kcptun.png │ ├── server │ │ └── main.cpp │ └── smux │ │ ├── smux.cpp │ │ └── smux.h ├── mqtt │ ├── mqtt_client_test.cpp │ ├── mqtt_pub.c │ └── mqtt_sub.c ├── multi-thread │ ├── multi-acceptor-processes.c │ ├── multi-acceptor-threads.c │ └── one-acceptor-multi-workers.c ├── nc.c ├── nmap │ ├── main.cpp │ ├── nmap.cpp │ └── nmap.h ├── pipe_test.c ├── protorpc │ ├── handler │ │ ├── calc.h │ │ ├── handler.h │ │ └── login.h │ ├── proto │ │ ├── base.proto │ │ ├── calc.proto │ │ ├── login.proto │ │ └── protoc.sh │ ├── protorpc.c │ ├── protorpc.h │ ├── protorpc_client.cpp │ ├── protorpc_server.cpp │ └── router.h ├── socks5_proxy_server.c ├── tcp_chat_server.c ├── tcp_client_test.c ├── tcp_echo_server.c ├── tcp_proxy_server.c ├── tinyhttpd.c ├── tinyproxyd.c ├── udp_echo_server.c ├── udp_proxy_server.c ├── websocket_client_test.cpp ├── websocket_server_test.cpp ├── wget.cpp └── wrk.cpp ├── getting_started.sh ├── hconfig.h ├── hconfig.h.in ├── hexport.h ├── html ├── EventSource.html ├── WebSocket.html ├── downloads │ ├── libhv-vs-nginx.png │ └── 中文.html ├── error.html ├── index.html ├── uploads │ └── upload.sh └── 中文路径 │ └── 中文名称.txt ├── http ├── Http1Parser.cpp ├── Http1Parser.h ├── Http2Parser.cpp ├── Http2Parser.h ├── HttpMessage.cpp ├── HttpMessage.h ├── HttpParser.cpp ├── HttpParser.h ├── README.md ├── WebSocketChannel.cpp ├── WebSocketChannel.h ├── WebSocketParser.cpp ├── WebSocketParser.h ├── client │ ├── AsyncHttpClient.cpp │ ├── AsyncHttpClient.h │ ├── HttpClient.cpp │ ├── HttpClient.h │ ├── WebSocketClient.cpp │ ├── WebSocketClient.h │ ├── axios.h │ └── requests.h ├── grpcdef.h ├── http2def.h ├── http_content.cpp ├── http_content.h ├── http_parser.c ├── http_parser.h ├── httpdef.c ├── httpdef.h ├── multipart_parser.c ├── multipart_parser.h ├── server │ ├── FileCache.cpp │ ├── FileCache.h │ ├── HttpContext.h │ ├── HttpHandler.cpp │ ├── HttpHandler.h │ ├── HttpMiddleware.cpp │ ├── HttpMiddleware.h │ ├── HttpResponseWriter.cpp │ ├── HttpResponseWriter.h │ ├── HttpServer.cpp │ ├── HttpServer.h │ ├── HttpService.cpp │ ├── HttpService.h │ ├── WebSocketServer.h │ ├── http_page.cpp │ └── http_page.h ├── websocket_parser.c ├── websocket_parser.h ├── wsdef.c └── wsdef.h ├── hv.h ├── hv.rc.in ├── misc ├── grpc_server.h └── win32_getopt.h ├── mqtt ├── mqtt_client.c ├── mqtt_client.h ├── mqtt_protocol.c └── mqtt_protocol.h ├── protocol ├── README.md ├── dns.c ├── dns.h ├── ftp.c ├── ftp.h ├── icmp.c ├── icmp.h ├── smtp.c └── smtp.h ├── scripts ├── check.sh ├── cmake_cross_compile.sh ├── consul.py ├── consul_agent.sh ├── coredump.sh ├── create_repo.sh ├── libhv.cmake ├── shini.sh ├── test-coverage.sh ├── toolchain.sh ├── unittest.sh └── websocket_server.py ├── ssl ├── appletls.c ├── gnutls.c ├── hssl.c ├── hssl.h ├── mbedtls.c ├── nossl.c ├── openssl.c └── wintls.c ├── unittest ├── CMakeLists.txt ├── base64_test.c ├── connect_test.c ├── date_test.c ├── defer_test.cpp ├── ftp_test.c ├── hatomic_test.c ├── hatomic_test.cpp ├── hbase_test.c ├── hlog_test.c ├── hmutex_test.c ├── hpath_test.cpp ├── hstring_test.cpp ├── hthread_test.cpp ├── hurl_test.cpp ├── ifconfig_test.cpp ├── listdir_test.cpp ├── md5_test.c ├── mkdir_test.c ├── nslookup_test.c ├── objectpool_test.cpp ├── ping_test.c ├── rbtree_test.c ├── rmdir_test.c ├── sendmail_test.c ├── sha1_test.c ├── sizeof_test.cpp ├── socketpair_test.c ├── synchronized_test.cpp ├── threadpool_test.cpp └── webbench.c └── util ├── README.md ├── base64.c ├── base64.h ├── md5.c ├── md5.h ├── sha1.c └── sha1.h /.bazelrc: -------------------------------------------------------------------------------- 1 | build --copt=-std=c99 2 | build --cxxopt=-std=c++11 3 | build --define BUILD_SHARED=ON 4 | build --define BUILD_STATIC=ON 5 | build --define BUILD_EXAMPLES=ON 6 | build --define BUILD_UNITTEST=OFF 7 | build --define WITH_PROTOCOL=OFF 8 | build --define WITH_EVPP=ON 9 | build --define WITH_HTTP=ON 10 | build --define WITH_HTTP_SERVER=ON 11 | build --define WITH_HTTP_CLIENT=ON 12 | build --define WITH_MQTT=OFF 13 | build --define ENABLE_UDS=OFF 14 | build --define USE_MULTIMAP=OFF 15 | build --define WITH_CURL=OFF 16 | build --define WITH_NGHTTP2=OFF 17 | build --define WITH_OPENSSL=OFF 18 | build --define WITH_GNUTLS=OFF 19 | build --define WITH_MBEDTLS=OFF 20 | build --define WITH_KCP=OFF 21 | build --define WITH_WEPOLL=ON 22 | build --define ENABLE_WINDUMP=OFF 23 | build --define BUILD_FOR_MT=OFF 24 | 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | cpputil/json.hpp linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/workflows/benchmark.yml: -------------------------------------------------------------------------------- 1 | name: benchmark 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.md' 7 | 8 | pull_request: 9 | paths-ignore: 10 | - '**.md' 11 | 12 | jobs: 13 | benchmark: 14 | name: benchmark 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: prepare 20 | run: | 21 | sudo apt update 22 | sudo apt install libssl-dev 23 | sudo apt install iperf 24 | sudo apt install nginx 25 | 26 | - name: make examples 27 | run: | 28 | ./configure 29 | make examples 30 | 31 | - name: build echo-servers 32 | run: | 33 | bash echo-servers/build.sh 34 | 35 | - name: benchmark echo-servers 36 | run: | 37 | bash echo-servers/benchmark.sh 38 | 39 | - name: benchmark tcp_proxy_server 40 | run: | 41 | iperf -s -p 5001 > /dev/null & 42 | bin/tcp_proxy_server 1212 127.0.0.1:5001 & 43 | iperf -c 127.0.0.1 -p 5001 -l 8K 44 | iperf -c 127.0.0.1 -p 1212 -l 8K 45 | 46 | - name: webbench 47 | run: | 48 | sudo nginx -c /etc/nginx/nginx.conf 49 | sudo cp html/index.html index.html 50 | sudo cp html/index.html /var/www/html/index.html 51 | bin/httpd -c etc/httpd.conf -d 52 | ps aux | grep nginx 53 | ps aux | grep httpd 54 | bin/tinyhttpd 9090 & 55 | echo -e "\n======Test nginx send file===============================" 56 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:80/ 57 | echo -e "\n======Test libhv/httpd send file (with FileCache)========" 58 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:8080/ 59 | echo -e "\n======Test libhv/tinyhttpd send file (no FileCache)======" 60 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:9090/ 61 | echo -e "\n======Test nginx 404 Not Found===========================" 62 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:80/404 63 | echo -e "\n======Test libhv/httpd 404 Not Found=====================" 64 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:8080/404 65 | echo -e "\n======Test libhv/tinyhttpd 404 Not found=================" 66 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:9090/404 67 | echo -e "\n======Test libhv/httpd /ping=============================" 68 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:8080/ping 69 | echo -e "\n======Test libhv/tinyhttpd /ping=========================" 70 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:9090/ping 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Bazel 2 | bazel-* 3 | MODULE.bazel 4 | MODULE.bazel.lock 5 | 6 | # Compiled Object files 7 | *.o 8 | *.lo 9 | *.slo 10 | *.obj 11 | 12 | # Precompiled Headers 13 | *.gch 14 | *.pch 15 | 16 | # Compiled Dynamic libraries 17 | *.so 18 | *.dylib 19 | *.dll 20 | 21 | # Compiled Static libraries 22 | *.a 23 | *.la 24 | *.lai 25 | *.lib 26 | 27 | # Executables 28 | *.exe 29 | *.out 30 | *.app 31 | 32 | # cache 33 | *~ 34 | *.bk 35 | *.bak 36 | *.old 37 | *.new 38 | 39 | # IDE 40 | .vs 41 | .vscode 42 | .DS_Store 43 | 44 | tags 45 | cscope* 46 | .ycm* 47 | 48 | # generated 49 | examples/protorpc/generated 50 | 51 | # output 52 | *.pid 53 | *.log 54 | *.db 55 | 56 | include 57 | lib 58 | bin 59 | tmp 60 | dist 61 | test 62 | *_test 63 | build 64 | config.mk 65 | hconfig.h 66 | html/uploads 67 | 68 | # msvc 69 | *.VC.* 70 | *.vcxproj.* 71 | Debug 72 | Release 73 | 74 | # cmake 75 | CMakeFiles 76 | CMakeCache.txt 77 | cmake_install.cmake 78 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | jobs: 4 | include: 5 | - os: linux 6 | dist: xenial 7 | compiler: gcc 8 | env: COVERALLS=no 9 | script: 10 | - ./configure 11 | - make libhv examples unittest evpp 12 | 13 | - os: osx 14 | compiler: clang 15 | env: COVERALLS=no 16 | script: 17 | - ./configure 18 | - make libhv examples unittest evpp 19 | 20 | - os: windows 21 | compiler: msvc 22 | env: COVERALLS=no 23 | script: 24 | - mkdir win64 25 | - cd win64 26 | - cmake .. -G "Visual Studio 15 2017 Win64" 27 | - cmake --build . 28 | 29 | before_script: 30 | - if [ "$COVERALLS" = "yes" ]; then 31 | pip install --user cpp-coveralls; 32 | export CC="$CC --coverage" CXX="$CXX --coverage"; 33 | fi 34 | 35 | after_success: 36 | - if [ "$COVERALLS" = "yes" ]; then 37 | scripts/test-coverage.sh; 38 | coveralls --gcov-options '\-lp' --include base --include event --include http; 39 | fi 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, ithewei 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | -------------------------------------------------------------------------------- /Makefile.vars: -------------------------------------------------------------------------------- 1 | MKDIR = -mkdir -p 2>/dev/null 2 | CP = -cp -r 2>/dev/null 3 | RM = -rm -r 2>/dev/null 4 | LDCONFIG = -ldconfig 2>/dev/null 5 | 6 | PREFIX ?= /usr/local 7 | INSTALL_INCDIR ?= $(PREFIX)/include/hv 8 | INSTALL_LIBDIR ?= $(PREFIX)/lib 9 | 10 | BASE_HEADERS = base/hplatform.h\ 11 | \ 12 | base/hdef.h\ 13 | base/hatomic.h\ 14 | base/herr.h\ 15 | base/htime.h\ 16 | base/hmath.h\ 17 | base/hbase.h\ 18 | base/hversion.h\ 19 | base/hsysinfo.h\ 20 | base/hproc.h\ 21 | base/hthread.h\ 22 | base/hmutex.h\ 23 | base/hsocket.h\ 24 | base/hlog.h\ 25 | base/hbuf.h\ 26 | base/hmain.h\ 27 | base/hendian.h\ 28 | 29 | SSL_HEADERS = ssl/hssl.h 30 | 31 | EVENT_HEADERS = event/hloop.h\ 32 | event/nlog.h\ 33 | 34 | UTIL_HEADERS = util/base64.h\ 35 | util/md5.h\ 36 | util/sha1.h\ 37 | 38 | CPPUTIL_HEADERS = cpputil/hmap.h\ 39 | cpputil/hstring.h\ 40 | cpputil/hfile.h\ 41 | cpputil/hpath.h\ 42 | cpputil/hdir.h\ 43 | cpputil/hurl.h\ 44 | cpputil/hscope.h\ 45 | cpputil/hthreadpool.h\ 46 | cpputil/hasync.h\ 47 | cpputil/hobjectpool.h\ 48 | cpputil/ifconfig.h\ 49 | cpputil/iniparser.h\ 50 | cpputil/json.hpp\ 51 | cpputil/singleton.h\ 52 | cpputil/ThreadLocalStorage.h\ 53 | 54 | EVPP_HEADERS = evpp/Buffer.h\ 55 | evpp/Channel.h\ 56 | evpp/Event.h\ 57 | evpp/EventLoop.h\ 58 | evpp/EventLoopThread.h\ 59 | evpp/EventLoopThreadPool.h\ 60 | evpp/Status.h\ 61 | evpp/TcpClient.h\ 62 | evpp/TcpServer.h\ 63 | evpp/UdpClient.h\ 64 | evpp/UdpServer.h\ 65 | 66 | PROTOCOL_HEADERS = protocol/icmp.h\ 67 | protocol/dns.h\ 68 | protocol/ftp.h\ 69 | protocol/smtp.h 70 | 71 | HTTP_HEADERS = http/httpdef.h\ 72 | http/wsdef.h\ 73 | http/http_content.h\ 74 | http/HttpMessage.h\ 75 | http/HttpParser.h\ 76 | http/WebSocketParser.h\ 77 | http/WebSocketChannel.h\ 78 | 79 | HTTP2_HEADERS = http/http2def.h\ 80 | http/grpcdef.h\ 81 | 82 | HTTP_CLIENT_HEADERS = http/client/HttpClient.h\ 83 | http/client/requests.h\ 84 | http/client/axios.h\ 85 | http/client/AsyncHttpClient.h\ 86 | http/client/WebSocketClient.h\ 87 | 88 | HTTP_SERVER_HEADERS = http/server/HttpServer.h\ 89 | http/server/HttpService.h\ 90 | http/server/HttpContext.h\ 91 | http/server/HttpResponseWriter.h\ 92 | http/server/WebSocketServer.h\ 93 | 94 | MQTT_HEADERS = mqtt/mqtt_protocol.h\ 95 | mqtt/mqtt_client.h\ 96 | -------------------------------------------------------------------------------- /TREE.md: -------------------------------------------------------------------------------- 1 | ## 目录结构 2 | 3 | ``` 4 | . 5 | ├── base libhv基础设施,如常用宏定义、数据结构、日期时间、线程、进程、日志、套接字 6 | ├── bin 可执行文件安装目录 7 | ├── build cmake默认构建目录 8 | ├── cert SSL证书存放目录 9 | ├── cmake cmake脚本存放目录 10 | ├── cpputil libhv工具类,如字符串、文件、路径、线程池、json解析、ini解析 11 | ├── docs 文档存放目录 12 | ├── echo-servers 包含libevent、libev、libuv、libhv、asio、poco、muduo等多个网络库的tcp echo server写法,并做压力测试 13 | ├── etc 应用程序配置目录 14 | ├── event libhv事件循环模块 15 | ├── evpp 事件循环c++封装类 16 | ├── examples 示例代码 17 | │ └── httpd 18 | ├── html 网页document_root目录 19 | │ ├── downloads 下载目录 20 | │ └── uploads 上传目录 21 | ├── http libhv http模块 22 | │ ├── client 23 | │ └── server 24 | ├── include 头文件安装目录 25 | │ └── hv 26 | ├── lib 库文件安装目录 27 | ├── logs 日志生成目录 28 | ├── misc 杂项 29 | ├── mqtt MQTT协议 30 | ├── protocol 包含icmp、dns、ftp、smtp等协议的实现 31 | ├── scripts shell脚本存放目录 32 | ├── ssl SSL/TLS加密通信 33 | ├── unittest 单元测试代码 34 | └── util libhv工具函数,如base64、md5、sha1 35 | 36 | ``` 37 | -------------------------------------------------------------------------------- /WORKSPACE.bazel: -------------------------------------------------------------------------------- 1 | workspace(name = "hv") 2 | -------------------------------------------------------------------------------- /base/README.md: -------------------------------------------------------------------------------- 1 | ## 目录结构 2 | 3 | ``` 4 | . 5 | ├── array.h 动态数组 6 | ├── hatomic.h 原子操作 7 | ├── hbase.h 基础函数 8 | ├── hbuf.h 缓存 9 | ├── hdef.h 常见宏定义 10 | ├── heap.h 堆 11 | ├── hendian.h 大小端 12 | ├── herr.h 错误码表 13 | ├── hlog.h 日志 14 | ├── hmain.h 命令行解析 15 | ├── hmath.h 数学函数 16 | ├── hmutex.h 线程同步锁 17 | ├── hplatform.h 平台相关宏 18 | ├── hproc.h 进程 19 | ├── hsocket.h 套接字 20 | ├── hsysinfo.h 系统信息 21 | ├── hthread.h 线程 22 | ├── htime.h 时间 23 | ├── hversion.h 版本 24 | ├── list.h 链表 25 | ├── queue.h 队列 26 | └── rbtree.h 红黑树 27 | 28 | ``` 29 | -------------------------------------------------------------------------------- /base/herr.c: -------------------------------------------------------------------------------- 1 | #include "herr.h" 2 | 3 | #include <string.h> // for strerror 4 | 5 | // errcode => errmsg 6 | const char* hv_strerror(int err) { 7 | if (err > 0 && err <= SYS_NERR) { 8 | return strerror(err); 9 | } 10 | 11 | switch (err) { 12 | #define F(errcode, name, errmsg) \ 13 | case errcode: return errmsg; 14 | FOREACH_ERR(F) 15 | #undef F 16 | default: 17 | return "Undefined error"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /base/hmath.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_MATH_H_ 2 | #define HV_MATH_H_ 3 | 4 | #include <math.h> 5 | 6 | static inline unsigned long floor2e(unsigned long num) { 7 | unsigned long n = num; 8 | int e = 0; 9 | while (n>>=1) ++e; 10 | unsigned long ret = 1; 11 | while (e--) ret<<=1; 12 | return ret; 13 | } 14 | 15 | static inline unsigned long ceil2e(unsigned long num) { 16 | // 2**0 = 1 17 | if (num == 0 || num == 1) return 1; 18 | unsigned long n = num - 1; 19 | int e = 1; 20 | while (n>>=1) ++e; 21 | unsigned long ret = 1; 22 | while (e--) ret<<=1; 23 | return ret; 24 | } 25 | 26 | // varint little-endian 27 | // MSB 28 | static inline int varint_encode(long long value, unsigned char* buf) { 29 | unsigned char ch; 30 | unsigned char *p = buf; 31 | int bytes = 0; 32 | do { 33 | ch = value & 0x7F; 34 | value >>= 7; 35 | *p++ = value == 0 ? ch : (ch | 0x80); 36 | ++bytes; 37 | } while (value); 38 | return bytes; 39 | } 40 | 41 | // @param[IN|OUT] len: in=>buflen, out=>varint bytesize 42 | static inline long long varint_decode(const unsigned char* buf, int* len) { 43 | long long ret = 0; 44 | int bytes = 0, bits = 0; 45 | const unsigned char *p = buf; 46 | do { 47 | if (len && *len && bytes == *len) { 48 | // Not enough length 49 | *len = 0; 50 | return 0; 51 | } 52 | ret |= ((long long)(*p & 0x7F)) << bits; 53 | ++bytes; 54 | if ((*p & 0x80) == 0) { 55 | // Found end 56 | if (len) *len = bytes; 57 | return ret; 58 | } 59 | ++p; 60 | bits += 7; 61 | } while(bytes < 10); 62 | 63 | // Not found end 64 | if (len) *len = -1; 65 | return ret; 66 | } 67 | 68 | #endif // HV_MATH_H_ 69 | -------------------------------------------------------------------------------- /base/hproc.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_PROC_H_ 2 | #define HV_PROC_H_ 3 | 4 | #include "hplatform.h" 5 | 6 | typedef struct proc_ctx_s { 7 | pid_t pid; // tid in Windows 8 | time_t start_time; 9 | int spawn_cnt; 10 | procedure_t init; 11 | void* init_userdata; 12 | procedure_t proc; 13 | void* proc_userdata; 14 | procedure_t exit; 15 | void* exit_userdata; 16 | } proc_ctx_t; 17 | 18 | static inline void hproc_run(proc_ctx_t* ctx) { 19 | if (ctx->init) { 20 | ctx->init(ctx->init_userdata); 21 | } 22 | if (ctx->proc) { 23 | ctx->proc(ctx->proc_userdata); 24 | } 25 | if (ctx->exit) { 26 | ctx->exit(ctx->exit_userdata); 27 | } 28 | } 29 | 30 | #ifdef OS_UNIX 31 | // unix use multi-processes 32 | static inline int hproc_spawn(proc_ctx_t* ctx) { 33 | ++ctx->spawn_cnt; 34 | ctx->start_time = time(NULL); 35 | pid_t pid = fork(); 36 | if (pid < 0) { 37 | perror("fork"); 38 | return -1; 39 | } else if (pid == 0) { 40 | // child process 41 | ctx->pid = getpid(); 42 | hproc_run(ctx); 43 | exit(0); 44 | } else if (pid > 0) { 45 | // parent process 46 | ctx->pid = pid; 47 | } 48 | return pid; 49 | } 50 | #elif defined(OS_WIN) 51 | // win32 use multi-threads 52 | static void win_thread(void* userdata) { 53 | proc_ctx_t* ctx = (proc_ctx_t*)userdata; 54 | ctx->pid = GetCurrentThreadId(); // tid in Windows 55 | hproc_run(ctx); 56 | } 57 | static inline int hproc_spawn(proc_ctx_t* ctx) { 58 | ++ctx->spawn_cnt; 59 | ctx->start_time = time(NULL); 60 | HANDLE h = (HANDLE)_beginthread(win_thread, 0, ctx); 61 | if (h == NULL) { 62 | return -1; 63 | } 64 | ctx->pid = GetThreadId(h); // tid in Windows 65 | return ctx->pid; 66 | } 67 | #endif 68 | 69 | #endif // HV_PROC_H_ 70 | -------------------------------------------------------------------------------- /base/hsysinfo.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_SYS_INFO_H_ 2 | #define HV_SYS_INFO_H_ 3 | 4 | #include "hplatform.h" 5 | 6 | #ifdef OS_LINUX 7 | #include <sys/sysinfo.h> 8 | #endif 9 | 10 | #ifdef OS_DARWIN 11 | #include <mach/mach_host.h> 12 | #include <sys/sysctl.h> 13 | #endif 14 | 15 | static inline int get_ncpu() { 16 | #ifdef OS_WIN 17 | SYSTEM_INFO si; 18 | GetSystemInfo(&si); 19 | return si.dwNumberOfProcessors; 20 | #else 21 | //return get_nprocs(); 22 | //return get_nprocs_conf(); 23 | //return sysconf(_SC_NPROCESSORS_ONLN); // processors available 24 | return sysconf(_SC_NPROCESSORS_CONF); // processors configured 25 | #endif 26 | } 27 | 28 | typedef struct meminfo_s { 29 | unsigned long total; // KB 30 | unsigned long free; // KB 31 | } meminfo_t; 32 | 33 | static inline int get_meminfo(meminfo_t* mem) { 34 | #ifdef OS_WIN 35 | MEMORYSTATUSEX memstat; 36 | memset(&memstat, 0, sizeof(memstat)); 37 | memstat.dwLength = sizeof(memstat); 38 | GlobalMemoryStatusEx(&memstat); 39 | mem->total = (unsigned long)(memstat.ullTotalPhys >> 10); 40 | mem->free = (unsigned long)(memstat.ullAvailPhys >> 10); 41 | return 0; 42 | #elif defined(OS_LINUX) 43 | struct sysinfo info; 44 | if (sysinfo(&info) < 0) { 45 | return errno; 46 | } 47 | mem->total = info.totalram * info.mem_unit >> 10; 48 | mem->free = info.freeram * info.mem_unit >> 10; 49 | return 0; 50 | #elif defined(OS_DARWIN) 51 | uint64_t memsize = 0; 52 | size_t size = sizeof(memsize); 53 | int which[2] = {CTL_HW, HW_MEMSIZE}; 54 | sysctl(which, 2, &memsize, &size, NULL, 0); 55 | mem->total = memsize >> 10; 56 | 57 | vm_statistics_data_t info; 58 | mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); 59 | host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count); 60 | mem->free = ((uint64_t)info.free_count * sysconf(_SC_PAGESIZE)) >> 10; 61 | return 0; 62 | #else 63 | (void)(mem); 64 | return -10; 65 | #endif 66 | } 67 | 68 | #endif // HV_SYS_INFO_H_ 69 | -------------------------------------------------------------------------------- /base/hversion.c: -------------------------------------------------------------------------------- 1 | #include "hversion.h" 2 | 3 | #include "htime.h" 4 | 5 | const char* hv_compile_version() { 6 | static char s_version[16] = {0}; 7 | datetime_t dt = hv_compile_datetime(); 8 | snprintf(s_version, sizeof(s_version), "%d.%d.%d.%d", 9 | HV_VERSION_MAJOR, dt.year%100, dt.month, dt.day); 10 | return s_version; 11 | } 12 | 13 | int version_atoi(const char* str) { 14 | int hex = 0; 15 | 16 | // trim v1.2.3.4 17 | const char* pv = strchr(str, 'v'); 18 | const char* pdot = pv ? pv+1 : str; 19 | 20 | while (1) { 21 | hex = (hex << 8) | atoi(pdot); 22 | pdot = strchr(pdot, '.'); 23 | if (pdot == NULL) break; 24 | ++pdot; 25 | } 26 | 27 | return hex; 28 | } 29 | 30 | void version_itoa(int num, char* str) { 31 | char* ch = (char*)# 32 | sprintf(str, "%d.%d.%d.%d", ch[3], ch[2], ch[1], ch[0]); 33 | 34 | // trim 0.1.2.3 35 | const char* p = str; 36 | while (1) { 37 | if (p[0] == '0' && p[1] == '.') { 38 | p += 2; 39 | } 40 | else { 41 | break; 42 | } 43 | } 44 | 45 | if (p != str) { 46 | strcpy(str, p); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /base/hversion.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_VERSION_H_ 2 | #define HV_VERSION_H_ 3 | 4 | #include "hexport.h" 5 | #include "hdef.h" 6 | 7 | BEGIN_EXTERN_C 8 | 9 | #define HV_VERSION_MAJOR 1 10 | #define HV_VERSION_MINOR 3 11 | #define HV_VERSION_PATCH 3 12 | 13 | #define HV_VERSION_STRING STRINGIFY(HV_VERSION_MAJOR) "." \ 14 | STRINGIFY(HV_VERSION_MINOR) "." \ 15 | STRINGIFY(HV_VERSION_PATCH) 16 | 17 | #define HV_VERSION_NUMBER ((HV_VERSION_MAJOR << 16) | (HV_VERSION_MINOR << 8) | HV_VERSION_PATCH) 18 | 19 | 20 | HV_INLINE const char* hv_version() { 21 | return HV_VERSION_STRING; 22 | } 23 | 24 | HV_EXPORT const char* hv_compile_version(); 25 | 26 | // 1.2.3.4 => 0x01020304 27 | HV_EXPORT int version_atoi(const char* str); 28 | 29 | // 0x01020304 => 1.2.3.4 30 | HV_EXPORT void version_itoa(int hex, char* str); 31 | 32 | END_EXTERN_C 33 | 34 | #endif // HV_VERSION_H_ 35 | -------------------------------------------------------------------------------- /cert/gen.sh: -------------------------------------------------------------------------------- 1 | openssl genrsa -out server.key 2048 2 | openssl req -new -x509 -key server.key -out server.crt -days 3650 3 | -------------------------------------------------------------------------------- /cert/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDgjCCAmqgAwIBAgIJAL/XKTs4cCwRMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV 3 | BAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTERMA8GA1UEBwwIU2hhbmdoYWkxITAf 4 | BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTA4MjkwNDA1NDda 5 | Fw0yOTA4MjYwNDA1NDdaMFYxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hh 6 | aTERMA8GA1UEBwwIU2hhbmdoYWkxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg 7 | UHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANJJ5DaZroEg 8 | N34H6A3uq5qDRlJe/ELhWcmtkj57cYb5U/T39C7dEjqGfhURb/NEuYbYoEb+4/lD 9 | xJOvn0qqXiJGPt0avecGhQUpggykEj4NZe6W1/WIX81Z1tF6QJZrpUDq4MD1S3KA 10 | ETvnTDv0bc6lvT/YDDb8qYMKGNZUTIQKpucdrIet9kDVIrXON4icJL9PDi/zFu3S 11 | e6feGsG7SAWv1ZInzF6wkEr8pCQc2Aab9R2BsbDsnVc8qZahfLniif7yDxiRbcEf 12 | P4P4cJ7YCD6T1Vyv7bDCnxSEJ+b+j4Ih5XVwHUeK2wG9rrNQ6Z4sSMrv1oSoxVfG 13 | mGdHHEhBN0ECAwEAAaNTMFEwHQYDVR0OBBYEFGPOMSeA6p3Vkf4uHdt5eRbrkoXU 14 | MB8GA1UdIwQYMBaAFGPOMSeA6p3Vkf4uHdt5eRbrkoXUMA8GA1UdEwEB/wQFMAMB 15 | Af8wDQYJKoZIhvcNAQELBQADggEBAEa3x7MzIBQvr43l6zuoK9SBoW6DET42yCRb 16 | YA6as/8fFYc4A1GqJStjvOgS0nIhBeCdyLJCy40U5ERhhVKqjPLLHHB4i7sv98Ac 17 | aXgVr91eUZcKeuiEkDHWRPCitkPj3p2v9J2VDAHpxfKvIB+r/q+7O4W6tjJT5fPq 18 | iUgdpr/R6uBFndBtGc+k65byxSQgvAvdM4kpGA8nnypT9i8SGVp0gB7lMG8avUTU 19 | zH16msecIEKLBHED40w9aiuOf3CnMDqGSTilP7p4gvMY4u+qm3zAXN29OHqoldbn 20 | j/A8RE9Q1F1H3G+eSltBuug3DBGp0q0EskxiqtfJbzW9G5fwGxM= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /cert/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA0knkNpmugSA3fgfoDe6rmoNGUl78QuFZya2SPntxhvlT9Pf0 3 | Lt0SOoZ+FRFv80S5htigRv7j+UPEk6+fSqpeIkY+3Rq95waFBSmCDKQSPg1l7pbX 4 | 9YhfzVnW0XpAlmulQOrgwPVLcoARO+dMO/RtzqW9P9gMNvypgwoY1lRMhAqm5x2s 5 | h632QNUitc43iJwkv08OL/MW7dJ7p94awbtIBa/VkifMXrCQSvykJBzYBpv1HYGx 6 | sOydVzyplqF8ueKJ/vIPGJFtwR8/g/hwntgIPpPVXK/tsMKfFIQn5v6PgiHldXAd 7 | R4rbAb2us1DpnixIyu/WhKjFV8aYZ0ccSEE3QQIDAQABAoIBAG/JthbMdbbRAI7v 8 | 9w1I/lKCTKTHN8T59PhAXAj5KG2/J0GHenhbLzCLhIUAowmoVBP6HqH/KAO/YcW8 9 | y6oujSIdQ5fYenFQxu/qk+bSZZw1FSXTbHRrDbqlcowjOCh+ivfKpLYO8A+rQv4c 10 | RCtvEdyTwNoqqLumbxppCLEPWSmrZOuw/EegeDMRspMpPNsFsk7mcl2w3jJjiX9D 11 | NB/yHZZzbZoTf/ayT/ca6FueKqPF52jIiuDzOncE1PqQ8hiPrXFIh4yZYCn1IpnK 12 | 48hotm3NSixpnag6UziwCRSoPsUCue8h2h3Tr690Lr/7LzILdtBJ+4Qxs3mU8Crz 13 | +bM7Xo0CgYEA9K94Gp0gyIxEiIPQ/zcOLeY2y3gUpS5LNaM6lxm82u4nsrC4FQJZ 14 | H72kV6YxvedmC1JZcYc6WiaAelkv9GqSekTMFv05rTsA4zQX59habsXPa5r3vzds 15 | NzgdjhDfwvWZCp3QXDG1n0wEMKJleG9+CK7khQ4doHmjQtCt0w16AxMCgYEA3AM9 16 | qxJSOADePq0G4fJ+4jAr4MgsMBsWaGEWU0Z7pBaTj+hm0ULiwShWy9/bbxw+zZgL 17 | VqE0cCiqKsMZMFtZvwX89fIN71YLkyTbl5Y/Lb65HKfKI8a8LGSNNaAd6CG9vy2C 18 | krqDKoHILCQdBpT7tSi2vF57+uDxJDH6+VyZ0tsCgYEAsVE7o2W87TihLaEA4wJ9 19 | 1wtfKCJUK8QZorwwaHGxZ6JwyFDChg8WkSb4IsCAiZNYYtoBkYEi61O9hWx+kQxu 20 | LAcRM5O8qWn54azNqikil+Xnw54g7cR3OqkC2gImdf1PM99bsIQhj1giLTBygk2h 21 | sx8y4a1yEOo1QuVBIpJAmlsCgYEAv3NehXAC9dLjkny0oYeIHEG43PizYwUfQaNC 22 | byLFUquGqtKcLfrbISR+KxjYdV6J1BQ7wZ2z6Omp8l4lnCvR8+U9E7QXpi4lEl0f 23 | bVCEF8WAhcwInYtBkgvJyWFUxPwfhq4OkqoUm7elvauLSn/4bNNJ+K7riguWK14G 24 | vFl1TcMCgYEA9EDTbpBl1oXICe2xevuh1HJQ1eBXAXP0l3hy0h76czBoBlnoF13A 25 | 6TOy6iSE3tpEwGPZZ33goKZdKyBLXG3TmemthWMxjBHc31C55gpIIgYPlW4Blv35 26 | djt8AlxF68VKIhY7CkaGhNmUpdSSfANHyirfw9rx70IRwRJfnZ5MN+M= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /cmake/utils.cmake: -------------------------------------------------------------------------------- 1 | include(CheckIncludeFiles) 2 | macro(check_header header) 3 | string(TOUPPER ${header} str1) 4 | string(REGEX REPLACE "[/.]" "_" str2 ${str1}) 5 | set(str3 HAVE_${str2}) 6 | check_include_files(${header} ${str3}) 7 | if (${str3}) 8 | set(${str3} 1) 9 | else() 10 | set(${str3} 0) 11 | endif() 12 | endmacro() 13 | 14 | include(CheckSymbolExists) 15 | macro(check_function function header) 16 | string(TOUPPER ${function} str1) 17 | set(str2 HAVE_${str1}) 18 | check_symbol_exists(${function} ${header} ${str2}) 19 | if (${str2}) 20 | set(${str2} 1) 21 | else() 22 | set(${str2} 0) 23 | endif() 24 | endmacro() 25 | 26 | macro(list_source_directories srcs) 27 | unset(tmp) 28 | foreach(dir ${ARGN}) 29 | aux_source_directory(${dir} tmp) 30 | endforeach() 31 | set(${srcs} ${tmp}) 32 | list(FILTER ${srcs} EXCLUDE REGEX ".*_test\\.c") 33 | endmacro() 34 | 35 | macro(glob_headers_and_sources files) 36 | unset(tmp) 37 | foreach(dir ${ARGN}) 38 | file(GLOB tmp ${dir}/*.h ${dir}/*.c ${dir}/*.hpp ${dir}/*.cpp) 39 | list(APPEND ${files} ${tmp}) 40 | endforeach() 41 | list(FILTER ${files} EXCLUDE REGEX ".*_test\\.c") 42 | endmacro() 43 | -------------------------------------------------------------------------------- /cmake/vars.cmake: -------------------------------------------------------------------------------- 1 | # see Makefile.vars 2 | 3 | set(BASE_HEADERS 4 | base/hplatform.h 5 | base/hdef.h 6 | base/hatomic.h 7 | base/herr.h 8 | base/htime.h 9 | base/hmath.h 10 | base/hbase.h 11 | base/hversion.h 12 | base/hsysinfo.h 13 | base/hproc.h 14 | base/hthread.h 15 | base/hmutex.h 16 | base/hsocket.h 17 | base/hlog.h 18 | base/hbuf.h 19 | base/hmain.h 20 | base/hendian.h 21 | ) 22 | 23 | set(SSL_HEADERS 24 | ssl/hssl.h 25 | ) 26 | 27 | set(EVENT_HEADERS 28 | event/hloop.h 29 | event/nlog.h 30 | ) 31 | 32 | set(UTIL_HEADERS 33 | util/base64.h 34 | util/md5.h 35 | util/sha1.h 36 | ) 37 | 38 | set(CPPUTIL_HEADERS 39 | cpputil/hmap.h 40 | cpputil/hstring.h 41 | cpputil/hfile.h 42 | cpputil/hpath.h 43 | cpputil/hdir.h 44 | cpputil/hurl.h 45 | cpputil/hscope.h 46 | cpputil/hthreadpool.h 47 | cpputil/hasync.h 48 | cpputil/hobjectpool.h 49 | cpputil/ifconfig.h 50 | cpputil/iniparser.h 51 | cpputil/json.hpp 52 | cpputil/singleton.h 53 | cpputil/ThreadLocalStorage.h 54 | ) 55 | 56 | set(EVPP_HEADERS 57 | evpp/Buffer.h 58 | evpp/Channel.h 59 | evpp/Event.h 60 | evpp/EventLoop.h 61 | evpp/EventLoopThread.h 62 | evpp/EventLoopThreadPool.h 63 | evpp/Status.h 64 | evpp/TcpClient.h 65 | evpp/TcpServer.h 66 | evpp/UdpClient.h 67 | evpp/UdpServer.h 68 | ) 69 | 70 | set(PROTOCOL_HEADERS 71 | protocol/icmp.h 72 | protocol/dns.h 73 | protocol/ftp.h 74 | protocol/smtp.h 75 | ) 76 | 77 | set(HTTP_HEADERS 78 | http/httpdef.h 79 | http/wsdef.h 80 | http/http_content.h 81 | http/HttpMessage.h 82 | http/HttpParser.h 83 | http/WebSocketParser.h 84 | http/WebSocketChannel.h 85 | ) 86 | 87 | set(HTTP2_HEADERS 88 | http/http2def.h 89 | http/grpcdef.h 90 | ) 91 | 92 | set(HTTP_CLIENT_HEADERS 93 | http/client/HttpClient.h 94 | http/client/requests.h 95 | http/client/axios.h 96 | http/client/AsyncHttpClient.h 97 | http/client/WebSocketClient.h) 98 | 99 | set(HTTP_SERVER_HEADERS 100 | http/server/HttpServer.h 101 | http/server/HttpService.h 102 | http/server/HttpContext.h 103 | http/server/HttpResponseWriter.h 104 | http/server/WebSocketServer.h 105 | ) 106 | 107 | set(MQTT_HEADERS 108 | mqtt/mqtt_protocol.h 109 | mqtt/mqtt_client.h 110 | ) 111 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | # Don't modify this file, you should modify config.mk or 2 | # run ./configure --with-MODULE --enable-FEATURE 3 | 4 | PREFIX=/usr/local 5 | INSTALL_INCDIR=$(PREFIX)/include/hv 6 | INSTALL_LIBDIR=$(PREFIX)/lib 7 | 8 | # modules 9 | # include icmp dns ftp smtp 10 | WITH_PROTOCOL=no 11 | 12 | WITH_EVPP=yes 13 | WITH_HTTP=yes 14 | WITH_HTTP_SERVER=yes 15 | WITH_HTTP_CLIENT=yes 16 | WITH_MQTT=no 17 | 18 | # features 19 | # base/hsocket.h: Unix Domain Socket 20 | ENABLE_UDS=no 21 | # base/RAII.cpp: Windows MiniDumpWriteDump 22 | ENABLE_WINDUMP=no 23 | # http/http_content.h: KeyValue,QueryParams,MultiPart 24 | USE_MULTIMAP=no 25 | 26 | # dependencies 27 | # for http/client 28 | WITH_CURL=no 29 | # for http2 30 | WITH_NGHTTP2=no 31 | # for SSL/TLS 32 | WITH_OPENSSL=no 33 | WITH_GNUTLS=no 34 | WITH_MBEDTLS=no 35 | 36 | # rudp 37 | WITH_KCP=no 38 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | 2 | PREFIX=/usr/local 3 | INSTALL_INCDIR=$(PREFIX)/include/hv 4 | INSTALL_LIBDIR=$(PREFIX)/lib 5 | WITH_PROTOCOL=no 6 | WITH_EVPP=yes 7 | WITH_HTTP=yes 8 | WITH_HTTP_SERVER=yes 9 | WITH_HTTP_CLIENT=yes 10 | WITH_MQTT=no 11 | ENABLE_UDS=no 12 | ENABLE_WINDUMP=no 13 | USE_MULTIMAP=no 14 | WITH_CURL=no 15 | WITH_NGHTTP2=no 16 | WITH_OPENSSL=no 17 | WITH_GNUTLS=no 18 | WITH_MBEDTLS=no 19 | WITH_KCP=no 20 | CONFIG_DATE=20220224 21 | -------------------------------------------------------------------------------- /cpputil/RAII.cpp: -------------------------------------------------------------------------------- 1 | #include "hplatform.h" 2 | 3 | #ifdef OS_WIN 4 | #ifdef ENABLE_WINDUMP 5 | #include <dbghelp.h> 6 | #ifdef _MSC_VER 7 | #pragma comment(lib,"dbghelp.lib") 8 | #endif 9 | static LONG UnhandledException(EXCEPTION_POINTERS *pException) { 10 | char modulefile[256]; 11 | GetModuleFileName(NULL, modulefile, sizeof(modulefile)); 12 | char* pos = strrchr(modulefile, '\\'); 13 | char* modulefilename = pos + 1; 14 | SYSTEMTIME st; 15 | GetLocalTime(&st); 16 | char filename[256]; 17 | snprintf(filename, sizeof(filename), "core_%s_%04d%02d%02d_%02d%02d%02d_%03d.dump", 18 | modulefilename, 19 | st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); 20 | HANDLE hDumpFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 21 | MINIDUMP_EXCEPTION_INFORMATION dumpInfo; 22 | dumpInfo.ExceptionPointers = pException; 23 | dumpInfo.ThreadId = GetCurrentThreadId(); 24 | dumpInfo.ClientPointers = TRUE; 25 | MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL); 26 | CloseHandle(hDumpFile); 27 | return EXCEPTION_EXECUTE_HANDLER; 28 | } 29 | #endif 30 | 31 | #include "hsocket.h" 32 | class WsaRAII { 33 | public: 34 | WsaRAII() { 35 | WSAInit(); 36 | #ifdef ENABLE_WINDUMP 37 | SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)UnhandledException); 38 | #endif 39 | } 40 | ~WsaRAII() { 41 | WSADeinit(); 42 | } 43 | }; 44 | static WsaRAII s_wsa; 45 | #endif 46 | 47 | #ifdef WITH_CURL 48 | #include "curl/curl.h" 49 | #ifdef _MSC_VER 50 | //#pragma comment(lib, "libcurl.a") 51 | #pragma comment(lib, "ws2_32.lib") 52 | #pragma comment(lib, "wldap32.lib") 53 | #pragma comment(lib, "advapi32.lib") 54 | #pragma comment(lib, "crypt32.lib") 55 | #endif 56 | class CurlRAII { 57 | public: 58 | CurlRAII() { 59 | curl_global_init(CURL_GLOBAL_ALL); 60 | } 61 | ~CurlRAII() { 62 | curl_global_cleanup(); 63 | } 64 | }; 65 | static CurlRAII s_curl; 66 | #endif 67 | -------------------------------------------------------------------------------- /cpputil/README.md: -------------------------------------------------------------------------------- 1 | ## 目录结构 2 | 3 | ``` 4 | . 5 | ├── hasync.h hv::async实现 6 | ├── hdir.h 目录(ls实现) 7 | ├── hfile.h 文件类 8 | ├── hobjectpool.h 对象池 9 | ├── hpath.h 路径操作 10 | ├── hscope.h 作用域模板类 11 | ├── hstring.h 字符串操作 12 | ├── hthreadpool.h 线程池 13 | ├── hurl.h URL操作 14 | ├── ifconfig.h 网络配置(ifconfig实现) 15 | ├── iniparser.h INI解析 16 | ├── json.hpp JSON解析 17 | ├── singleton.h 单例模式宏 18 | └── ThreadLocalStorage.h 线程本地存储类 19 | 20 | ``` 21 | -------------------------------------------------------------------------------- /cpputil/ThreadLocalStorage.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadLocalStorage.h" 2 | 3 | #include "hthread.h" 4 | 5 | namespace hv { 6 | 7 | ThreadLocalStorage ThreadLocalStorage::tls[ThreadLocalStorage::MAX_NUM]; 8 | 9 | void ThreadLocalStorage::set(int idx, void* val) { 10 | return tls[idx].set(val); 11 | } 12 | 13 | void* ThreadLocalStorage::get(int idx) { 14 | return tls[idx].get(); 15 | } 16 | 17 | void ThreadLocalStorage::setThreadName(const char* name) { 18 | set(THREAD_NAME, (void*)name); 19 | } 20 | 21 | const char* ThreadLocalStorage::threadName() { 22 | void* value = get(THREAD_NAME); 23 | if (value) { 24 | return (char*)value; 25 | } 26 | 27 | static char unnamed[32] = {0}; 28 | snprintf(unnamed, sizeof(unnamed)-1, "thread-%ld", hv_gettid()); 29 | return unnamed; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /cpputil/ThreadLocalStorage.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_THREAD_LOCAL_STORAGE_H_ 2 | #define HV_THREAD_LOCAL_STORAGE_H_ 3 | 4 | #include "hexport.h" 5 | #include "hplatform.h" 6 | 7 | #ifdef OS_WIN 8 | 9 | #define hthread_key_t DWORD 10 | #define INVALID_HTHREAD_KEY 0xFFFFFFFF 11 | #define hthread_key_create(pkey) *pkey = TlsAlloc() 12 | #define hthread_key_delete TlsFree 13 | #define hthread_get_value TlsGetValue 14 | #define hthread_set_value TlsSetValue 15 | 16 | #else 17 | 18 | #define hthread_key_t pthread_key_t 19 | #define INVALID_HTHREAD_KEY 0xFFFFFFFF 20 | #define hthread_key_create(pkey) pthread_key_create(pkey, NULL) 21 | #define hthread_key_delete pthread_key_delete 22 | #define hthread_get_value pthread_getspecific 23 | #define hthread_set_value pthread_setspecific 24 | 25 | #endif 26 | 27 | #ifdef __cplusplus 28 | namespace hv { 29 | 30 | class HV_EXPORT ThreadLocalStorage { 31 | public: 32 | enum { 33 | THREAD_NAME = 0, 34 | EVENT_LOOP = 1, 35 | MAX_NUM = 16, 36 | }; 37 | ThreadLocalStorage() { 38 | hthread_key_create(&key); 39 | } 40 | 41 | ~ThreadLocalStorage() { 42 | hthread_key_delete(key); 43 | } 44 | 45 | void set(void* val) { 46 | hthread_set_value(key, val); 47 | } 48 | 49 | void* get() { 50 | return hthread_get_value(key); 51 | } 52 | 53 | static void set(int idx, void* val); 54 | static void* get(int idx); 55 | 56 | static void setThreadName(const char* name); 57 | static const char* threadName(); 58 | 59 | private: 60 | hthread_key_t key; 61 | static ThreadLocalStorage tls[MAX_NUM]; 62 | }; 63 | 64 | } 65 | #endif 66 | 67 | #endif // HV_THREAD_LOCAL_STORAGE_H_ 68 | -------------------------------------------------------------------------------- /cpputil/hasync.cpp: -------------------------------------------------------------------------------- 1 | #include "hasync.h" 2 | 3 | namespace hv { 4 | 5 | SINGLETON_IMPL(GlobalThreadPool) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /cpputil/hasync.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_ASYNC_H_ 2 | #define HV_ASYNC_H_ 3 | 4 | #include "hexport.h" 5 | #include "hthreadpool.h" 6 | #include "singleton.h" 7 | 8 | namespace hv { 9 | 10 | class HV_EXPORT GlobalThreadPool : public HThreadPool { 11 | SINGLETON_DECL(GlobalThreadPool) 12 | protected: 13 | GlobalThreadPool() : HThreadPool() {} 14 | ~GlobalThreadPool() {} 15 | }; 16 | 17 | /* 18 | * return a future, calling future.get() will wait task done and return RetType. 19 | * async(fn, args...) 20 | * async(std::bind(&Class::mem_fn, &obj)) 21 | * async(std::mem_fn(&Class::mem_fn, &obj)) 22 | * 23 | */ 24 | template<class Fn, class... Args> 25 | auto async(Fn&& fn, Args&&... args) -> std::future<decltype(fn(args...))> { 26 | return GlobalThreadPool::instance()->commit(std::forward<Fn>(fn), std::forward<Args>(args)...); 27 | } 28 | 29 | class async { 30 | public: 31 | static void startup(int min_threads = DEFAULT_THREAD_POOL_MIN_THREAD_NUM, 32 | int max_threads = DEFAULT_THREAD_POOL_MAX_THREAD_NUM, 33 | int max_idle_ms = DEFAULT_THREAD_POOL_MAX_IDLE_TIME) { 34 | GlobalThreadPool* gtp = GlobalThreadPool::instance(); 35 | if (gtp->isStarted()) return; 36 | gtp->setMinThreadNum(min_threads); 37 | gtp->setMaxThreadNum(max_threads); 38 | gtp->setMaxIdleTime(max_idle_ms); 39 | gtp->start(); 40 | } 41 | 42 | static void cleanup() { 43 | GlobalThreadPool::exitInstance(); 44 | } 45 | }; 46 | 47 | } // end namespace hv 48 | 49 | #endif // HV_ASYNC_H_ 50 | -------------------------------------------------------------------------------- /cpputil/hdir.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_DIR_H_ 2 | #define HV_DIR_H_ 3 | 4 | /* 5 | *@code 6 | int main(int argc, char* argv[]) { 7 | const char* dir = "."; 8 | if (argc > 1) { 9 | dir = argv[1]; 10 | } 11 | std::list<hdir_t> dirs; 12 | listdir(dir, dirs); 13 | for (auto& item : dirs) { 14 | printf("%c%c%c%c%c%c%c%c%c%c\t", 15 | item.type, 16 | item.mode & 0400 ? 'r' : '-', 17 | item.mode & 0200 ? 'w' : '-', 18 | item.mode & 0100 ? 'x' : '-', 19 | item.mode & 0040 ? 'r' : '-', 20 | item.mode & 0020 ? 'w' : '-', 21 | item.mode & 0010 ? 'x' : '-', 22 | item.mode & 0004 ? 'r' : '-', 23 | item.mode & 0002 ? 'w' : '-', 24 | item.mode & 0001 ? 'x' : '-'); 25 | float hsize; 26 | if (item.size < 1024) { 27 | printf("%lu\t", item.size); 28 | } 29 | else if ((hsize = item.size/1024.0f) < 1024.0f) { 30 | printf("%.1fK\t", hsize); 31 | } 32 | else if ((hsize /= 1024.0f) < 1024.0f) { 33 | printf("%.1fM\t", hsize); 34 | } 35 | else { 36 | hsize /= 1024.0f; 37 | printf("%.1fG\t", hsize); 38 | } 39 | struct tm* tm = localtime(&item.mtime); 40 | printf("%04d-%02d-%02d %02d:%02d:%02d\t", 41 | tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); 42 | printf("%s%s\n", item.name, item.type == 'd' ? "/" : ""); 43 | } 44 | return 0; 45 | } 46 | */ 47 | 48 | #include <string.h> 49 | #include <time.h> 50 | 51 | #include <list> 52 | 53 | #include "hexport.h" 54 | 55 | typedef struct hdir_s { 56 | char name[256]; 57 | char type; // f:file d:dir l:link b:block c:char s:socket p:pipe 58 | char reserverd; 59 | unsigned short mode; 60 | size_t size; 61 | time_t atime; 62 | time_t mtime; 63 | time_t ctime; 64 | } hdir_t; 65 | 66 | // listdir: same as ls on unix, dir on win 67 | HV_EXPORT int listdir(const char* dir, std::list<hdir_t>& dirs); 68 | 69 | #endif // HV_DIR_H_ 70 | -------------------------------------------------------------------------------- /cpputil/hmap.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_MAP_H_ 2 | #define HV_MAP_H_ 3 | 4 | #include "hconfig.h" 5 | 6 | #include <map> 7 | #include <string> 8 | 9 | // MultiMap 10 | namespace std { 11 | /* 12 | int main() { 13 | std::MultiMap<std::string, std::string> kvs; 14 | kvs["name"] = "hw"; 15 | kvs["filename"] = "1.jpg"; 16 | kvs["filename"] = "2.jpg"; 17 | //kvs.insert(std::pair<std::string,std::string>("name", "hw")); 18 | //kvs.insert(std::pair<std::string,std::string>("filename", "1.jpg")); 19 | //kvs.insert(std::pair<std::string,std::string>("filename", "2.jpg")); 20 | for (auto& pair : kvs) { 21 | printf("%s:%s\n", pair.first.c_str(), pair.second.c_str()); 22 | } 23 | auto iter = kvs.find("filename"); 24 | if (iter != kvs.end()) { 25 | for (int i = 0; i < kvs.count("filename"); ++i, ++iter) { 26 | printf("%s:%s\n", iter->first.c_str(), iter->second.c_str()); 27 | } 28 | } 29 | return 0; 30 | } 31 | */ 32 | template<typename Key,typename Value> 33 | class MultiMap : public multimap<Key, Value> { 34 | public: 35 | Value& operator[](Key key) { 36 | auto iter = this->insert(std::pair<Key,Value>(key,Value())); 37 | return (*iter).second; 38 | } 39 | }; 40 | } 41 | 42 | #ifdef USE_MULTIMAP 43 | #define HV_MAP std::MultiMap 44 | #else 45 | #define HV_MAP std::map 46 | #endif 47 | 48 | // KeyValue 49 | namespace hv { 50 | typedef std::map<std::string, std::string> keyval_t; 51 | typedef std::MultiMap<std::string, std::string> multi_keyval_t; 52 | typedef HV_MAP<std::string, std::string> KeyValue; 53 | } 54 | 55 | #endif // HV_MAP_H_ 56 | -------------------------------------------------------------------------------- /cpputil/hpath.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_PATH_H_ 2 | #define HV_PATH_H_ 3 | 4 | #include <string> // for std::string 5 | 6 | #include "hexport.h" 7 | 8 | class HV_EXPORT HPath { 9 | public: 10 | static bool exists(const char* path); 11 | static bool isdir(const char* path); 12 | static bool isfile(const char* path); 13 | static bool islink(const char* path); 14 | 15 | // filepath = /mnt/share/image/test.jpg 16 | // basename = test.jpg 17 | // dirname = /mnt/share/image 18 | // filename = test 19 | // suffixname = jpg 20 | static std::string basename(const std::string& filepath); 21 | static std::string dirname(const std::string& filepath); 22 | static std::string filename(const std::string& filepath); 23 | static std::string suffixname(const std::string& filepath); 24 | 25 | static std::string join(const std::string& dir, const std::string& filename); 26 | }; 27 | 28 | #endif // HV_PATH_H_ 29 | -------------------------------------------------------------------------------- /cpputil/hscope.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_SCOPE_H_ 2 | #define HV_SCOPE_H_ 3 | 4 | #include <functional> 5 | typedef std::function<void()> Function; 6 | 7 | #include "hdef.h" 8 | 9 | // same as golang defer 10 | class Defer { 11 | public: 12 | Defer(Function&& fn) : _fn(std::move(fn)) {} 13 | ~Defer() { if(_fn) _fn();} 14 | private: 15 | Function _fn; 16 | }; 17 | #define defer(code) Defer STRINGCAT(_defer_, __LINE__)([&](){code}); 18 | 19 | class ScopeCleanup { 20 | public: 21 | template<typename Fn, typename... Args> 22 | ScopeCleanup(Fn&& fn, Args&&... args) { 23 | _cleanup = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...); 24 | } 25 | 26 | ~ScopeCleanup() { 27 | _cleanup(); 28 | } 29 | 30 | private: 31 | Function _cleanup; 32 | }; 33 | 34 | template<typename T> 35 | class ScopeFree { 36 | public: 37 | ScopeFree(T* p) : _p(p) {} 38 | ~ScopeFree() {SAFE_FREE(_p);} 39 | private: 40 | T* _p; 41 | }; 42 | 43 | template<typename T> 44 | class ScopeDelete { 45 | public: 46 | ScopeDelete(T* p) : _p(p) {} 47 | ~ScopeDelete() {SAFE_DELETE(_p);} 48 | private: 49 | T* _p; 50 | }; 51 | 52 | template<typename T> 53 | class ScopeDeleteArray { 54 | public: 55 | ScopeDeleteArray(T* p) : _p(p) {} 56 | ~ScopeDeleteArray() {SAFE_DELETE_ARRAY(_p);} 57 | private: 58 | T* _p; 59 | }; 60 | 61 | template<typename T> 62 | class ScopeRelease { 63 | public: 64 | ScopeRelease(T* p) : _p(p) {} 65 | ~ScopeRelease() {SAFE_RELEASE(_p);} 66 | private: 67 | T* _p; 68 | }; 69 | 70 | template<typename T> 71 | class ScopeLock { 72 | public: 73 | ScopeLock(T& mutex) : _mutex(mutex) {_mutex.lock();} 74 | ~ScopeLock() {_mutex.unlock();} 75 | private: 76 | T& _mutex; 77 | }; 78 | 79 | #endif // HV_SCOPE_H_ 80 | -------------------------------------------------------------------------------- /cpputil/hurl.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_URL_H_ 2 | #define HV_URL_H_ 3 | 4 | #include <string> // import std::string 5 | 6 | #include "hexport.h" 7 | 8 | class HV_EXPORT HUrl { 9 | public: 10 | static std::string escape(const std::string& str, const char* unescaped_chars = ""); 11 | static std::string unescape(const std::string& str); 12 | 13 | HUrl() : port(0) {} 14 | ~HUrl() {} 15 | 16 | void reset(); 17 | bool parse(const std::string& url); 18 | const std::string& dump(); 19 | 20 | std::string url; 21 | std::string scheme; 22 | std::string username; 23 | std::string password; 24 | std::string host; 25 | int port; 26 | std::string path; 27 | std::string query; 28 | std::string fragment; 29 | }; 30 | 31 | namespace hv { 32 | 33 | HV_INLINE std::string escapeURL(const std::string& url) { 34 | return HUrl::escape(url, ":/@[]?=&#+"); 35 | } 36 | 37 | HV_EXPORT std::string escapeHTML(const std::string& str); 38 | 39 | } // end namespace hv 40 | 41 | #endif // HV_URL_H_ 42 | -------------------------------------------------------------------------------- /cpputil/ifconfig.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_IFCONFIG_H_ 2 | #define HV_IFCONFIG_H_ 3 | 4 | #include <vector> 5 | 6 | #include "hexport.h" 7 | 8 | #ifdef _MSC_VER 9 | #pragma comment(lib, "iphlpapi.lib") 10 | #pragma comment(lib, "ws2_32.lib") 11 | #endif 12 | 13 | typedef struct ifconfig_s { 14 | char name[128]; 15 | char ip[16]; 16 | char mask[16]; 17 | char broadcast[16]; 18 | char mac[20]; 19 | } ifconfig_t; 20 | 21 | /* 22 | * @test 23 | std::vector<ifconfig_t> ifcs; 24 | ifconfig(ifcs); 25 | for (auto& item : ifcs) { 26 | printf("%s\nip: %s\nmask: %s\nbroadcast: %s\nmac: %s\n\n", 27 | item.name, 28 | item.ip, 29 | item.mask, 30 | item.broadcast, 31 | item.mac); 32 | } 33 | */ 34 | HV_EXPORT int ifconfig(std::vector<ifconfig_t>& ifcs); 35 | 36 | #endif // HV_IFCONFIG_H_ 37 | -------------------------------------------------------------------------------- /cpputil/iniparser.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_INI_PARSER_H_ 2 | #define HV_INI_PARSER_H_ 3 | 4 | #include <string> 5 | #include <list> 6 | 7 | #include "hexport.h" 8 | 9 | #define DEFAULT_INI_COMMENT "#" 10 | #define DEFAULT_INI_DELIM "=" 11 | 12 | // fwd 13 | class IniNode; 14 | 15 | class HV_EXPORT IniParser { 16 | public: 17 | IniParser(); 18 | ~IniParser(); 19 | 20 | int LoadFromFile(const char* filepath); 21 | int LoadFromMem(const char* data); 22 | int Unload(); 23 | int Reload(); 24 | 25 | std::string DumpString(); 26 | int Save(); 27 | int SaveAs(const char* filepath); 28 | 29 | std::list<std::string> GetSections(); 30 | std::list<std::string> GetKeys(const std::string& section = ""); 31 | std::string GetValue(const std::string& key, const std::string& section = ""); 32 | void SetValue(const std::string& key, const std::string& value, const std::string& section = ""); 33 | 34 | // T = [bool, int, float] 35 | template<typename T> 36 | T Get(const std::string& key, const std::string& section = "", T defvalue = 0); 37 | 38 | // T = [bool, int, float] 39 | template<typename T> 40 | void Set(const std::string& key, const T& value, const std::string& section = ""); 41 | 42 | protected: 43 | void DumpString(IniNode* pNode, std::string& str); 44 | 45 | public: 46 | std::string _comment; 47 | std::string _delim; 48 | std::string _filepath; 49 | private: 50 | IniNode* root_; 51 | }; 52 | 53 | #endif // HV_INI_PARSER_H_ 54 | -------------------------------------------------------------------------------- /cpputil/singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_SINGLETON_H_ 2 | #define HV_SINGLETON_H_ 3 | 4 | #include <mutex> 5 | 6 | #define DISABLE_COPY(Class) \ 7 | Class(const Class&) = delete; \ 8 | Class& operator=(const Class&) = delete; 9 | 10 | #define SINGLETON_DECL(Class) \ 11 | public: \ 12 | static Class* instance(); \ 13 | static void exitInstance(); \ 14 | private: \ 15 | DISABLE_COPY(Class) \ 16 | static Class* s_pInstance; \ 17 | static std::once_flag s_initFlag; \ 18 | static std::mutex s_mutex; 19 | 20 | #define SINGLETON_IMPL(Class) \ 21 | Class* Class::s_pInstance = NULL; \ 22 | std::once_flag Class::s_initFlag; \ 23 | std::mutex Class::s_mutex; \ 24 | Class* Class::instance() { \ 25 | std::call_once(s_initFlag, []() {s_pInstance = new Class;}); \ 26 | return s_pInstance; \ 27 | } \ 28 | void Class::exitInstance() { \ 29 | std::lock_guard<std::mutex> lock(s_mutex); \ 30 | if (s_pInstance) { \ 31 | delete s_pInstance; \ 32 | s_pInstance = nullptr; \ 33 | } \ 34 | } 35 | 36 | #endif // HV_SINGLETON_H_ 37 | -------------------------------------------------------------------------------- /docs/PLAN.md: -------------------------------------------------------------------------------- 1 | ## Done 2 | 3 | - base: cross platfrom infrastructure 4 | - event: select/poll/epoll/wepoll/kqueue/port 5 | - ssl: openssl/gnutls/mbedtls/wintls/appletls 6 | - rudp: KCP 7 | - evpp: c++ EventLoop interface similar to muduo and evpp 8 | - http client/server: include https http1/x http2 9 | - websocket client/server 10 | - mqtt client 11 | 12 | ## Improving 13 | 14 | - Path router: optimized matching via trie? 15 | - FileCache use LRUCache 16 | 17 | ## Plan 18 | 19 | - redis client 20 | - async DNS 21 | - lua binding 22 | - js binding 23 | - hrpc = libhv + protobuf 24 | - rudp: FEC, ARQ, UDT, QUIC 25 | - kcptun 26 | - have a taste of io_uring 27 | - coroutine 28 | - cppsocket.io 29 | - IM-libhv 30 | - MediaServer-libhv 31 | - GameServer-libhv 32 | -------------------------------------------------------------------------------- /docs/cn/README.md: -------------------------------------------------------------------------------- 1 | ## c接口 2 | 3 | - [hloop: 事件循环](hloop.md) 4 | - [hbase: 基础函数](hbase.md) 5 | - [hlog: 日志](hlog.md) 6 | 7 | ## c++接口 8 | 9 | - [class EventLoop: 事件循环类](EventLoop.md) 10 | - [class Channel: 通道类](Channel.md) 11 | - [class TcpServer: TCP服务端类](TcpServer.md) 12 | - [class TcpClient: TCP客户端类](TcpClient.md) 13 | - [class UdpServer: UDP服务端类](UdpServer.md) 14 | - [class UdpClient: UDP客户端类](UdpClient.md) 15 | - [class HttpServer: HTTP服务端类](HttpServer.md) 16 | - [class HttpClient: HTTP客户端类](HttpClient.md) 17 | - [class WebSocketServer: WebSocket服务端类](WebSocketServer.md) 18 | - [class WebSocketClient: WebSocket客户端类](WebSocketClient.md) 19 | -------------------------------------------------------------------------------- /docs/cn/TcpClient.md: -------------------------------------------------------------------------------- 1 | TCP 客户端类 2 | 3 | ```c++ 4 | 5 | class TcpClient { 6 | 7 | // 返回所在的事件循环 8 | const EventLoopPtr& loop(); 9 | 10 | // 创建套接字 11 | int createsocket(int remote_port, const char* remote_host = "127.0.0.1"); 12 | int createsocket(struct sockaddr* remote_addr); 13 | 14 | // 绑定端口 15 | int bind(int local_port, const char* local_host = "0.0.0.0"); 16 | int bind(struct sockaddr* local_addr); 17 | 18 | // 关闭套接字 19 | void closesocket(); 20 | 21 | // 开始运行 22 | void start(bool wait_threads_started = true); 23 | 24 | // 停止运行 25 | void stop(bool wait_threads_stopped = true); 26 | 27 | // 是否已连接 28 | bool isConnected(); 29 | 30 | // 发送 31 | int send(const void* data, int size); 32 | int send(Buffer* buf); 33 | int send(const std::string& str); 34 | 35 | // 设置SSL/TLS加密通信 36 | int withTLS(hssl_ctx_opt_t* opt = NULL); 37 | 38 | // 设置连接超时 39 | void setConnectTimeout(int ms); 40 | 41 | // 设置重连 42 | void setReconnect(reconn_setting_t* setting); 43 | 44 | // 是否是重连 45 | bool isReconnect(); 46 | 47 | // 设置拆包规则 48 | void setUnpack(unpack_setting_t* setting); 49 | 50 | // 连接状态回调 51 | std::function<void(const TSocketChannelPtr&)> onConnection; 52 | 53 | // 消息回调 54 | std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage; 55 | 56 | // 写完成回调 57 | std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete; 58 | 59 | }; 60 | 61 | ``` 62 | 63 | 测试代码见 [evpp/TcpClient_test.cpp](../../evpp/TcpClient_test.cpp) 64 | -------------------------------------------------------------------------------- /docs/cn/TcpServer.md: -------------------------------------------------------------------------------- 1 | TCP 服务端类 2 | 3 | ```c++ 4 | 5 | class TcpServer { 6 | 7 | // 返回索引的事件循环 8 | EventLoopPtr loop(int idx = -1); 9 | 10 | // 创建套接字 11 | int createsocket(int port, const char* host = "0.0.0.0"); 12 | 13 | // 关闭套接字 14 | void closesocket(); 15 | 16 | // 设置最大连接数 17 | void setMaxConnectionNum(uint32_t num); 18 | 19 | // 设置负载均衡策略 20 | void setLoadBalance(load_balance_e lb); 21 | 22 | // 设置线程数 23 | void setThreadNum(int num); 24 | 25 | // 开始运行 26 | void start(bool wait_threads_started = true); 27 | 28 | // 停止运行 29 | void stop(bool wait_threads_stopped = true); 30 | 31 | // 设置SSL/TLS加密通信 32 | int withTLS(hssl_ctx_opt_t* opt = NULL); 33 | 34 | // 设置拆包规则 35 | void setUnpack(unpack_setting_t* setting); 36 | 37 | // 返回当前连接数 38 | size_t connectionNum(); 39 | 40 | // 遍历连接 41 | int foreachChannel(std::function<void(const TSocketChannelPtr& channel)> fn); 42 | 43 | // 广播消息 44 | int broadcast(const void* data, int size); 45 | int broadcast(const std::string& str); 46 | 47 | // 连接到来/断开回调 48 | std::function<void(const TSocketChannelPtr&)> onConnection; 49 | 50 | // 消息回调 51 | std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage; 52 | 53 | // 写完成回调 54 | std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete; 55 | 56 | }; 57 | 58 | ``` 59 | 60 | 测试代码见 [evpp/TcpServer_test.cpp](../../evpp/TcpServer_test.cpp) 61 | -------------------------------------------------------------------------------- /docs/cn/UdpClient.md: -------------------------------------------------------------------------------- 1 | UDP 客户端类 2 | 3 | ```c++ 4 | 5 | class UdpClient { 6 | 7 | // 返回所在的事件循环 8 | const EventLoopPtr& loop(); 9 | 10 | // 创建套接字 11 | int createsocket(int remote_port, const char* remote_host = "127.0.0.1"); 12 | 13 | // 绑定端口 14 | int bind(int local_port, const char* local_host = "0.0.0.0"); 15 | 16 | // 关闭套接字 17 | void closesocket(); 18 | 19 | // 开始运行 20 | void start(bool wait_threads_started = true); 21 | 22 | // 停止运行 23 | void stop(bool wait_threads_stopped = true); 24 | 25 | // 发送 26 | int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL); 27 | int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL); 28 | int sendto(const std::string& str, struct sockaddr* peeraddr = NULL); 29 | 30 | // 设置KCP 31 | void setKcp(kcp_setting_t* setting); 32 | 33 | // 消息回调 34 | std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage; 35 | 36 | // 写完成回调 37 | std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete; 38 | }; 39 | 40 | ``` 41 | 42 | 测试代码见 [evpp/UdpClient_test.cpp](../../evpp/UdpClient_test.cpp) 43 | -------------------------------------------------------------------------------- /docs/cn/UdpServer.md: -------------------------------------------------------------------------------- 1 | UDP 服务端类 2 | 3 | ```c++ 4 | 5 | class UdpServer { 6 | 7 | // 返回所在的事件循环 8 | const EventLoopPtr& loop(); 9 | 10 | // 创建套接字 11 | int createsocket(int port, const char* host = "0.0.0.0"); 12 | 13 | // 关闭套接字 14 | void closesocket(); 15 | 16 | // 开始运行 17 | void start(bool wait_threads_started = true); 18 | 19 | // 停止运行 20 | void stop(bool wait_threads_stopped = true); 21 | 22 | // 发送 23 | int sendto(const void* data, int size, struct sockaddr* peeraddr = NULL); 24 | int sendto(Buffer* buf, struct sockaddr* peeraddr = NULL); 25 | int sendto(const std::string& str, struct sockaddr* peeraddr = NULL); 26 | 27 | // 设置KCP 28 | void setKcp(kcp_setting_t* setting); 29 | 30 | // 消息回调 31 | std::function<void(const TSocketChannelPtr&, Buffer*)> onMessage; 32 | 33 | // 写完成回调 34 | std::function<void(const TSocketChannelPtr&, Buffer*)> onWriteComplete; 35 | }; 36 | 37 | ``` 38 | 39 | 测试代码见 [evpp/UdpServer_test.cpp](../../evpp/UdpServer_test.cpp) 40 | -------------------------------------------------------------------------------- /docs/cn/WebSocketClient.md: -------------------------------------------------------------------------------- 1 | WebSocket 客户端类 2 | 3 | ```c++ 4 | 5 | class WebSocketClient { 6 | 7 | // 打开回调 8 | std::function<void()> onopen; 9 | // 关闭回调 10 | std::function<void()> onclose; 11 | // 消息回调 12 | std::function<void(const std::string& msg)> onmessage; 13 | 14 | // 打开 15 | int open(const char* url, const http_headers& headers = DefaultHeaders); 16 | 17 | // 关闭 18 | int close(); 19 | 20 | // 发送 21 | int send(const std::string& msg); 22 | int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY); 23 | 24 | // 设置心跳间隔 25 | void setPingInterval(int ms); 26 | 27 | // 设置WebSocket握手阶段的HTTP请求 28 | void setHttpRequest(const HttpRequestPtr& req); 29 | 30 | // 获取WebSocket握手阶段的HTTP响应 31 | const HttpResponsePtr& getHttpResponse(); 32 | 33 | }; 34 | 35 | ``` 36 | 37 | 测试代码见 [examples/websocket_client_test.cpp](../../examples/websocket_client_test.cpp) 38 | -------------------------------------------------------------------------------- /docs/cn/WebSocketServer.md: -------------------------------------------------------------------------------- 1 | WebSocket 服务端类 2 | 3 | ```c++ 4 | 5 | // WebSocketServer 继承自 HttpServer 6 | class WebSocketServer : public HttpServer { 7 | 8 | // 注册WebSocket业务类 9 | void registerWebSocketService(WebSocketService* service); 10 | 11 | }; 12 | 13 | // WebSocket业务类 14 | struct WebSocketService { 15 | // 打开回调 16 | std::function<void(const WebSocketChannelPtr&, const HttpRequestPtr&)> onopen; 17 | 18 | // 消息回调 19 | std::function<void(const WebSocketChannelPtr&, const std::string&)> onmessage; 20 | 21 | // 关闭回调 22 | std::function<void(const WebSocketChannelPtr&)> onclose; 23 | 24 | // 心跳间隔 25 | int ping_interval; 26 | }; 27 | 28 | ``` 29 | 30 | 测试代码见 [examples/websocket_server_test.cpp](../../examples/websocket_server_test.cpp) 31 | -------------------------------------------------------------------------------- /echo-servers/benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | host=127.0.0.1 4 | port=2000 5 | connections=100 6 | duration=10 7 | threads=2 8 | sendbytes=1024 9 | 10 | while getopts 'h:p:c:d:t:' opt 11 | do 12 | case $opt in 13 | h) host=$OPTARG;; 14 | p) port=$OPTARG;; 15 | c) connections=$OPTARG;; 16 | d) duration=$OPTARG;; 17 | t) threads=$OPTARG;; 18 | *) exit -1;; 19 | esac 20 | done 21 | 22 | SCRIPT_DIR=$(cd `dirname $0`; pwd) 23 | cd ${SCRIPT_DIR}/.. 24 | 25 | killall_echo_servers() { 26 | #sudo killall libevent_echo libev_echo libuv_echo libhv_echo asio_echo poco_echo muduo_echo 27 | if [ $(ps aux | grep _echo | grep -v grep | wc -l) -gt 0 ]; then 28 | ps aux | grep _echo | grep -v grep | awk '{print $2}' | xargs sudo kill -9 29 | fi 30 | } 31 | 32 | export LD_LIBRARY_PATH=lib:$LD_LIBRARY_PATH 33 | 34 | killall_echo_servers 35 | 36 | sport=$port 37 | 38 | if [ -x bin/libevent_echo ]; then 39 | let port++ 40 | bin/libevent_echo $port & 41 | echo "libevent running on port $port" 42 | fi 43 | 44 | if [ -x bin/libev_echo ]; then 45 | let port++ 46 | bin/libev_echo $port & 47 | echo "libev running on port $port" 48 | fi 49 | 50 | if [ -x bin/libuv_echo ]; then 51 | let port++ 52 | bin/libuv_echo $port & 53 | echo "libuv running on port $port" 54 | fi 55 | 56 | if [ -x bin/libhv_echo ]; then 57 | let port++ 58 | bin/libhv_echo $port & 59 | echo "libhv running on port $port" 60 | fi 61 | 62 | if [ -x bin/asio_echo ]; then 63 | let port++ 64 | bin/asio_echo $port & 65 | echo "asio running on port $port" 66 | fi 67 | 68 | if [ -x bin/poco_echo ]; then 69 | let port++ 70 | bin/poco_echo $port & 71 | echo "poco running on port $port" 72 | fi 73 | 74 | if [ -x bin/muduo_echo ]; then 75 | let port++ 76 | bin/muduo_echo $port & 77 | echo "muduo running on port $port" 78 | fi 79 | 80 | sleep 1 81 | 82 | for ((p=$sport+1; p<=$port; ++p)); do 83 | echo -e "\n==============$p=====================================" 84 | bin/pingpong_client -H $host -p $p -c $connections -d $duration -t $threads -b $sendbytes 85 | sleep 1 86 | done 87 | 88 | killall_echo_servers 89 | -------------------------------------------------------------------------------- /echo-servers/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(cd `dirname $0`; pwd) 4 | ROOT_DIR=${SCRIPT_DIR}/.. 5 | 6 | # install libevent libev libuv asio poco 7 | UNAME=$(uname -a) 8 | case ${UNAME} in 9 | *Ubuntu*|*Debian*) 10 | sudo apt install libevent-dev libev-dev libuv1-dev libboost-dev libboost-system-dev libasio-dev libpoco-dev 11 | ;; 12 | *CentOS*) 13 | sudo yum install libevent-devel libev-devel libuv-devel boost-devel asio-devel poco-devel 14 | ;; 15 | *Darwin*) 16 | brew install libevent libev libuv boost asio poco 17 | ;; 18 | *) 19 | echo 'please install libevent libev libuv boost asio poco' 20 | ;; 21 | esac 22 | 23 | # install muduo => https://github.com/chenshuo/muduo.git 24 | TEST_MUDUO=false 25 | if [ "$TEST_MUDUO" == "true" ]; then 26 | cd ${ROOT_DIR}/.. 27 | git clone https://github.com/chenshuo/muduo.git 28 | cd muduo 29 | mkdir build && cd build 30 | cmake .. && make && sudo make install 31 | fi 32 | 33 | # install libhv 34 | cd ${ROOT_DIR} 35 | make libhv && sudo make install && sudo ldconfig 36 | 37 | # build echo-servers 38 | make echo-servers 39 | -------------------------------------------------------------------------------- /echo-servers/libev_echo.c: -------------------------------------------------------------------------------- 1 | #include <unistd.h> 2 | #include <stdio.h> 3 | #include <stdlib.h> 4 | #include <string.h> 5 | 6 | #include <sys/types.h> 7 | #include <sys/socket.h> 8 | #include <netinet/in.h> 9 | 10 | #include "ev.h" 11 | 12 | #define RECV_BUFSIZE 8192 13 | static char recvbuf[RECV_BUFSIZE]; 14 | 15 | void do_recv(struct ev_loop *loop, struct ev_io *io, int revents) { 16 | int nread, nsend; 17 | nread = recv(io->fd, recvbuf, RECV_BUFSIZE, 0); 18 | if (nread <= 0) { 19 | goto error; 20 | } 21 | nsend = send(io->fd, recvbuf, nread, 0); 22 | if (nsend != nread) { 23 | goto error; 24 | } 25 | return; 26 | 27 | error: 28 | ev_io_stop(loop, io); 29 | close(io->fd); 30 | free(io); 31 | } 32 | 33 | void do_accept(struct ev_loop *loop, struct ev_io *listenio, int revents) { 34 | struct sockaddr_in peeraddr; 35 | socklen_t addrlen = sizeof(peeraddr); 36 | int connfd = accept(listenio->fd, (struct sockaddr*)&peeraddr, &addrlen); 37 | if (connfd <= 0) { 38 | return; 39 | } 40 | 41 | struct ev_io* io = (struct ev_io*)malloc(sizeof(struct ev_io)); 42 | ev_io_init(io, do_recv, connfd, EV_READ); 43 | ev_io_start(loop, io); 44 | } 45 | 46 | int main(int argc, char** argv) { 47 | if (argc < 2) { 48 | printf("Usage: cmd port\n"); 49 | return -10; 50 | } 51 | int port = atoi(argv[1]); 52 | 53 | struct sockaddr_in addr; 54 | int addrlen = sizeof(addr); 55 | memset(&addr, 0, addrlen); 56 | addr.sin_family = AF_INET; 57 | addr.sin_port = htons(port); 58 | int listenfd = socket(AF_INET, SOCK_STREAM, 0); 59 | if (listenfd < 0) { 60 | return -20; 61 | } 62 | if (bind(listenfd, (struct sockaddr*)&addr, addrlen) < 0) { 63 | return -30; 64 | } 65 | if (listen(listenfd, SOMAXCONN) < 0) { 66 | return -40; 67 | } 68 | 69 | struct ev_loop* loop = ev_loop_new(0); 70 | 71 | struct ev_io listenio; 72 | ev_io_init(&listenio, do_accept, listenfd, EV_READ); 73 | ev_io_start(loop, &listenio); 74 | 75 | ev_run(loop, 0); 76 | ev_loop_destroy(loop); 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /echo-servers/libevent_echo.c: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | #include <stdlib.h> 3 | #include <string.h> 4 | 5 | #include "event2/event.h" 6 | #include "event2/listener.h" 7 | #include "event2/bufferevent.h" 8 | #include "event2/buffer.h" 9 | 10 | //#define RECV_BUFSIZE 8192 11 | 12 | void error_cb(struct bufferevent* bev, short event, void* userdata) { 13 | bufferevent_free(bev); 14 | } 15 | 16 | void read_cb(struct bufferevent* bev, void* userdata) { 17 | //static char recvbuf[RECV_BUFSIZE]; 18 | //int nread = bufferevent_read(bev, &recvbuf, RECV_BUFSIZE); 19 | //bufferevent_write(bev, recvbuf, nread); 20 | struct evbuffer* buf = evbuffer_new(); 21 | int ret = bufferevent_read_buffer(bev, buf); 22 | if (ret == 0) { 23 | bufferevent_write_buffer(bev, buf); 24 | } 25 | evbuffer_free(buf); 26 | } 27 | 28 | void on_accept(struct evconnlistener* listener, evutil_socket_t connfd, struct sockaddr* peeraddr, int addrlen, void* userdata) { 29 | struct event_base* loop = evconnlistener_get_base(listener); 30 | struct bufferevent* bev = bufferevent_socket_new(loop, connfd, BEV_OPT_CLOSE_ON_FREE); 31 | bufferevent_setcb(bev, read_cb, NULL, error_cb, NULL); 32 | bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST); 33 | } 34 | 35 | int main(int argc, char** argv) { 36 | if (argc < 2) { 37 | printf("Usage: cmd port\n"); 38 | return -10; 39 | } 40 | int port = atoi(argv[1]); 41 | 42 | struct event_base* loop = event_base_new(); 43 | 44 | struct sockaddr_in addr; 45 | memset(&addr, 0, sizeof(addr)); 46 | addr.sin_family = AF_INET; 47 | addr.sin_port = htons(port); 48 | struct evconnlistener* listener = evconnlistener_new_bind( 49 | loop, on_accept, NULL, 50 | LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, 51 | -1, (struct sockaddr*)&addr, sizeof(addr)); 52 | if (listener == NULL) { 53 | return -20; 54 | } 55 | 56 | event_base_dispatch(loop); 57 | 58 | evconnlistener_free(listener); 59 | event_base_free(loop); 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /echo-servers/libhv_echo.c: -------------------------------------------------------------------------------- 1 | #include "hv/hloop.h" 2 | 3 | void on_close(hio_t* io) { 4 | } 5 | 6 | void on_recv(hio_t* io, void* buf, int readbytes) { 7 | hio_write(io, buf, readbytes); 8 | } 9 | 10 | void on_accept(hio_t* io) { 11 | hio_setcb_close(io, on_close); 12 | hio_setcb_read(io, on_recv); 13 | hio_read(io); 14 | } 15 | 16 | int main(int argc, char** argv) { 17 | if (argc < 2) { 18 | printf("Usage: cmd port\n"); 19 | return -10; 20 | } 21 | int port = atoi(argv[1]); 22 | 23 | hloop_t* loop = hloop_new(0); 24 | hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept); 25 | if (listenio == NULL) { 26 | return -20; 27 | } 28 | hloop_run(loop); 29 | hloop_free(&loop); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /echo-servers/libuv_echo.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 1 2 | #include <stdio.h> 3 | #include <stdlib.h> 4 | #include <string.h> 5 | 6 | #include "uv.h" 7 | 8 | typedef struct { 9 | uv_write_t req; 10 | uv_buf_t buf; 11 | } uv_write_req_t; 12 | 13 | void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { 14 | buf->base = (char*)malloc(suggested_size); 15 | buf->len = suggested_size; 16 | } 17 | 18 | void close_cb(uv_handle_t* handle) { 19 | free(handle); 20 | } 21 | 22 | void write_cb(uv_write_t* req, int status) { 23 | uv_write_req_t* wr = (uv_write_req_t*)req; 24 | free(wr->buf.base); 25 | free(wr); 26 | } 27 | 28 | void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { 29 | if (nread <= 0) { 30 | uv_close((uv_handle_t*)stream, close_cb); 31 | return; 32 | } 33 | uv_write_req_t* wr = (uv_write_req_t*)malloc(sizeof(uv_write_req_t)); 34 | wr->buf.base = buf->base; 35 | wr->buf.len = nread; 36 | uv_write(&wr->req, stream, &wr->buf, 1, write_cb); 37 | } 38 | 39 | void do_accept(uv_stream_t* server, int status) { 40 | uv_tcp_t* tcp_stream = (uv_tcp_t*)malloc(sizeof(uv_tcp_t)); 41 | uv_tcp_init(server->loop, tcp_stream); 42 | uv_accept(server, (uv_stream_t*)tcp_stream); 43 | uv_read_start((uv_stream_t*)tcp_stream, alloc_cb, read_cb); 44 | } 45 | 46 | int main(int argc, char** argv) { 47 | if (argc < 2) { 48 | printf("Usage: cmd port\n"); 49 | return -10; 50 | } 51 | int port = atoi(argv[1]); 52 | 53 | uv_loop_t loop; 54 | uv_loop_init(&loop); 55 | 56 | struct sockaddr_in addr; 57 | memset(&addr, 0, sizeof(addr)); 58 | //addr.sin_family = AF_INET; 59 | //addr.sin_port = htons(port); 60 | uv_ip4_addr("0.0.0.0", port, &addr); 61 | 62 | uv_tcp_t tcp_server; 63 | uv_tcp_init(&loop, &tcp_server); 64 | int ret = uv_tcp_bind(&tcp_server, (struct sockaddr*)&addr, 0); 65 | if (ret) { 66 | return -20; 67 | } 68 | ret = uv_listen((uv_stream_t*)&tcp_server, SOMAXCONN, do_accept); 69 | if (ret) { 70 | return -30; 71 | } 72 | 73 | uv_run(&loop, UV_RUN_DEFAULT); 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /echo-servers/muduo_echo.cpp: -------------------------------------------------------------------------------- 1 | // @see muduo/examples/simple/echo 2 | #include "muduo/base/Logging.h" 3 | #include "muduo/net/EventLoop.h" 4 | #include "muduo/net/InetAddress.h" 5 | #include "muduo/net/TcpServer.h" 6 | 7 | using std::placeholders::_1; 8 | using std::placeholders::_2; 9 | using std::placeholders::_3; 10 | 11 | using muduo::Timestamp; 12 | 13 | using muduo::net::EventLoop; 14 | using muduo::net::InetAddress; 15 | using muduo::net::TcpServer; 16 | using muduo::net::TcpConnectionPtr; 17 | using muduo::net::Buffer; 18 | 19 | class EchoTcpServer { 20 | public: 21 | EchoTcpServer(EventLoop* loop, const InetAddress& addr) 22 | : server_(loop, addr, "EchoTcpServer") 23 | { 24 | server_.setConnectionCallback(std::bind(&EchoTcpServer::onConnection, this, _1)); 25 | server_.setMessageCallback(std::bind(&EchoTcpServer::onMessage, this, _1, _2, _3)); 26 | } 27 | 28 | void start() { 29 | server_.start(); 30 | } 31 | 32 | private: 33 | void onConnection(const TcpConnectionPtr& conn) { 34 | } 35 | 36 | void onMessage(const TcpConnectionPtr& conn, 37 | Buffer* buf, Timestamp time) { 38 | muduo::string msg(buf->retrieveAllAsString()); 39 | conn->send(msg); 40 | } 41 | 42 | TcpServer server_; 43 | }; 44 | 45 | int main(int argc, char** argv) { 46 | if (argc < 2) { 47 | printf("Usage: cmd port\n"); 48 | return -10; 49 | } 50 | int port = atoi(argv[1]); 51 | 52 | muduo::g_logLevel = muduo::Logger::ERROR; 53 | muduo::net::EventLoop loop; 54 | 55 | muduo::net::InetAddress addr(port); 56 | EchoTcpServer server(&loop, addr); 57 | server.start(); 58 | 59 | loop.loop(); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /etc/hmain_test.conf: -------------------------------------------------------------------------------- 1 | # [root] 2 | 3 | # logfile = logs/test.log 4 | # loglevel = [VERBOSE,DEBUG,INFO,WARN,ERROR,FATAL,SILENT] 5 | loglevel = DEBUG 6 | log_remain_days = 3 7 | log_filesize = 16M 8 | 9 | # worker_processes = auto # auto = ncpu 10 | worker_processes = auto 11 | worker_threads = 2 12 | 13 | port = 8086 14 | -------------------------------------------------------------------------------- /etc/httpd.conf: -------------------------------------------------------------------------------- 1 | # [root] 2 | 3 | # logfile = logs/httpd.log 4 | # loglevel = [VERBOSE,DEBUG,INFO,WARN,ERROR,FATAL,SILENT] 5 | loglevel = INFO 6 | log_remain_days = 3 7 | log_filesize = 64M 8 | 9 | # multi-processes mode 10 | # auto = ncpu 11 | worker_processes = auto 12 | worker_threads = 1 13 | 14 | # multi-threads mode 15 | # worker_processes = 1 16 | # worker_threads = auto 17 | 18 | # Disable multi-processes mode for debugging 19 | # worker_processes = 0 20 | 21 | # max_connections = workers * worker_connections 22 | worker_connections = 1024 23 | 24 | # http server 25 | http_port = 8080 26 | https_port = 8443 27 | #base_url = /api/v1 28 | document_root = html 29 | home_page = index.html 30 | #error_page = error.html 31 | index_of = /downloads/ 32 | keepalive_timeout = 75000 # ms 33 | limit_rate = 500 # KB/s 34 | access_log = off 35 | cors = true 36 | 37 | # SSL/TLS 38 | ssl_certificate = cert/server.crt 39 | ssl_privatekey = cert/server.key 40 | ssl_ca_certificate = cert/cacert.pem 41 | 42 | # proxy 43 | [proxy] 44 | proxy_connect_timeout = 10000 # ms 45 | proxy_read_timeout = 60000 # ms 46 | proxy_write_timeout = 60000 # ms 47 | # forward proxy 48 | forward_proxy = true 49 | trust_proxies = *httpbin.org;*postman-echo.com;*apifox.com 50 | #no_proxies = * 51 | # reverse proxy 52 | /httpbin/ => http://httpbin.org/ 53 | /postman/ => http://postman-echo.com/ 54 | /apifox/ => https://echo.apifox.com/ 55 | -------------------------------------------------------------------------------- /etc/nginx.conf: -------------------------------------------------------------------------------- 1 | # cd libhv 2 | # sudo nginx -p . -c etc/nginx.conf 3 | # bin/httpd -c etc/httpd.conf -s restart -d 4 | # bin/curl -v http://127.0.0.1/api/v1/get 5 | 6 | worker_processes auto; 7 | 8 | pid logs/nginx.pid; 9 | error_log logs/error.log; 10 | 11 | events { 12 | worker_connections 1024; 13 | } 14 | 15 | http { 16 | access_log logs/access.log; 17 | 18 | server { 19 | listen 80; 20 | 21 | # static files service 22 | location / { 23 | root html; 24 | index index.html; 25 | } 26 | 27 | # autoindex service 28 | location /downloads/ { 29 | autoindex on; 30 | } 31 | 32 | # api service: nginx => libhv 33 | location /api/v1/ { 34 | proxy_pass http://127.0.0.1:8080/; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /event/README.md: -------------------------------------------------------------------------------- 1 | ## 目录结构 2 | 3 | ``` 4 | . 5 | ├── hloop.h 事件循环模块对外头文件 6 | ├── hevent.h 事件结构体定义 7 | ├── nlog.h 网络日志 8 | ├── unpack.h 拆包 9 | ├── rudp.h 可靠UDP 10 | ├── iowatcher.h IO多路复用统一抽象接口 11 | ├── select.c EVENT_SELECT实现 12 | ├── poll.c EVENT_POLL实现 13 | ├── epoll.c EVENT_EPOLL实现 (for OS_LINUX) 14 | ├── iocp.c EVENT_IOCP实现 (for OS_WIN) 15 | ├── kqueue.c EVENT_KQUEUE实现(for OS_BSD/OS_MAC) 16 | ├── evport.c EVENT_PORT实现 (for OS_SOLARIS) 17 | ├── nio.c 非阻塞IO 18 | └── overlapio.c 重叠IO 19 | 20 | ``` 21 | -------------------------------------------------------------------------------- /event/iocp.c: -------------------------------------------------------------------------------- 1 | #include "iowatcher.h" 2 | 3 | #ifdef EVENT_IOCP 4 | #include "hplatform.h" 5 | #include "hdef.h" 6 | 7 | #include "hevent.h" 8 | #include "overlapio.h" 9 | 10 | typedef struct iocp_ctx_s { 11 | HANDLE iocp; 12 | } iocp_ctx_t; 13 | 14 | int iowatcher_init(hloop_t* loop) { 15 | if (loop->iowatcher) return 0; 16 | iocp_ctx_t* iocp_ctx; 17 | HV_ALLOC_SIZEOF(iocp_ctx); 18 | iocp_ctx->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 19 | loop->iowatcher = iocp_ctx; 20 | return 0; 21 | } 22 | 23 | int iowatcher_cleanup(hloop_t* loop) { 24 | if (loop->iowatcher == NULL) return 0; 25 | iocp_ctx_t* iocp_ctx = (iocp_ctx_t*)loop->iowatcher; 26 | CloseHandle(iocp_ctx->iocp); 27 | HV_FREE(loop->iowatcher); 28 | return 0; 29 | } 30 | 31 | int iowatcher_add_event(hloop_t* loop, int fd, int events) { 32 | if (loop->iowatcher == NULL) { 33 | iowatcher_init(loop); 34 | } 35 | iocp_ctx_t* iocp_ctx = (iocp_ctx_t*)loop->iowatcher; 36 | hio_t* io = loop->ios.ptr[fd]; 37 | if (io && io->events == 0 && events != 0) { 38 | CreateIoCompletionPort((HANDLE)fd, iocp_ctx->iocp, 0, 0); 39 | } 40 | return 0; 41 | } 42 | 43 | int iowatcher_del_event(hloop_t* loop, int fd, int events) { 44 | hio_t* io = loop->ios.ptr[fd]; 45 | if ((io->events & ~events) == 0) { 46 | CancelIo((HANDLE)fd); 47 | } 48 | return 0; 49 | } 50 | 51 | int iowatcher_poll_events(hloop_t* loop, int timeout) { 52 | if (loop->iowatcher == NULL) return 0; 53 | iocp_ctx_t* iocp_ctx = (iocp_ctx_t*)loop->iowatcher; 54 | DWORD bytes = 0; 55 | ULONG_PTR key = 0; 56 | LPOVERLAPPED povlp = NULL; 57 | BOOL bRet = GetQueuedCompletionStatus(iocp_ctx->iocp, &bytes, &key, &povlp, timeout); 58 | int err = 0; 59 | if (povlp == NULL) { 60 | err = WSAGetLastError(); 61 | if (err == WAIT_TIMEOUT || ERROR_NETNAME_DELETED || ERROR_OPERATION_ABORTED) { 62 | return 0; 63 | } 64 | return -err; 65 | } 66 | hoverlapped_t* hovlp = (hoverlapped_t*)povlp; 67 | hio_t* io = hovlp->io; 68 | if (bRet == FALSE) { 69 | err = WSAGetLastError(); 70 | printd("iocp ret=%d err=%d bytes=%u\n", bRet, err, bytes); 71 | // NOTE: when ConnectEx failed, err != 0 72 | hovlp->error = err; 73 | } 74 | // NOTE: when WSASend/WSARecv disconnect, bytes = 0 75 | hovlp->bytes = bytes; 76 | io->hovlp = hovlp; 77 | io->revents |= hovlp->event; 78 | EVENT_PENDING(hovlp->io); 79 | return 1; 80 | } 81 | #endif 82 | -------------------------------------------------------------------------------- /event/iowatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_WATCHER_H_ 2 | #define IO_WATCHER_H_ 3 | 4 | #include "hloop.h" 5 | 6 | #include "hplatform.h" 7 | #if !defined(EVENT_SELECT) && \ 8 | !defined(EVENT_POLL) && \ 9 | !defined(EVENT_EPOLL) && \ 10 | !defined(EVENT_KQUEUE) && \ 11 | !defined(EVENT_IOCP) && \ 12 | !defined(EVENT_PORT) && \ 13 | !defined(EVENT_NOEVENT) 14 | #ifdef OS_WIN 15 | #if WITH_WEPOLL 16 | #define EVENT_EPOLL // wepoll -> iocp 17 | #else 18 | #define EVENT_POLL // WSAPoll 19 | #endif 20 | #elif defined(OS_LINUX) 21 | #define EVENT_EPOLL 22 | #elif defined(OS_MAC) 23 | #define EVENT_KQUEUE 24 | #elif defined(OS_BSD) 25 | #define EVENT_KQUEUE 26 | #elif defined(OS_SOLARIS) 27 | #define EVENT_PORT 28 | #else 29 | #define EVENT_SELECT 30 | #endif 31 | #endif 32 | 33 | int iowatcher_init(hloop_t* loop); 34 | int iowatcher_cleanup(hloop_t* loop); 35 | int iowatcher_add_event(hloop_t* loop, int fd, int events); 36 | int iowatcher_del_event(hloop_t* loop, int fd, int events); 37 | int iowatcher_poll_events(hloop_t* loop, int timeout); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /event/kcp/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Lin Wei (skywind3000 at gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /event/kcp/hkcp.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_KCP_H_ 2 | #define HV_KCP_H_ 3 | 4 | #include "hloop.h" 5 | 6 | #if WITH_KCP 7 | 8 | #include "ikcp.h" 9 | #include "hbuf.h" 10 | 11 | #define DEFAULT_KCP_UPDATE_INTERVAL 10 // ms 12 | #define DEFAULT_KCP_READ_BUFSIZE 1400 13 | 14 | typedef struct kcp_s { 15 | ikcpcb* ikcp; 16 | uint32_t conv; 17 | htimer_t* update_timer; 18 | hbuf_t readbuf; 19 | } kcp_t; 20 | 21 | // NOTE: kcp_create in hio_get_kcp 22 | void kcp_release(kcp_t* kcp); 23 | 24 | kcp_t* hio_get_kcp (hio_t* io, uint32_t conv); 25 | int hio_read_kcp (hio_t* io, void* buf, int readbytes); 26 | int hio_write_kcp(hio_t* io, const void* buf, size_t len); 27 | 28 | #endif 29 | 30 | #endif // HV_KCP_H_ 31 | -------------------------------------------------------------------------------- /event/nlog.c: -------------------------------------------------------------------------------- 1 | #include "nlog.h" 2 | 3 | #include "list.h" 4 | #include "hdef.h" 5 | #include "hbase.h" 6 | #include "hsocket.h" 7 | #include "hmutex.h" 8 | 9 | typedef struct network_logger_s { 10 | hloop_t* loop; 11 | hio_t* listenio; 12 | struct list_head clients; 13 | } network_logger_t; 14 | 15 | typedef struct nlog_client { 16 | hio_t* io; 17 | struct list_node node; 18 | } nlog_client; 19 | 20 | static network_logger_t s_logger = {0}; 21 | static hmutex_t s_mutex; 22 | 23 | static void on_close(hio_t* io) { 24 | printd("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io)); 25 | 26 | nlog_client* client = (nlog_client*)hevent_userdata(io); 27 | if (client) { 28 | hevent_set_userdata(io, NULL); 29 | 30 | hmutex_lock(&s_mutex); 31 | list_del(&client->node); 32 | hmutex_unlock(&s_mutex); 33 | 34 | HV_FREE(client); 35 | } 36 | } 37 | 38 | static void on_read(hio_t* io, void* buf, int readbytes) { 39 | printd("on_read fd=%d readbytes=%d\n", hio_fd(io), readbytes); 40 | printd("< %s\n", (char*)buf); 41 | // nothing to do 42 | } 43 | 44 | static void on_accept(hio_t* io) { 45 | /* 46 | printd("on_accept connfd=%d\n", hio_fd(io)); 47 | char localaddrstr[SOCKADDR_STRLEN] = {0}; 48 | char peeraddrstr[SOCKADDR_STRLEN] = {0}; 49 | printd("accept connfd=%d [%s] <= [%s]\n", hio_fd(io), 50 | SOCKADDR_STR(hio_localaddr(io), localaddrstr), 51 | SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); 52 | */ 53 | 54 | hio_setcb_read(io, on_read); 55 | hio_setcb_close(io, on_close); 56 | hio_read(io); 57 | 58 | // free on_close 59 | nlog_client* client; 60 | HV_ALLOC_SIZEOF(client); 61 | client->io = io; 62 | hevent_set_userdata(io, client); 63 | 64 | hmutex_lock(&s_mutex); 65 | list_add(&client->node, &s_logger.clients); 66 | hmutex_unlock(&s_mutex); 67 | } 68 | 69 | void network_logger(int loglevel, const char* buf, int len) { 70 | struct list_node* node; 71 | nlog_client* client; 72 | 73 | hmutex_lock(&s_mutex); 74 | list_for_each (node, &s_logger.clients) { 75 | client = list_entry(node, nlog_client, node); 76 | hio_write(client->io, buf, len); 77 | } 78 | hmutex_unlock(&s_mutex); 79 | } 80 | 81 | hio_t* nlog_listen(hloop_t* loop, int port) { 82 | s_logger.loop = loop; 83 | s_logger.listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept); 84 | list_init(&s_logger.clients); 85 | hmutex_init(&s_mutex); 86 | return s_logger.listenio; 87 | } 88 | -------------------------------------------------------------------------------- /event/nlog.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_NLOG_H_ 2 | #define HV_NLOG_H_ 3 | 4 | // nlog: extend hlog use hloop 5 | 6 | /* you can recv log by: 7 | * Windows: telnet ip port 8 | * Linux: nc ip port 9 | */ 10 | 11 | /* 12 | * @see examples/hloop_test.c 13 | #include "hlog.h" 14 | #include "nlog.h" 15 | 16 | void timer_write_log(htimer_t* timer) { 17 | static int cnt = 0; 18 | hlogi("[%d] Do you recv me?", ++cnt); 19 | } 20 | 21 | int main() { 22 | hloop_t* loop = hloop_new(0); 23 | hlog_set_handler(network_logger); 24 | nlog_listen(loop, DEFAULT_LOG_PORT); 25 | htimer_add(loop, timer_write_log, 1000, INFINITE); 26 | hloop_run(loop); 27 | hloop_free(&loop); 28 | } 29 | */ 30 | 31 | 32 | #include "hexport.h" 33 | #include "hloop.h" 34 | 35 | #define DEFAULT_LOG_PORT 10514 36 | 37 | BEGIN_EXTERN_C 38 | 39 | HV_EXPORT void network_logger(int loglevel, const char* buf, int len); 40 | HV_EXPORT hio_t* nlog_listen(hloop_t* loop, int port); 41 | 42 | END_EXTERN_C 43 | 44 | #endif // HV_NLOG_H_ 45 | -------------------------------------------------------------------------------- /event/noevent.c: -------------------------------------------------------------------------------- 1 | #include "iowatcher.h" 2 | 3 | #ifdef EVENT_NOEVENT 4 | int iowatcher_init(hloop_t* loop) { 5 | return 0; 6 | } 7 | 8 | int iowatcher_cleanup(hloop_t* loop) { 9 | return 0; 10 | } 11 | 12 | int iowatcher_add_event(hloop_t* loop, int fd, int events) { 13 | return 0; 14 | } 15 | 16 | int iowatcher_del_event(hloop_t* loop, int fd, int events) { 17 | return 0; 18 | } 19 | 20 | int iowatcher_poll_events(hloop_t* loop, int timeout) { 21 | hv_delay(timeout); 22 | return 0; 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /event/overlapio.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_OVERLAPPED_H_ 2 | #define HV_OVERLAPPED_H_ 3 | 4 | #include "iowatcher.h" 5 | 6 | #ifdef EVENT_IOCP 7 | 8 | #include "hbuf.h" 9 | #include "hsocket.h" 10 | #include <mswsock.h> 11 | #ifdef _MSC_VER 12 | #pragma comment(lib, "mswsock.lib") 13 | #endif 14 | 15 | typedef struct hoverlapped_s { 16 | OVERLAPPED ovlp; 17 | int fd; 18 | int event; 19 | WSABUF buf; 20 | int bytes; 21 | int error; 22 | hio_t* io; 23 | // for recvfrom 24 | struct sockaddr* addr; 25 | int addrlen; 26 | } hoverlapped_t; 27 | 28 | int post_acceptex(hio_t* listenio, hoverlapped_t* hovlp); 29 | int post_recv(hio_t* io, hoverlapped_t* hovlp); 30 | 31 | #endif 32 | 33 | #endif // HV_OVERLAPPED_H_ 34 | -------------------------------------------------------------------------------- /event/rudp.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_RUDP_H_ 2 | #define HV_RUDP_H_ 3 | 4 | #include "hloop.h" 5 | 6 | #if WITH_RUDP 7 | 8 | #include "rbtree.h" 9 | #include "hsocket.h" 10 | #include "hmutex.h" 11 | #if WITH_KCP 12 | #include "kcp/hkcp.h" 13 | #endif 14 | 15 | typedef struct rudp_s { 16 | struct rb_root rb_root; 17 | hmutex_t mutex; 18 | } rudp_t; 19 | 20 | typedef struct rudp_entry_s { 21 | struct rb_node rb_node; 22 | sockaddr_u addr; // key 23 | // val 24 | hio_t* io; 25 | #if WITH_KCP 26 | kcp_t kcp; 27 | #endif 28 | } rudp_entry_t; 29 | 30 | // NOTE: rudp_entry_t alloc when rudp_get 31 | void rudp_entry_free(rudp_entry_t* entry); 32 | 33 | void rudp_init(rudp_t* rudp); 34 | void rudp_cleanup(rudp_t* rudp); 35 | 36 | bool rudp_insert(rudp_t* rudp, rudp_entry_t* entry); 37 | // NOTE: just rb_erase, not free 38 | rudp_entry_t* rudp_remove(rudp_t* rudp, struct sockaddr* addr); 39 | rudp_entry_t* rudp_search(rudp_t* rudp, struct sockaddr* addr); 40 | #define rudp_has(rudp, addr) (rudp_search(rudp, addr) != NULL) 41 | 42 | // rudp_search + malloc + rudp_insert 43 | rudp_entry_t* rudp_get(rudp_t* rudp, struct sockaddr* addr); 44 | // rudp_remove + free 45 | void rudp_del(rudp_t* rudp, struct sockaddr* addr); 46 | 47 | // rudp_get(&io->rudp, io->peeraddr) 48 | rudp_entry_t* hio_get_rudp(hio_t* io); 49 | 50 | #endif // WITH_RUDP 51 | 52 | #endif // HV_RUDP_H_ 53 | -------------------------------------------------------------------------------- /event/unpack.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_UNPACK_H_ 2 | #define HV_UNPACK_H_ 3 | 4 | #include "hloop.h" 5 | 6 | int hio_unpack(hio_t* io, void* buf, int readbytes); 7 | int hio_unpack_by_fixed_length(hio_t* io, void* buf, int readbytes); 8 | int hio_unpack_by_delimiter(hio_t* io, void* buf, int readbytes); 9 | int hio_unpack_by_length_field(hio_t* io, void* buf, int readbytes); 10 | 11 | #endif // HV_UNPACK_H_ 12 | -------------------------------------------------------------------------------- /event/wepoll/LICENSE: -------------------------------------------------------------------------------- 1 | wepoll - epoll for Windows 2 | https://github.com/piscisaureus/wepoll 3 | 4 | Copyright 2012-2020, Bert Belder <bertbelder@gmail.com> 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are 9 | met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /evpp/Buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_BUFFER_HPP_ 2 | #define HV_BUFFER_HPP_ 3 | 4 | #include <memory> 5 | 6 | #include "hbuf.h" 7 | 8 | namespace hv { 9 | 10 | typedef HBuf Buffer; 11 | typedef std::shared_ptr<Buffer> BufferPtr; 12 | 13 | } 14 | 15 | #endif // HV_BUFFER_HPP_ 16 | -------------------------------------------------------------------------------- /evpp/Event.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_EVENT_HPP_ 2 | #define HV_EVENT_HPP_ 3 | 4 | #include <functional> 5 | #include <memory> 6 | 7 | #include "hloop.h" 8 | 9 | namespace hv { 10 | 11 | struct Event; 12 | struct Timer; 13 | 14 | typedef uint64_t TimerID; 15 | #define INVALID_TIMER_ID ((hv::TimerID)-1) 16 | 17 | typedef std::function<void(Event*)> EventCallback; 18 | typedef std::function<void(TimerID)> TimerCallback; 19 | 20 | struct Event { 21 | hevent_t event; 22 | EventCallback cb; 23 | 24 | Event(EventCallback cb = NULL) { 25 | memset(&event, 0, sizeof(hevent_t)); 26 | this->cb = std::move(cb); 27 | } 28 | }; 29 | 30 | struct Timer { 31 | htimer_t* timer; 32 | TimerCallback cb; 33 | uint32_t repeat; 34 | 35 | Timer(htimer_t* timer = NULL, TimerCallback cb = NULL, uint32_t repeat = INFINITE) { 36 | this->timer = timer; 37 | this->cb = std::move(cb); 38 | this->repeat = repeat; 39 | } 40 | }; 41 | 42 | typedef std::shared_ptr<Event> EventPtr; 43 | typedef std::shared_ptr<Timer> TimerPtr; 44 | 45 | } 46 | 47 | #endif // HV_EVENT_HPP_ 48 | -------------------------------------------------------------------------------- /evpp/EventLoopThreadPool_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoopThreadPool_test.cpp 3 | * 4 | * @build: make evpp 5 | * 6 | */ 7 | 8 | #include "hv.h" 9 | 10 | #include "EventLoopThreadPool.h" 11 | 12 | using namespace hv; 13 | 14 | static void onTimer(TimerID timerID, int n) { 15 | printf("tid=%ld timerID=%lu time=%lus n=%d\n", hv_gettid(), (unsigned long)timerID, (unsigned long)time(NULL), n); 16 | } 17 | 18 | int main(int argc, char* argv[]) { 19 | HV_MEMCHECK; 20 | hlog_set_level(LOG_LEVEL_DEBUG); 21 | 22 | printf("main tid=%ld\n", hv_gettid()); 23 | 24 | EventLoopThreadPool loop_threads(4); 25 | loop_threads.start(true); 26 | 27 | int thread_num = loop_threads.threadNum(); 28 | for (int i = 0; i < thread_num; ++i) { 29 | EventLoopPtr loop = loop_threads.nextLoop(); 30 | printf("worker[%d] tid=%ld\n", i, loop->tid()); 31 | 32 | loop->runInLoop([loop](){ 33 | // runEvery 1s 34 | loop->setInterval(1000, std::bind(onTimer, std::placeholders::_1, 100)); 35 | }); 36 | 37 | loop->queueInLoop([](){ 38 | printf("queueInLoop tid=%ld\n", hv_gettid()); 39 | }); 40 | 41 | loop->runInLoop([](){ 42 | printf("runInLoop tid=%ld\n", hv_gettid()); 43 | }); 44 | } 45 | 46 | // runAfter 10s 47 | loop_threads.loop()->setTimeout(10000, [&loop_threads](TimerID timerID){ 48 | loop_threads.stop(false); 49 | }); 50 | 51 | // wait loop_threads exit 52 | loop_threads.join(); 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /evpp/EventLoopThread_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoopThread_test.cpp 3 | * 4 | * @build: make evpp 5 | * 6 | */ 7 | 8 | #include "hv.h" 9 | 10 | #include "EventLoopThread.h" 11 | 12 | using namespace hv; 13 | 14 | static void onTimer(TimerID timerID, int n) { 15 | printf("tid=%ld timerID=%lu time=%lus n=%d\n", hv_gettid(), (unsigned long)timerID, (unsigned long)time(NULL), n); 16 | } 17 | 18 | int main(int argc, char* argv[]) { 19 | HV_MEMCHECK; 20 | 21 | printf("main tid=%ld\n", hv_gettid()); 22 | 23 | EventLoopThread loop_thread; 24 | const EventLoopPtr& loop = loop_thread.loop(); 25 | 26 | // runEvery 1s 27 | loop->setInterval(1000, std::bind(onTimer, std::placeholders::_1, 100)); 28 | 29 | // runAfter 10s 30 | loop->setTimeout(10000, [&loop](TimerID timerID){ 31 | loop->stop(); 32 | }); 33 | 34 | loop_thread.start(); 35 | 36 | loop->queueInLoop([](){ 37 | printf("queueInLoop tid=%ld\n", hv_gettid()); 38 | }); 39 | 40 | loop->runInLoop([](){ 41 | printf("runInLoop tid=%ld\n", hv_gettid()); 42 | }); 43 | 44 | // wait loop_thread exit 45 | loop_thread.join(); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /evpp/EventLoop_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EventLoop_test.cpp 3 | * 4 | * @build: make evpp 5 | * 6 | */ 7 | 8 | #include "hv.h" 9 | 10 | #include "EventLoop.h" 11 | 12 | using namespace hv; 13 | 14 | static void onTimer(TimerID timerID, int n) { 15 | printf("tid=%ld timerID=%lu time=%lus n=%d\n", hv_gettid(), (unsigned long)timerID, (unsigned long)time(NULL), n); 16 | } 17 | 18 | int main(int argc, char* argv[]) { 19 | HV_MEMCHECK; 20 | 21 | printf("main tid=%ld\n", hv_gettid()); 22 | 23 | auto loop = std::make_shared<EventLoop>(); 24 | 25 | // runEvery 1s 26 | loop->setInterval(1000, std::bind(onTimer, std::placeholders::_1, 100)); 27 | 28 | // runAfter 10s 29 | loop->setTimeout(10000, [&loop](TimerID timerID){ 30 | loop->stop(); 31 | }); 32 | 33 | loop->queueInLoop([](){ 34 | printf("queueInLoop tid=%ld\n", hv_gettid()); 35 | }); 36 | 37 | loop->runInLoop([](){ 38 | printf("runInLoop tid=%ld\n", hv_gettid()); 39 | }); 40 | 41 | // run until loop stopped 42 | loop->run(); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /evpp/README.md: -------------------------------------------------------------------------------- 1 | The evpp module is designed to be header-only and does not participate in compilation. 2 | hloop.h is encapsulated into c++ classes, referring to muduo and evpp. 3 | You can modify and use evpp classes according to your own business. 4 | 5 | evpp模块被设计成只包含头文件,不参与编译。 6 | hloop.h中的c接口被封装成了c++的类,参考了muduo和evpp。 7 | 你能修改和使用这些类根据你自己的业务。 8 | 9 | ## 目录结构 10 | 11 | ``` 12 | . 13 | ├── Buffer.h 缓存类 14 | ├── Channel.h 通道类,封装了hio_t 15 | ├── Event.h 事件类,封装了hevent_t、htimer_t 16 | ├── EventLoop.h 事件循环类,封装了hloop_t 17 | ├── EventLoopThread.h 事件循环线程类,组合了EventLoop和thread 18 | ├── EventLoopThreadPool.h 事件循环线程池类,组合了EventLoop和ThreadPool 19 | ├── TcpClient.h TCP客户端类 20 | ├── TcpServer.h TCP服务端类 21 | ├── UdpClient.h UDP客户端类 22 | └── UdpServer.h UDP服务端类 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /evpp/Status.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_STATUS_HPP_ 2 | #define HV_STATUS_HPP_ 3 | 4 | #include <atomic> 5 | 6 | namespace hv { 7 | 8 | class Status { 9 | public: 10 | enum KStatus { 11 | kNull = 0, 12 | kInitializing = 1, 13 | kInitialized = 2, 14 | kStarting = 3, 15 | kStarted = 4, 16 | kRunning = 5, 17 | kPause = 6, 18 | kStopping = 7, 19 | kStopped = 8, 20 | kDestroyed = 9, 21 | }; 22 | 23 | Status() { 24 | status_ = kNull; 25 | } 26 | ~Status() { 27 | status_ = kDestroyed; 28 | } 29 | 30 | KStatus status() { 31 | return status_; 32 | } 33 | 34 | void setStatus(KStatus status) { 35 | status_ = status; 36 | } 37 | 38 | bool isRunning() { 39 | return status_ == kRunning; 40 | } 41 | 42 | bool isPause() { 43 | return status_ == kPause; 44 | } 45 | 46 | bool isStopped() { 47 | return status_ == kStopped; 48 | } 49 | 50 | private: 51 | std::atomic<KStatus> status_; 52 | }; 53 | 54 | } 55 | 56 | #endif // HV_STATUS_HPP_ 57 | -------------------------------------------------------------------------------- /evpp/TcpServer_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TcpServer_test.cpp 3 | * 4 | * @build make evpp 5 | * @server bin/TcpServer_test 1234 6 | * @client bin/TcpClient_test 1234 7 | * 8 | */ 9 | 10 | #include <iostream> 11 | 12 | #include "TcpServer.h" 13 | 14 | using namespace hv; 15 | 16 | #define TEST_TLS 0 17 | 18 | int main(int argc, char* argv[]) { 19 | if (argc < 2) { 20 | printf("Usage: %s port\n", argv[0]); 21 | return -10; 22 | } 23 | int port = atoi(argv[1]); 24 | 25 | hlog_set_level(LOG_LEVEL_DEBUG); 26 | 27 | TcpServer srv; 28 | int listenfd = srv.createsocket(port); 29 | if (listenfd < 0) { 30 | return -20; 31 | } 32 | printf("server listen on port %d, listenfd=%d ...\n", port, listenfd); 33 | srv.onConnection = [](const SocketChannelPtr& channel) { 34 | std::string peeraddr = channel->peeraddr(); 35 | if (channel->isConnected()) { 36 | printf("%s connected! connfd=%d id=%d tid=%ld\n", peeraddr.c_str(), channel->fd(), channel->id(), currentThreadEventLoop->tid()); 37 | } else { 38 | printf("%s disconnected! connfd=%d id=%d tid=%ld\n", peeraddr.c_str(), channel->fd(), channel->id(), currentThreadEventLoop->tid()); 39 | } 40 | }; 41 | srv.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) { 42 | // echo 43 | printf("< %.*s\n", (int)buf->size(), (char*)buf->data()); 44 | channel->write(buf); 45 | }; 46 | srv.setThreadNum(4); 47 | srv.setLoadBalance(LB_LeastConnections); 48 | 49 | #if TEST_TLS 50 | hssl_ctx_opt_t ssl_opt; 51 | memset(&ssl_opt, 0, sizeof(hssl_ctx_opt_t)); 52 | ssl_opt.crt_file = "cert/server.crt"; 53 | ssl_opt.key_file = "cert/server.key"; 54 | ssl_opt.verify_peer = 0; 55 | srv.withTLS(&ssl_opt); 56 | #endif 57 | 58 | srv.start(); 59 | 60 | std::string str; 61 | while (std::getline(std::cin, str)) { 62 | if (str == "close") { 63 | srv.closesocket(); 64 | } else if (str == "start") { 65 | srv.start(); 66 | } else if (str == "stop") { 67 | srv.stop(); 68 | break; 69 | } else { 70 | srv.broadcast(str.data(), str.size()); 71 | } 72 | } 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /evpp/TimerThread.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_TIMER_THREAD_HPP_ 2 | #define HV_TIMER_THREAD_HPP_ 3 | 4 | #include "EventLoopThread.h" 5 | 6 | namespace hv { 7 | 8 | class TimerThread : public EventLoopThread { 9 | public: 10 | std::atomic<TimerID> nextTimerID; 11 | TimerThread() : EventLoopThread() { 12 | nextTimerID = 0; 13 | start(); 14 | } 15 | 16 | virtual ~TimerThread() { 17 | stop(); 18 | join(); 19 | } 20 | 21 | public: 22 | // setTimer, setTimeout, killTimer, resetTimer thread-safe 23 | TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE) { 24 | TimerID timerID = ++nextTimerID; 25 | loop()->setTimerInLoop(timeout_ms, cb, repeat, timerID); 26 | return timerID; 27 | } 28 | // alias javascript setTimeout, setInterval 29 | TimerID setTimeout(int timeout_ms, TimerCallback cb) { 30 | return setTimer(timeout_ms, cb, 1); 31 | } 32 | TimerID setInterval(int interval_ms, TimerCallback cb) { 33 | return setTimer(interval_ms, cb, INFINITE); 34 | } 35 | 36 | void killTimer(TimerID timerID) { 37 | loop()->killTimer(timerID); 38 | } 39 | 40 | void resetTimer(TimerID timerID, int timeout_ms = 0) { 41 | loop()->resetTimer(timerID, timeout_ms); 42 | } 43 | }; 44 | 45 | } // end namespace hv 46 | 47 | #endif // HV_TIMER_THREAD_HPP_ 48 | -------------------------------------------------------------------------------- /evpp/TimerThread_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TimerThread_test.cpp 3 | * 4 | * @build: make evpp 5 | * 6 | */ 7 | 8 | #include "TimerThread.h" 9 | #include "singleton.h" 10 | 11 | namespace hv { 12 | 13 | class GlobalTimerThread : public TimerThread { 14 | SINGLETON_DECL(GlobalTimerThread) 15 | protected: 16 | GlobalTimerThread() : TimerThread() {} 17 | ~GlobalTimerThread() {} 18 | 19 | public: 20 | static TimerID setTimeout(int timeout_ms, TimerCallback cb) { 21 | return GlobalTimerThread::instance()->setTimer(timeout_ms, cb, 1); 22 | } 23 | 24 | static void clearTimeout(TimerID timerID) { 25 | GlobalTimerThread::instance()->killTimer(timerID); 26 | } 27 | 28 | static TimerID setInterval(int interval_ms, TimerCallback cb) { 29 | return GlobalTimerThread::instance()->setTimer(interval_ms, cb, INFINITE); 30 | } 31 | 32 | static void clearInterval(TimerID timerID) { 33 | GlobalTimerThread::instance()->killTimer(timerID); 34 | } 35 | }; 36 | 37 | SINGLETON_IMPL(GlobalTimerThread) 38 | 39 | } // end namespace hv 40 | 41 | int main(int argc, char* argv[]) { 42 | hv::GlobalTimerThread::setTimeout(3000, [](hv::TimerID timerID) { 43 | printf("setTimeout timerID=%lu time=%lus\n", (unsigned long)timerID, (unsigned long)time(NULL)); 44 | }); 45 | 46 | hv::GlobalTimerThread::setInterval(1000, [](hv::TimerID timerID) { 47 | printf("setInterval timerID=%lu time=%lus\n", (unsigned long)timerID, (unsigned long)time(NULL)); 48 | }); 49 | 50 | // press Enter to stop 51 | while (getchar() != '\n'); 52 | 53 | hv::GlobalTimerThread::exitInstance(); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /evpp/UdpClient_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * UdpClient_test.cpp 3 | * 4 | * @build make evpp 5 | * @server bin/UdpServer_test 1234 6 | * @client bin/UdpClient_test 1234 7 | * 8 | */ 9 | 10 | #include <iostream> 11 | 12 | #include "UdpClient.h" 13 | #include "htime.h" 14 | 15 | using namespace hv; 16 | 17 | int main(int argc, char* argv[]) { 18 | if (argc < 2) { 19 | printf("Usage: %s remote_port [remote_host]\n", argv[0]); 20 | return -10; 21 | } 22 | int remote_port = atoi(argv[1]); 23 | const char* remote_host = "127.0.0.1"; 24 | if (argc > 2) { 25 | remote_host = argv[2]; 26 | } 27 | 28 | UdpClient cli; 29 | int sockfd = cli.createsocket(remote_port, remote_host); 30 | if (sockfd < 0) { 31 | return -20; 32 | } 33 | printf("client sendto port %d, sockfd=%d ...\n", remote_port, sockfd); 34 | cli.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) { 35 | printf("< %.*s\n", (int)buf->size(), (char*)buf->data()); 36 | }; 37 | cli.start(); 38 | 39 | // sendto(time) every 3s 40 | cli.loop()->setInterval(3000, [&cli](TimerID timerID) { 41 | char str[DATETIME_FMT_BUFLEN] = {0}; 42 | datetime_t dt = datetime_now(); 43 | datetime_fmt(&dt, str); 44 | cli.sendto(str); 45 | }); 46 | 47 | std::string str; 48 | while (std::getline(std::cin, str)) { 49 | if (str == "close") { 50 | cli.closesocket(); 51 | } else if (str == "start") { 52 | cli.start(); 53 | } else if (str == "stop") { 54 | cli.stop(); 55 | break; 56 | } else { 57 | cli.sendto(str); 58 | } 59 | } 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /evpp/UdpServer_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * UdpServer_test.cpp 3 | * 4 | * @build make evpp 5 | * @server bin/UdpServer_test 1234 6 | * @client bin/UdpClient_test 1234 7 | * 8 | */ 9 | 10 | #include <iostream> 11 | 12 | #include "UdpServer.h" 13 | 14 | using namespace hv; 15 | 16 | int main(int argc, char* argv[]) { 17 | if (argc < 2) { 18 | printf("Usage: %s port\n", argv[0]); 19 | return -10; 20 | } 21 | int port = atoi(argv[1]); 22 | 23 | UdpServer srv; 24 | int bindfd = srv.createsocket(port); 25 | if (bindfd < 0) { 26 | return -20; 27 | } 28 | printf("server bind on port %d, bindfd=%d ...\n", port, bindfd); 29 | srv.onMessage = [](const SocketChannelPtr& channel, Buffer* buf) { 30 | // echo 31 | printf("< %.*s\n", (int)buf->size(), (char*)buf->data()); 32 | channel->write(buf); 33 | }; 34 | srv.start(); 35 | 36 | std::string str; 37 | while (std::getline(std::cin, str)) { 38 | if (str == "close") { 39 | srv.closesocket(); 40 | } else if (str == "start") { 41 | srv.start(); 42 | } else if (str == "stop") { 43 | srv.stop(); 44 | break; 45 | } else { 46 | srv.sendto(str); 47 | } 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /evpp/build_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(cd `dirname $0`; pwd) 4 | ROOT_DIR=${SCRIPT_DIR}/.. 5 | 6 | cd ${ROOT_DIR} 7 | make evpp 8 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ## 目录结构 2 | 3 | ``` 4 | . 5 | ├── consul/ consul服务注册与发现 6 | ├── httpd/ HTTP服务端 7 | ├── jsonrpc/ json RPC示例 8 | ├── kcptun/ kcp隧道 9 | ├── mqtt/ MQTT发布订阅示例 10 | ├── multi-thread/ 多线程网络编程示例 11 | ├── nmap/ 网络扫描工具 12 | ├── protorpc/ protobuf RPC示例 13 | ├── curl.cpp HTTP请求工具 14 | ├── hloop_test.c 事件循环测试代码 15 | ├── hmain_test.cpp 命令行程序示例代码 16 | ├── htimer_test.c 定时器测试代码 17 | ├── http_client_test.c HTTP客户端测试代码 18 | ├── http_server_test.c HTTP服务端测试代码 19 | ├── nc.c 网络连接工具 20 | ├── pipe_test.c pipe示例代码 21 | ├── socks5_proxy_server.c SOCKS5代理服务 22 | ├── tcp_chat_server.c TCP聊天服务 23 | ├── tcp_echo_server.c TCP回显服务 24 | ├── tcp_proxy_server.c TCP代理服务 25 | ├── tinyhttpd.c 微型HTTP服务 26 | ├── tinyproxyd.c 微型HTTP代理服务 27 | ├── udp_echo_server.c UDP回显服务 28 | ├── udp_proxy_server.c UDP代理服务 29 | ├── websocket_client_test.c WebSocket客户端测试代码 30 | ├── websocket_server_test.c WebSocket服务端测试代码 31 | ├── wget.cpp HTTP文件下载工具 32 | └── wrk.cpp HTTP压测工具 33 | 34 | ``` 35 | -------------------------------------------------------------------------------- /examples/consul/consul.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSUL_H_ 2 | #define CONSUL_H_ 3 | 4 | #include <vector> 5 | #include <string.h> 6 | 7 | typedef struct consul_node_s { 8 | // node 9 | char ip[64]; 10 | int port; 11 | 12 | consul_node_s() { 13 | strcpy(ip, "127.0.0.1"); 14 | port = 8500; 15 | } 16 | } consul_node_t; 17 | 18 | typedef struct consul_service_s { 19 | // service 20 | char name[64]; 21 | char ip[64]; 22 | int port; 23 | 24 | consul_service_s() { 25 | name[0] = '\0'; 26 | strcpy(ip, "127.0.0.1"); 27 | port = 0; 28 | } 29 | } consul_service_t; 30 | 31 | typedef struct consul_health_s { 32 | // check 33 | char protocol[32]; // TCP,HTTP 34 | char url[256]; 35 | char status[32]; // any,passing,warning,critical 36 | 37 | int interval; // ms 38 | int timeout; // ms 39 | 40 | consul_health_s() { 41 | strcpy(protocol, "TCP"); 42 | url[0] = '\0'; 43 | strcpy(status, "passing"); 44 | interval = 10000; 45 | timeout = 3000; 46 | } 47 | } consul_health_t; 48 | 49 | int register_service(consul_node_t* node, consul_service_t* service, consul_health_t* health); 50 | int deregister_service(consul_node_t* node, consul_service_t* service); 51 | int discover_services(consul_node_t* node, const char* service_name, std::vector<consul_service_t>& services); 52 | 53 | #endif // CONSUL_H_ 54 | -------------------------------------------------------------------------------- /examples/consul/main.cpp: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | #include <stdlib.h> 3 | #include <string.h> 4 | 5 | #include "consul.h" 6 | 7 | int main(int argc, char* argv[]) { 8 | if (argc < 3) { 9 | printf("Usage: consul_cli subcmd ServiceName [ServiceAddress ServicePort] [NodeIP NodePort]\n"); 10 | printf("subcmd=[register,deregister,discover]\n"); 11 | return -10; 12 | } 13 | const char* subcmd = argv[1]; 14 | const char* ServiceName = argv[2]; 15 | const char* ServiceAddress = "127.0.0.1"; 16 | int ServicePort = 0; 17 | const char* NodeIP = "127.0.0.1"; 18 | int NodePort = 8500; 19 | if (argc > 3) { 20 | ServiceAddress = argv[3]; 21 | } 22 | if (argc > 4) { 23 | ServicePort = atoi(argv[4]); 24 | } 25 | if (argc > 5) { 26 | NodeIP = argv[5]; 27 | } 28 | if (argc > 6) { 29 | NodePort = atoi(argv[6]); 30 | } 31 | 32 | consul_node_t node; 33 | strncpy(node.ip, NodeIP, sizeof(node.ip)); 34 | node.port = NodePort; 35 | 36 | consul_service_t service; 37 | strncpy(service.name, ServiceName, sizeof(service.name)); 38 | strncpy(service.ip, ServiceAddress, sizeof(service.ip)); 39 | service.port = ServicePort; 40 | 41 | consul_health_t health; 42 | 43 | if (strcmp(subcmd, "register") == 0) { 44 | int ret = register_service(&node, &service, &health); 45 | printf("register_service retval=%d\n", ret); 46 | goto discover; 47 | } 48 | else if (strcmp(subcmd, "deregister") == 0) { 49 | int ret = deregister_service(&node, &service); 50 | printf("deregister_service retval=%d\n", ret); 51 | goto discover; 52 | } 53 | else if (strcmp(subcmd, "discover") == 0) { 54 | discover: 55 | std::vector<consul_service_t> services; 56 | discover_services(&node, ServiceName, services); 57 | for (auto& service : services) { 58 | printf("name=%s ip=%s port=%d\n", service.name, service.ip, service.port); 59 | } 60 | } 61 | else { 62 | printf("subcmd error!\n"); 63 | return -20; 64 | } 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /examples/htimer_test.c: -------------------------------------------------------------------------------- 1 | #include "hloop.h" 2 | #include "hbase.h" 3 | 4 | void on_timer(htimer_t* timer) { 5 | printf("time=%llus on_timer\n", LLU(hloop_now(hevent_loop(timer)))); 6 | } 7 | 8 | // test htimer_add 9 | void on_timer_add(htimer_t* timer) { 10 | printf("time=%llus on_timer_add\n", LLU(hloop_now(hevent_loop(timer)))); 11 | htimer_add(hevent_loop(timer), on_timer_add, 1000, 1); 12 | } 13 | 14 | // test htimer_del 15 | void on_timer_del(htimer_t* timer) { 16 | printf("time=%llus on_timer_del\n", LLU(hloop_now(hevent_loop(timer)))); 17 | htimer_del(timer); 18 | } 19 | 20 | // test htimer_reset 21 | void on_timer_reset(htimer_t* timer) { 22 | printf("time=%llus on_timer_reset\n", LLU(hloop_now(hevent_loop(timer)))); 23 | htimer_reset((htimer_t*)hevent_userdata(timer), 0); 24 | } 25 | 26 | // test hloop_stop 27 | void on_timer_quit(htimer_t* timer) { 28 | printf("time=%llus on_timer_quit\n", LLU(hloop_now(hevent_loop(timer)))); 29 | hloop_stop(hevent_loop(timer)); 30 | } 31 | 32 | // test cron 33 | void cron_hourly(htimer_t* timer) { 34 | time_t tt = time(NULL); 35 | printf("time=%llus cron_hourly: %s\n", LLU(hloop_now(hevent_loop(timer))), ctime(&tt)); 36 | } 37 | 38 | int main() { 39 | HV_MEMCHECK; 40 | hloop_t* loop = hloop_new(0); 41 | 42 | htimer_add(loop, on_timer_add, 1000, 1); 43 | htimer_add(loop, on_timer_del, 1000, 10); 44 | htimer_t* reseted = htimer_add(loop, on_timer, 5000, 1); 45 | htimer_t* reset = htimer_add(loop, on_timer_reset, 1000, 5); 46 | hevent_set_userdata(reset, reseted); 47 | 48 | // cron_hourly next triggered in one minute 49 | int minute = time(NULL)%3600/60; 50 | htimer_add_period(loop, cron_hourly, minute+1, -1, -1, -1, -1, INFINITE); 51 | 52 | // quit application after 1 min 53 | htimer_add(loop, on_timer_quit, 60000, 1); 54 | 55 | printf("time=%llus begin\n", LLU(hloop_now(loop))); 56 | hloop_run(loop); 57 | printf("time=%llus stop\n", LLU(hloop_now(loop))); 58 | hloop_free(&loop); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /examples/httpd/handler.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_HTTPD_HANDLER_H 2 | #define HV_HTTPD_HANDLER_H 3 | 4 | #include "HttpService.h" 5 | 6 | class Handler { 7 | public: 8 | // headerHandler => preprocessor => middleware -> handlers => postprocessor 9 | static int headerHandler(HttpRequest* req, HttpResponse* resp); 10 | static int preprocessor(HttpRequest* req, HttpResponse* resp); 11 | static int postprocessor(HttpRequest* req, HttpResponse* resp); 12 | static int errorHandler(const HttpContextPtr& ctx); 13 | 14 | // middleware 15 | static int Authorization(HttpRequest* req, HttpResponse* resp); 16 | 17 | static int sleep(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer); 18 | static int setTimeout(const HttpContextPtr& ctx); 19 | static int query(const HttpContextPtr& ctx); 20 | 21 | static int kv(HttpRequest* req, HttpResponse* resp); 22 | static int json(HttpRequest* req, HttpResponse* resp); 23 | static int form(HttpRequest* req, HttpResponse* resp); 24 | static int grpc(HttpRequest* req, HttpResponse* resp); 25 | 26 | static int test(const HttpContextPtr& ctx); 27 | static int restful(const HttpContextPtr& ctx); 28 | 29 | static int login(const HttpContextPtr& ctx); 30 | static int upload(const HttpContextPtr& ctx); 31 | // SSE: Server Send Events 32 | static int sse(const HttpContextPtr& ctx); 33 | 34 | // LargeFile 35 | static int sendLargeFile(const HttpContextPtr& ctx); 36 | static int recvLargeFile(const HttpContextPtr& ctx, http_parser_state state, const char* data, size_t size); 37 | 38 | private: 39 | static int response_status(HttpResponse* resp, int code = 200, const char* message = NULL) { 40 | if (message == NULL) message = http_status_str((enum http_status)code); 41 | resp->Set("code", code); 42 | resp->Set("message", message); 43 | return code; 44 | } 45 | static int response_status(const HttpResponseWriterPtr& writer, int code = 200, const char* message = NULL) { 46 | response_status(writer->response.get(), code, message); 47 | writer->End(); 48 | return code; 49 | } 50 | static int response_status(const HttpContextPtr& ctx, int code = 200, const char* message = NULL) { 51 | response_status(ctx->response.get(), code, message); 52 | ctx->send(); 53 | return code; 54 | } 55 | }; 56 | 57 | #endif // HV_HTTPD_HANDLER_H 58 | -------------------------------------------------------------------------------- /examples/httpd/router.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_HTTPD_ROUTER_H 2 | #define HV_HTTPD_ROUTER_H 3 | 4 | #include "HttpService.h" 5 | 6 | class Router { 7 | public: 8 | static void Register(hv::HttpService& router); 9 | }; 10 | 11 | #endif // HV_HTTPD_ROUTER_H 12 | -------------------------------------------------------------------------------- /examples/jsonrpc/router.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_JSON_RPC_ROUTER_H_ 2 | #define HV_JSON_RPC_ROUTER_H_ 3 | 4 | #include "cJSON.h" 5 | 6 | typedef void (*jsonrpc_handler)(cJSON* jreq, cJSON* jres); 7 | 8 | typedef struct { 9 | const char* method; 10 | jsonrpc_handler handler; 11 | } jsonrpc_router; 12 | 13 | void error_response(cJSON* jres, int code, const char* message); 14 | void not_found(cJSON* jreq, cJSON* jres); 15 | void bad_request(cJSON* jreq, cJSON* jres); 16 | 17 | void calc_add(cJSON* jreq, cJSON* jres); 18 | void calc_sub(cJSON* jreq, cJSON* jres); 19 | void calc_mul(cJSON* jreq, cJSON* jres); 20 | void calc_div(cJSON* jreq, cJSON* jres); 21 | 22 | #endif // HV_JSON_RPC_ROUTER_H_ 23 | -------------------------------------------------------------------------------- /examples/kcptun/README.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | <img src="kcptun.png" alt="kcptun" height="300px"/> 4 | 5 | > *Disclaimer: The picture comes from [github.com/xtaci/kcptun](https://github.com/xtaci/kcptun). Thanks so much.* 6 | 7 | # Build 8 | 9 | ```shell 10 | ./configure --with-kcp 11 | make clean 12 | make examples 13 | make kcptun 14 | ``` 15 | 16 | # Usage 17 | 18 | ```shell 19 | $ bin/kcptun_server -h 20 | 21 | Usage: kcptun_server [hvdl:t:m:] 22 | Options: 23 | 24 | -h|--help Print this information 25 | -v|--version Print version 26 | -d|--daemon Daemonize 27 | -l|--listen value kcp server listen address (default: ":4000") 28 | -t|--target value target server address (default: "127.0.0.1:8080") 29 | -m|--mode value profiles: fast3, fast2, fast, normal (default: "fast") 30 | --mtu value set maximum transmission unit for UDP packets (default: 1350) 31 | --sndwnd value set send window size(num of packets) (default: 1024) 32 | --rcvwnd value set receive window size(num of packets) (default: 1024) 33 | ``` 34 | 35 | ```shell 36 | $ bin/kcptun_client -h 37 | 38 | Usage: kcptun_client [hvdl:r:m:] 39 | Options: 40 | 41 | -h|--help Print this information 42 | -v|--version Print version 43 | -d|--daemon Daemonize 44 | -l|--localaddr value local listen address (default: ":8388") 45 | -r|--remoteaddr value kcp server address (default: "127.0.0.1:4000") 46 | -m|--mode value profiles: fast3, fast2, fast, normal (default: "fast") 47 | --mtu value set maximum transmission unit for UDP packets (default: 1350) 48 | --sndwnd value set send window size(num of packets) (default: 128) 49 | --rcvwnd value set receive window size(num of packets) (default: 512) 50 | ``` 51 | 52 | # Test 53 | `tcp_client -> kcptun_client -> kcptun_server -> tcp_server` 54 | ```shell 55 | tcp_server: bin/tcp_echo_server 1234 56 | kcptun_server: bin/kcptun_server -l :4000 -t 127.0.0.1:1234 --mode fast3 57 | kcptun_client: bin/kcptun_client -l :8388 -r 127.0.0.1:4000 --mode fast3 58 | tcp_client: bin/nc 127.0.0.1 8388 59 | > hello 60 | < hello 61 | ``` 62 | 63 | This kcptun examples does not implement encryption, compression, and fec.<br> 64 | if you want to use [github.com/xtaci/kcptun](https://github.com/xtaci/kcptun), please add `--crypt null --nocomp --ds 0 --ps 0`.<br> 65 | For example: 66 | ```shell 67 | golang_kcptun_server -l :4000 -t 127.0.0.1:1234 --mode fast3 --crypt null --nocomp --ds 0 --ps 0 68 | ``` 69 | -------------------------------------------------------------------------------- /examples/kcptun/kcptun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ithewei/libhv/1778d4a155b43bc3dee1cd6422afe1d7f5fe5ebc/examples/kcptun/kcptun.png -------------------------------------------------------------------------------- /examples/kcptun/smux/smux.cpp: -------------------------------------------------------------------------------- 1 | #include "smux.h" 2 | 3 | #define SMUX_USE_LITTLE_ENDIAN 1 4 | 5 | int smux_frame_pack(const smux_frame_t* frame, void* buf, int len) { 6 | if (!frame || !buf || !len) return -1; 7 | const smux_head_t* head = &(frame->head); 8 | unsigned int packlen = smux_package_length(head); 9 | // Check is buffer enough 10 | if (len < packlen) { 11 | return -2; 12 | } 13 | unsigned char* p = (unsigned char*)buf; 14 | *p++ = head->version; 15 | *p++ = head->cmd; 16 | #if SMUX_USE_LITTLE_ENDIAN 17 | *p++ = head->length; 18 | *p++ = (head->length >> 8) & 0xFF; 19 | #else 20 | // hton length 21 | *p++ = (head->length >> 8) & 0xFF; 22 | *p++ = head->length; 23 | #endif 24 | 25 | uint32_t sid = head->sid; 26 | #if SMUX_USE_LITTLE_ENDIAN 27 | *p++ = sid & 0xFF; 28 | *p++ = (sid >> 8) & 0xFF; 29 | *p++ = (sid >> 16) & 0xFF; 30 | *p++ = (sid >> 24) & 0xFF; 31 | #else 32 | // hton sid 33 | *p++ = (sid >> 24) & 0xFF; 34 | *p++ = (sid >> 16) & 0xFF; 35 | *p++ = (sid >> 8) & 0xFF; 36 | *p++ = sid & 0xFF; 37 | #endif 38 | // memcpy data 39 | if (frame->data && head->length) { 40 | memcpy(p, frame->data, frame->head.length); 41 | } 42 | return packlen; 43 | } 44 | 45 | int smux_frame_unpack(smux_frame_t* frame, const void* buf, int len) { 46 | if (!frame || !buf || !len) return -1; 47 | if (len < SMUX_HEAD_LENGTH) return -2; 48 | smux_head_t* head = &(frame->head); 49 | unsigned char* p = (unsigned char*)buf; 50 | head->version = *p++; 51 | head->cmd = *p++; 52 | #if SMUX_USE_LITTLE_ENDIAN 53 | head->length = *p++; 54 | head->length |= ((uint16_t)*p++) << 8; 55 | #else 56 | // ntoh length 57 | head->length = ((uint16_t)*p++) << 8; 58 | head->length |= *p++; 59 | #endif 60 | 61 | #if SMUX_USE_LITTLE_ENDIAN 62 | head->sid = *p++; 63 | head->sid |= ((uint32_t)*p++) << 8; 64 | head->sid |= ((uint32_t)*p++) << 16; 65 | head->sid |= ((uint32_t)*p++) << 24; 66 | #else 67 | // ntoh sid 68 | head->sid = ((uint32_t)*p++) << 24; 69 | head->sid |= ((uint32_t)*p++) << 16; 70 | head->sid |= ((uint32_t)*p++) << 8; 71 | head->sid |= *p++; 72 | #endif 73 | // NOTE: just shadow copy 74 | if (len > SMUX_HEAD_LENGTH) { 75 | frame->data = (const char*)buf + SMUX_HEAD_LENGTH; 76 | } 77 | unsigned int packlen = smux_package_length(head); 78 | return MIN(len, packlen); 79 | } 80 | -------------------------------------------------------------------------------- /examples/mqtt/mqtt_client_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * mqtt client 3 | * 4 | * @build make examples 5 | * 6 | * @test bin/mqtt_client_test 127.0.0.1 1883 topic payload 7 | * 8 | */ 9 | 10 | #include "mqtt_client.h" 11 | using namespace hv; 12 | 13 | /* 14 | * @test MQTTS 15 | * #define TEST_SSL 1 16 | * 17 | * @build ./configure --with-mqtt --with-openssl && make clean && make 18 | * 19 | */ 20 | #define TEST_SSL 0 21 | #define TEST_AUTH 0 22 | #define TEST_RECONNECT 1 23 | #define TEST_QOS 0 24 | 25 | int main(int argc, char** argv) { 26 | if (argc < 5) { 27 | printf("Usage: %s host port topic payload\n", argv[0]); 28 | return -10; 29 | } 30 | const char* host = argv[1]; 31 | int port = atoi(argv[2]); 32 | const char* topic = argv[3]; 33 | const char* payload = argv[4]; 34 | 35 | MqttClient cli; 36 | 37 | cli.onConnect = [topic, payload](MqttClient* cli) { 38 | printf("connected!\n"); 39 | #if TEST_QOS 40 | cli->subscribe(topic, 1, [topic, payload](MqttClient* cli) { 41 | printf("subscribe OK!\n"); 42 | cli->publish(topic, payload, 1, 0, [](MqttClient* cli) { 43 | printf("publish OK!\n"); 44 | }); 45 | }); 46 | #else 47 | cli->subscribe(topic); 48 | cli->publish(topic, payload); 49 | #endif 50 | }; 51 | 52 | cli.onMessage = [](MqttClient* cli, mqtt_message_t* msg) { 53 | printf("topic: %.*s\n", msg->topic_len, msg->topic); 54 | printf("payload: %.*s\n", msg->payload_len, msg->payload); 55 | cli->disconnect(); 56 | cli->stop(); 57 | }; 58 | 59 | cli.onClose = [](MqttClient* cli) { 60 | printf("disconnected!\n"); 61 | }; 62 | 63 | #if TEST_AUTH 64 | cli.setAuth("test", "123456"); 65 | #endif 66 | 67 | #if TEST_RECONNECT 68 | reconn_setting_t reconn; 69 | reconn_setting_init(&reconn); 70 | reconn.min_delay = 1000; 71 | reconn.max_delay = 10000; 72 | reconn.delay_policy = 2; 73 | cli.setReconnect(&reconn); 74 | #endif 75 | 76 | cli.setPingInterval(10); 77 | 78 | int ssl = 0; 79 | #if TEST_SSL 80 | ssl = 1; 81 | #endif 82 | cli.connect(host, port, ssl); 83 | cli.run(); 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /examples/nmap/nmap.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_NMAP_H_ 2 | #define HV_NMAP_H_ 3 | 4 | #include <stdint.h> 5 | #include <map> 6 | 7 | // addr => 0:down 1:up 8 | typedef std::map<uint32_t, int> Nmap; 9 | 10 | // ip = segment + host 11 | // segment16: 192.168.x.x 12 | // segment24: 192.168.1.x 13 | 14 | // @return up_cnt 15 | int nmap_discover(Nmap* nmap); 16 | int segment_discover(const char* segment16, Nmap* nmap); 17 | int host_discover(const char* segment24, Nmap* nmap); 18 | 19 | #endif // HV_NMAP_H_ 20 | -------------------------------------------------------------------------------- /examples/pipe_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hio_create_pipe test 3 | * 4 | * @build make examples 5 | * @test bin/pipe_test 6 | * 7 | */ 8 | 9 | #include "hloop.h" 10 | #include "htime.h" 11 | 12 | static hio_t* pipeio[2] = { NULL, NULL }; 13 | 14 | static void on_read(hio_t* io, void* buf, int readbytes) { 15 | printf("< %.*s\n", readbytes, (char*)buf); 16 | } 17 | 18 | static void on_timer_write(htimer_t* timer) { 19 | char str[DATETIME_FMT_BUFLEN] = {0}; 20 | datetime_t dt = datetime_now(); 21 | datetime_fmt(&dt, str); 22 | hio_write(pipeio[1], str, strlen(str)); 23 | } 24 | 25 | static void on_timer_stop(htimer_t* timer) { 26 | hio_close(pipeio[0]); 27 | hio_close(pipeio[1]); 28 | hloop_stop(hevent_loop(timer)); 29 | } 30 | 31 | int main(int argc, char** argv) { 32 | hloop_t* loop = hloop_new(0); 33 | 34 | int ret = hio_create_pipe(loop, pipeio); 35 | if (ret != 0) { 36 | printf("hio_create_pipe failed!\n"); 37 | return -10; 38 | } 39 | printf("pipefd %d<=>%d\n", hio_fd(pipeio[0]), hio_fd(pipeio[1])); 40 | 41 | hio_setcb_read(pipeio[0], on_read); 42 | hio_read(pipeio[0]); 43 | 44 | htimer_add(loop, on_timer_write, 1000, INFINITE); 45 | 46 | htimer_add(loop, on_timer_stop, 10000, 1); 47 | 48 | hloop_run(loop); 49 | hloop_free(&loop); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /examples/protorpc/handler/calc.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_PROTO_RPC_HANDLER_CALC_H_ 2 | #define HV_PROTO_RPC_HANDLER_CALC_H_ 3 | 4 | #include "../router.h" 5 | 6 | #include "../generated/calc.pb.h" 7 | 8 | void calc_add(const protorpc::Request& req, protorpc::Response* res) { 9 | // params 10 | if (req.params_size() != 2) { 11 | return bad_request(req, res); 12 | } 13 | protorpc::CalcParam param1, param2; 14 | if (!param1.ParseFromString(req.params(0)) || 15 | !param2.ParseFromString(req.params(1))) { 16 | return bad_request(req, res); 17 | } 18 | 19 | // result 20 | protorpc::CalcResult result; 21 | result.set_num(param1.num() + param2.num()); 22 | res->set_result(result.SerializeAsString()); 23 | } 24 | 25 | void calc_sub(const protorpc::Request& req, protorpc::Response* res) { 26 | // params 27 | if (req.params_size() != 2) { 28 | return bad_request(req, res); 29 | } 30 | protorpc::CalcParam param1, param2; 31 | if (!param1.ParseFromString(req.params(0)) || 32 | !param2.ParseFromString(req.params(1))) { 33 | return bad_request(req, res); 34 | } 35 | 36 | // result 37 | protorpc::CalcResult result; 38 | result.set_num(param1.num() - param2.num()); 39 | res->set_result(result.SerializeAsString()); 40 | } 41 | 42 | void calc_mul(const protorpc::Request& req, protorpc::Response* res) { 43 | // params 44 | if (req.params_size() != 2) { 45 | return bad_request(req, res); 46 | } 47 | protorpc::CalcParam param1, param2; 48 | if (!param1.ParseFromString(req.params(0)) || 49 | !param2.ParseFromString(req.params(1))) { 50 | return bad_request(req, res); 51 | } 52 | 53 | // result 54 | protorpc::CalcResult result; 55 | result.set_num(param1.num() * param2.num()); 56 | res->set_result(result.SerializeAsString()); 57 | } 58 | 59 | void calc_div(const protorpc::Request& req, protorpc::Response* res) { 60 | // params 61 | if (req.params_size() != 2) { 62 | return bad_request(req, res); 63 | } 64 | protorpc::CalcParam param1, param2; 65 | if (!param1.ParseFromString(req.params(0)) || 66 | !param2.ParseFromString(req.params(1))) { 67 | return bad_request(req, res); 68 | } 69 | if (param2.num() == 0) { 70 | return bad_request(req, res); 71 | } 72 | 73 | // result 74 | protorpc::CalcResult result; 75 | result.set_num(param1.num() / param2.num()); 76 | res->set_result(result.SerializeAsString()); 77 | } 78 | 79 | #endif // HV_PROTO_RPC_HANDLER_CALC_H_ 80 | -------------------------------------------------------------------------------- /examples/protorpc/handler/handler.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_PROTO_RPC_HANDLER_H_ 2 | #define HV_PROTO_RPC_HANDLER_H_ 3 | 4 | #include "../router.h" 5 | 6 | void error_response(protorpc::Response* res, int code, const std::string& message) { 7 | res->mutable_error()->set_code(code); 8 | res->mutable_error()->set_message(message); 9 | } 10 | 11 | void not_found(const protorpc::Request& req, protorpc::Response* res) { 12 | error_response(res, 404, "Not Found"); 13 | } 14 | 15 | void bad_request(const protorpc::Request& req, protorpc::Response* res) { 16 | error_response(res, 400, "Bad Request"); 17 | } 18 | 19 | #endif // HV_PROTO_RPC_HANDLER_H_ 20 | -------------------------------------------------------------------------------- /examples/protorpc/handler/login.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_PROTO_RPC_HANDLER_LOGIN_H_ 2 | #define HV_PROTO_RPC_HANDLER_LOGIN_H_ 3 | 4 | #include "../router.h" 5 | 6 | #include "../generated/login.pb.h" 7 | 8 | void login(const protorpc::Request& req, protorpc::Response* res) { 9 | // params 10 | if (req.params_size() == 0) { 11 | return bad_request(req, res); 12 | } 13 | protorpc::LoginParam param; 14 | if (!param.ParseFromString(req.params(0))) { 15 | return bad_request(req, res); 16 | } 17 | 18 | // result 19 | protorpc::LoginResult result; 20 | result.set_user_id(123456); 21 | result.set_token(param.username() + ":" + param.password()); 22 | res->set_result(result.SerializeAsString()); 23 | } 24 | 25 | #endif // HV_PROTO_RPC_HANDLER_LOGIN_H_ 26 | -------------------------------------------------------------------------------- /examples/protorpc/proto/base.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protorpc; 4 | 5 | message Error { 6 | int32 code = 1; 7 | string message = 2; 8 | } 9 | 10 | message Request { 11 | uint64 id = 1; 12 | string method = 2; 13 | repeated bytes params = 3; 14 | } 15 | 16 | message Response { 17 | uint64 id = 1; 18 | bytes result = 2; 19 | Error error = 3; 20 | } 21 | -------------------------------------------------------------------------------- /examples/protorpc/proto/calc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protorpc; 4 | 5 | message CalcParam { 6 | int64 num = 1; 7 | } 8 | 9 | message CalcResult { 10 | int64 num = 1; 11 | } 12 | -------------------------------------------------------------------------------- /examples/protorpc/proto/login.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package protorpc; 4 | 5 | message LoginParam { 6 | string username = 1; 7 | string password = 2; 8 | } 9 | 10 | message LoginResult { 11 | uint64 user_id = 1; 12 | string token = 2; 13 | } 14 | -------------------------------------------------------------------------------- /examples/protorpc/proto/protoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd `dirname $0` 4 | 5 | PROTOC=`which protoc` 6 | if [ $? -ne 0 ]; then 7 | echo "Not found command protoc!" 8 | echo "Please install libprotobuf first!" 9 | exit 1 10 | fi 11 | 12 | CPP_OUT_DIR=../generated 13 | if [ ! -d "${CPP_OUT_DIR}" ]; then 14 | mkdir -p ${CPP_OUT_DIR} 15 | fi 16 | 17 | set -x 18 | ${PROTOC} --cpp_out=${CPP_OUT_DIR} *.proto 19 | -------------------------------------------------------------------------------- /examples/protorpc/protorpc.c: -------------------------------------------------------------------------------- 1 | #include "protorpc.h" 2 | 3 | #include <string.h> // import memcpy 4 | 5 | int protorpc_pack(const protorpc_message* msg, void* buf, int len) { 6 | if (!msg || !buf || !len) return -1; 7 | const protorpc_head* head = &(msg->head); 8 | unsigned int packlen = protorpc_package_length(head); 9 | // Check is buffer enough 10 | if (len < packlen) { 11 | return -2; 12 | } 13 | unsigned char* p = (unsigned char*)buf; 14 | *p++ = head->protocol[0]; 15 | *p++ = head->protocol[1]; 16 | *p++ = head->protocol[2]; 17 | *p++ = head->protocol[3]; 18 | *p++ = head->version; 19 | *p++ = head->flags; 20 | *p++ = head->reserved[0]; 21 | *p++ = head->reserved[1]; 22 | // hton length 23 | unsigned int length = head->length; 24 | *p++ = (length >> 24) & 0xFF; 25 | *p++ = (length >> 16) & 0xFF; 26 | *p++ = (length >> 8) & 0xFF; 27 | *p++ = length & 0xFF; 28 | // memcpy body 29 | if (msg->body && head->length) { 30 | memcpy(p, msg->body, head->length); 31 | } 32 | 33 | return packlen; 34 | } 35 | 36 | int protorpc_unpack(protorpc_message* msg, const void* buf, int len) { 37 | if (!msg || !buf || !len) return -1; 38 | if (len < PROTORPC_HEAD_LENGTH) return -2; 39 | protorpc_head* head = &(msg->head); 40 | const unsigned char* p = (const unsigned char*)buf; 41 | head->protocol[0] = *p++; 42 | head->protocol[1] = *p++; 43 | head->protocol[2] = *p++; 44 | head->protocol[3] = *p++; 45 | head->version = *p++; 46 | head->flags = *p++; 47 | head->reserved[0] = *p++; 48 | head->reserved[1] = *p++; 49 | // ntoh length 50 | head->length = ((unsigned int)*p++) << 24; 51 | head->length |= ((unsigned int)*p++) << 16; 52 | head->length |= ((unsigned int)*p++) << 8; 53 | head->length |= *p++; 54 | // Check is buffer enough 55 | unsigned int packlen = protorpc_package_length(head); 56 | if (len < packlen) { 57 | return -3; 58 | } 59 | // NOTE: just shadow copy 60 | if (len > PROTORPC_HEAD_LENGTH) { 61 | msg->body = (const char*)buf + PROTORPC_HEAD_LENGTH; 62 | } 63 | 64 | return packlen; 65 | } 66 | -------------------------------------------------------------------------------- /examples/protorpc/protorpc.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_PROTO_RPC_H_ 2 | #define HV_PROTO_RPC_H_ 3 | 4 | #include <string.h> 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define PROTORPC_NAME "HRPC" 11 | #define PROTORPC_VERSION 1 12 | 13 | // protocol:4bytes + version:1byte + flags:1byte + reserved:2bytes + length:4bytes = 12bytes 14 | #define PROTORPC_HEAD_LENGTH 12 15 | #define PROTORPC_HEAD_LENGTH_FIELD_OFFSET 8 16 | #define PROTORPC_HEAD_LENGTH_FIELD_BYTES 4 17 | typedef struct { 18 | unsigned char protocol[4]; 19 | unsigned char version; 20 | unsigned char flags; 21 | unsigned char reserved[2]; 22 | unsigned int length; 23 | } protorpc_head; 24 | 25 | typedef const char* protorpc_body; 26 | 27 | typedef struct { 28 | protorpc_head head; 29 | protorpc_body body; 30 | } protorpc_message; 31 | 32 | static inline unsigned int protorpc_package_length(const protorpc_head* head) { 33 | return PROTORPC_HEAD_LENGTH + head->length; 34 | } 35 | 36 | static inline void protorpc_head_init(protorpc_head* head) { 37 | // protocol = HRPC 38 | memcpy(head->protocol, PROTORPC_NAME, 4); 39 | head->version = PROTORPC_VERSION; 40 | head->reserved[0] = head->reserved[1] = 0; 41 | head->length = 0; 42 | } 43 | 44 | static inline void protorpc_message_init(protorpc_message* msg) { 45 | protorpc_head_init(&msg->head); 46 | msg->body = NULL; 47 | } 48 | 49 | static inline int protorpc_head_check(protorpc_head* head) { 50 | if (memcmp(head->protocol, PROTORPC_NAME, 4) != 0) { 51 | return -1; 52 | } 53 | if (head->version != PROTORPC_VERSION) { 54 | return -2; 55 | } 56 | return 0; 57 | } 58 | 59 | // @retval >0 package_length, <0 error 60 | int protorpc_pack(const protorpc_message* msg, void* buf, int len); 61 | // @retval >0 package_length, <0 error 62 | int protorpc_unpack(protorpc_message* msg, const void* buf, int len); 63 | 64 | #ifdef __cplusplus 65 | } // extern "C" 66 | #endif 67 | 68 | #endif // HV_PROTO_RPC_H_ 69 | -------------------------------------------------------------------------------- /examples/protorpc/router.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_PROTO_RPC_ROUTER_H_ 2 | #define HV_PROTO_RPC_ROUTER_H_ 3 | 4 | #include "generated/base.pb.h" 5 | 6 | typedef void (*protorpc_handler)(const protorpc::Request& req, protorpc::Response* res); 7 | 8 | typedef struct { 9 | const char* method; 10 | protorpc_handler handler; 11 | } protorpc_router; 12 | 13 | void error_response(protorpc::Response* res, int code, const std::string& message); 14 | void not_found(const protorpc::Request& req, protorpc::Response* res); 15 | void bad_request(const protorpc::Request& req, protorpc::Response* res); 16 | 17 | void calc_add(const protorpc::Request& req, protorpc::Response* res); 18 | void calc_sub(const protorpc::Request& req, protorpc::Response* res); 19 | void calc_mul(const protorpc::Request& req, protorpc::Response* res); 20 | void calc_div(const protorpc::Request& req, protorpc::Response* res); 21 | 22 | void login(const protorpc::Request& req, protorpc::Response* res); 23 | 24 | #endif // HV_PROTO_RPC_ROUTER_H_ 25 | -------------------------------------------------------------------------------- /examples/udp_echo_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * udp echo server 3 | * 4 | * @build make examples 5 | * @server bin/udp_echo_server 1234 6 | * @client bin/nc -u 127.0.0.1 1234 7 | * nc -u 127.0.0.1 1234 8 | * 9 | */ 10 | 11 | #include "hloop.h" 12 | #include "hsocket.h" 13 | 14 | /* 15 | * @test kcp_server 16 | * #define TEST_KCP 1 17 | * 18 | * @build ./configure --with-kcp && make clean && make 19 | * @server bin/udp_echo_server 1234 20 | * @client bin/nc -k 127.0.0.1 1234 21 | * 22 | */ 23 | #define TEST_KCP 0 24 | 25 | static void on_recvfrom(hio_t* io, void* buf, int readbytes) { 26 | printf("on_recvfrom fd=%d readbytes=%d\n", hio_fd(io), readbytes); 27 | char localaddrstr[SOCKADDR_STRLEN] = {0}; 28 | char peeraddrstr[SOCKADDR_STRLEN] = {0}; 29 | printf("[%s] <=> [%s]\n", 30 | SOCKADDR_STR(hio_localaddr(io), localaddrstr), 31 | SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); 32 | 33 | char* str = (char*)buf; 34 | printf("< %.*s", readbytes, str); 35 | // echo 36 | printf("> %.*s", readbytes, str); 37 | hio_write(io, buf, readbytes); 38 | 39 | #if TEST_KCP 40 | if (strncmp(str, "CLOSE", 5) == 0) { 41 | hio_close_rudp(io, hio_peeraddr(io)); 42 | } 43 | #endif 44 | } 45 | 46 | int main(int argc, char** argv) { 47 | if (argc < 2) { 48 | printf("Usage: %s port|path\n", argv[0]); 49 | return -10; 50 | } 51 | const char* host = "0.0.0.0"; 52 | int port = atoi(argv[1]); 53 | #if ENABLE_UDS 54 | if (port == 0) { 55 | host = argv[1]; 56 | port = -1; 57 | } 58 | #endif 59 | 60 | hloop_t* loop = hloop_new(0); 61 | hio_t* io = hloop_create_udp_server(loop, host, port); 62 | if (io == NULL) { 63 | return -20; 64 | } 65 | #if TEST_KCP 66 | hio_set_kcp(io, NULL); 67 | #endif 68 | hio_setcb_read(io, on_recvfrom); 69 | hio_read(io); 70 | hloop_run(loop); 71 | hloop_free(&loop); 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /examples/udp_proxy_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * udp proxy server 3 | * 4 | * @build: make examples 5 | * @udp_server: bin/udp_echo_server 1234 6 | * @proxy_server: bin/udp_proxy_server 2222 127.0.0.1:1234 7 | * @client: bin/nc -u 127.0.0.1 2222 8 | * > hello 9 | * < hello 10 | */ 11 | 12 | #include "hloop.h" 13 | 14 | static char proxy_host[64] = "0.0.0.0"; 15 | static int proxy_port = 1080; 16 | 17 | static char backend_host[64] = "127.0.0.1"; 18 | static int backend_port = 80; 19 | 20 | // hloop_create_udp_server -> hio_setup_udp_upstream 21 | 22 | int main(int argc, char** argv) { 23 | if (argc < 3) { 24 | printf("Usage: %s proxy_port backend_host:backend_port\n", argv[0]); 25 | return -10; 26 | } 27 | proxy_port = atoi(argv[1]); 28 | char* pos = strchr(argv[2], ':'); 29 | if (pos) { 30 | int len = pos - argv[2]; 31 | if (len > 0) { 32 | memcpy(backend_host, argv[2], len); 33 | backend_host[len] = '\0'; 34 | } 35 | backend_port = atoi(pos + 1); 36 | } else { 37 | strncpy(backend_host, argv[2], sizeof(backend_host)); 38 | } 39 | printf("%s:%d proxy %s:%d\n", proxy_host, proxy_port, backend_host, backend_port); 40 | 41 | hloop_t* loop = hloop_new(0); 42 | hio_t* io = hloop_create_udp_server(loop, proxy_host, proxy_port); 43 | if (io == NULL) { 44 | return -20; 45 | } 46 | hio_t* upstream_io = hio_setup_udp_upstream(io, backend_host, backend_port); 47 | if (upstream_io == NULL) { 48 | return -30; 49 | } 50 | hloop_run(loop); 51 | hloop_free(&loop); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /hconfig.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_CONFIG_H_ 2 | #define HV_CONFIG_H_ 3 | 4 | #ifndef HAVE_STDBOOL_H 5 | #define HAVE_STDBOOL_H 1 6 | #endif 7 | 8 | #ifndef HAVE_STDINT_H 9 | #define HAVE_STDINT_H 1 10 | #endif 11 | 12 | #ifndef HAVE_STDATOMIC_H 13 | #define HAVE_STDATOMIC_H 0 14 | #endif 15 | 16 | #ifndef HAVE_SYS_TYPES_H 17 | #define HAVE_SYS_TYPES_H 1 18 | #endif 19 | 20 | #ifndef HAVE_SYS_STAT_H 21 | #define HAVE_SYS_STAT_H 1 22 | #endif 23 | 24 | #ifndef HAVE_SYS_TIME_H 25 | #define HAVE_SYS_TIME_H 1 26 | #endif 27 | 28 | #ifndef HAVE_FCNTL_H 29 | #define HAVE_FCNTL_H 1 30 | #endif 31 | 32 | #ifndef HAVE_PTHREAD_H 33 | #define HAVE_PTHREAD_H 1 34 | #endif 35 | 36 | #ifndef HAVE_ENDIAN_H 37 | #define HAVE_ENDIAN_H 1 38 | #endif 39 | 40 | #ifndef HAVE_SYS_ENDIAN_H 41 | #define HAVE_SYS_ENDIAN_H 0 42 | #endif 43 | 44 | #ifndef HAVE_GETTID 45 | #define HAVE_GETTID 0 46 | #endif 47 | 48 | #ifndef HAVE_STRLCPY 49 | #define HAVE_STRLCPY 1 50 | #endif 51 | 52 | #ifndef HAVE_STRLCAT 53 | #define HAVE_STRLCAT 1 54 | #endif 55 | 56 | #ifndef HAVE_CLOCK_GETTIME 57 | #define HAVE_CLOCK_GETTIME 1 58 | #endif 59 | 60 | #ifndef HAVE_GETTIMEOFDAY 61 | #define HAVE_GETTIMEOFDAY 1 62 | #endif 63 | 64 | #ifndef HAVE_PTHREAD_SPIN_LOCK 65 | #define HAVE_PTHREAD_SPIN_LOCK 0 66 | #endif 67 | 68 | #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK 69 | #define HAVE_PTHREAD_MUTEX_TIMEDLOCK 0 70 | #endif 71 | 72 | #ifndef HAVE_SEM_TIMEDWAIT 73 | #define HAVE_SEM_TIMEDWAIT 0 74 | #endif 75 | 76 | #ifndef HAVE_PIPE 77 | #define HAVE_PIPE 1 78 | #endif 79 | 80 | #ifndef HAVE_SOCKETPAIR 81 | #define HAVE_SOCKETPAIR 1 82 | #endif 83 | 84 | #ifndef HAVE_EVENTFD 85 | #define HAVE_EVENTFD 1 86 | #endif 87 | 88 | #ifndef HAVE_SETPROCTITLE 89 | #define HAVE_SETPROCTITLE 0 90 | #endif 91 | 92 | /* #undef WITH_OPENSSL */ 93 | /* #undef WITH_GNUTLS */ 94 | /* #undef WITH_MBEDTLS */ 95 | 96 | /* #undef ENABLE_UDS */ 97 | /* #undef USE_MULTIMAP */ 98 | 99 | #define WITH_WEPOLL 1 100 | /* #undef WITH_KCP */ 101 | 102 | #endif // HV_CONFIG_H_ 103 | -------------------------------------------------------------------------------- /hconfig.h.in: -------------------------------------------------------------------------------- 1 | #ifndef HV_CONFIG_H_ 2 | #define HV_CONFIG_H_ 3 | 4 | #ifndef HAVE_STDBOOL_H 5 | #define HAVE_STDBOOL_H @HAVE_STDBOOL_H@ 6 | #endif 7 | 8 | #ifndef HAVE_STDINT_H 9 | #define HAVE_STDINT_H @HAVE_STDINT_H@ 10 | #endif 11 | 12 | #ifndef HAVE_STDATOMIC_H 13 | #define HAVE_STDATOMIC_H @HAVE_STDATOMIC_H@ 14 | #endif 15 | 16 | #ifndef HAVE_SYS_TYPES_H 17 | #define HAVE_SYS_TYPES_H @HAVE_SYS_TYPES_H@ 18 | #endif 19 | 20 | #ifndef HAVE_SYS_STAT_H 21 | #define HAVE_SYS_STAT_H @HAVE_SYS_STAT_H@ 22 | #endif 23 | 24 | #ifndef HAVE_SYS_TIME_H 25 | #define HAVE_SYS_TIME_H @HAVE_SYS_TIME_H@ 26 | #endif 27 | 28 | #ifndef HAVE_FCNTL_H 29 | #define HAVE_FCNTL_H @HAVE_FCNTL_H@ 30 | #endif 31 | 32 | #ifndef HAVE_PTHREAD_H 33 | #define HAVE_PTHREAD_H @HAVE_PTHREAD_H@ 34 | #endif 35 | 36 | #ifndef HAVE_ENDIAN_H 37 | #define HAVE_ENDIAN_H @HAVE_ENDIAN_H@ 38 | #endif 39 | 40 | #ifndef HAVE_SYS_ENDIAN_H 41 | #define HAVE_SYS_ENDIAN_H @HAVE_SYS_ENDIAN_H@ 42 | #endif 43 | 44 | #ifndef HAVE_GETTID 45 | #define HAVE_GETTID @HAVE_GETTID@ 46 | #endif 47 | 48 | #ifndef HAVE_STRLCPY 49 | #define HAVE_STRLCPY @HAVE_STRLCPY@ 50 | #endif 51 | 52 | #ifndef HAVE_STRLCAT 53 | #define HAVE_STRLCAT @HAVE_STRLCAT@ 54 | #endif 55 | 56 | #ifndef HAVE_CLOCK_GETTIME 57 | #define HAVE_CLOCK_GETTIME @HAVE_CLOCK_GETTIME@ 58 | #endif 59 | 60 | #ifndef HAVE_GETTIMEOFDAY 61 | #define HAVE_GETTIMEOFDAY @HAVE_GETTIMEOFDAY@ 62 | #endif 63 | 64 | #ifndef HAVE_PTHREAD_SPIN_LOCK 65 | #define HAVE_PTHREAD_SPIN_LOCK @HAVE_PTHREAD_SPIN_LOCK@ 66 | #endif 67 | 68 | #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK 69 | #define HAVE_PTHREAD_MUTEX_TIMEDLOCK @HAVE_PTHREAD_MUTEX_TIMEDLOCK@ 70 | #endif 71 | 72 | #ifndef HAVE_SEM_TIMEDWAIT 73 | #define HAVE_SEM_TIMEDWAIT @HAVE_SEM_TIMEDWAIT@ 74 | #endif 75 | 76 | #ifndef HAVE_PIPE 77 | #define HAVE_PIPE @HAVE_PIPE@ 78 | #endif 79 | 80 | #ifndef HAVE_SOCKETPAIR 81 | #define HAVE_SOCKETPAIR @HAVE_SOCKETPAIR@ 82 | #endif 83 | 84 | #ifndef HAVE_EVENTFD 85 | #define HAVE_EVENTFD @HAVE_EVENTFD@ 86 | #endif 87 | 88 | #ifndef HAVE_SETPROCTITLE 89 | #define HAVE_SETPROCTITLE @HAVE_SETPROCTITLE@ 90 | #endif 91 | 92 | #cmakedefine WITH_OPENSSL 1 93 | #cmakedefine WITH_GNUTLS 1 94 | #cmakedefine WITH_MBEDTLS 1 95 | 96 | #cmakedefine ENABLE_UDS 1 97 | #cmakedefine USE_MULTIMAP 1 98 | 99 | #cmakedefine WITH_WEPOLL 1 100 | #cmakedefine WITH_KCP 1 101 | 102 | #endif // HV_CONFIG_H_ 103 | -------------------------------------------------------------------------------- /html/EventSource.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE HTML> 2 | <html> 3 | 4 | <head> 5 | <meta charset="utf-8"> 6 | <title>EventSource客户端</title> 7 | 8 | <script type="text/javascript"> 9 | function EventSourceTest(url) 10 | { 11 | if (typeof(EventSource) !== "undefined") 12 | { 13 | var es = new EventSource(url); 14 | 15 | es.onopen = function() 16 | { 17 | alert("连接已建立"); 18 | }; 19 | 20 | es.onmessage = function(ev) 21 | { 22 | console.log("received event: " + ev.data); 23 | var li=document.createElement("li"); 24 | li.innerHTML=ev.data; 25 | document.getElementById("msg_list").appendChild(li); 26 | }; 27 | 28 | es.onerror = function(e) 29 | { 30 | alert("连接断开"); 31 | }; 32 | } 33 | else 34 | { 35 | alert("您的浏览器不支持 EventSource!"); 36 | } 37 | } 38 | </script> 39 | </head> 40 | 41 | <body> 42 | URL: <input type="text" id="url" value="http://127.0.0.1:8080/sse" style="width:300px;"> 43 | <button onclick="EventSourceTest(document.getElementById('url').value)">运行 EventSource</button> 44 | <div> 45 | <ul id="msg_list" style="height:500px;overflow-y:scroll;"> 46 | </ul> 47 | <div> 48 | </body> 49 | 50 | </html> 51 | -------------------------------------------------------------------------------- /html/WebSocket.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE HTML> 2 | <html> 3 | 4 | <head> 5 | <meta charset="utf-8"> 6 | <title>WebSocket客户端</title> 7 | 8 | <script type="text/javascript"> 9 | function WebSocketTest(url) 10 | { 11 | if ("WebSocket" in window) 12 | { 13 | // 打开一个 web socket 14 | var ws = new WebSocket(url); 15 | 16 | ws.onopen = function() 17 | { 18 | alert("连接已建立"); 19 | ws.send("hello"); 20 | }; 21 | 22 | ws.onmessage = function(ev) 23 | { 24 | var received_msg = ev.data; 25 | console.log("received websocket message: " + received_msg); 26 | var li=document.createElement("li"); 27 | li.innerHTML=received_msg; 28 | document.getElementById("msg_list").appendChild(li); 29 | }; 30 | 31 | ws.onclose = function() 32 | { 33 | alert("连接已关闭"); 34 | }; 35 | } 36 | else 37 | { 38 | alert("您的浏览器不支持 WebSocket!"); 39 | } 40 | } 41 | </script> 42 | </head> 43 | 44 | <body> 45 | URL: <input type="text" id="url" value="ws://127.0.0.1:9999/test" style="width:300px;"> 46 | <button onclick="WebSocketTest(document.getElementById('url').value)">运行 WebSocket</button> 47 | <div> 48 | <ul id="msg_list" style="height:500px;overflow-y:scroll;"> 49 | </ul> 50 | <div> 51 | </body> 52 | 53 | </html> 54 | -------------------------------------------------------------------------------- /html/downloads/libhv-vs-nginx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ithewei/libhv/1778d4a155b43bc3dee1cd6422afe1d7f5fe5ebc/html/downloads/libhv-vs-nginx.png -------------------------------------------------------------------------------- /html/downloads/中文.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <meta charset="utf-8" /> 5 | <title>中文测试</title> 6 | </head> 7 | <body> 8 | <center><h1> 欢迎使用 <a href="https://github.com/ithewei/libhv.git">libhv</a> </h1></center> 9 | </body> 10 | </html> 11 | -------------------------------------------------------------------------------- /html/error.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <title>Error</title> 5 | </head> 6 | <body> 7 | <h1>An error occurred.</h1> 8 | <p>Sorry, the page you are looking for is currently unavailable.<br/>Please try again later.</p> 9 | <p>If you are the system administrator of this resource then you should check the error log for details.</p> 10 | <p><em>Faithfully yours, httpd.</em></p> 11 | </body> 12 | </html> 13 | -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <title>libhv</title> 5 | </head> 6 | <body> 7 | <center><h1> Welcome to <a href="https://github.com/ithewei/libhv.git">libhv</a> </h1></center> 8 | </body> 9 | </html> 10 | -------------------------------------------------------------------------------- /html/uploads/upload.sh: -------------------------------------------------------------------------------- 1 | bin/curl -v localhost:8080/upload -F "file=@LICENSE" 2 | -------------------------------------------------------------------------------- /html/中文路径/中文名称.txt: -------------------------------------------------------------------------------- 1 | http://127.0.0.1:8080/中文路径/中文名称.txt 2 | 用于Windows中文路径测试 -------------------------------------------------------------------------------- /http/Http2Parser.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_HTTP2_PARSER_H_ 2 | #define HV_HTTP2_PARSER_H_ 3 | 4 | #ifdef WITH_NGHTTP2 5 | #include "HttpParser.h" 6 | #include "http2def.h" 7 | #include "grpcdef.h" 8 | 9 | #include "nghttp2/nghttp2.h" 10 | 11 | enum http2_session_state { 12 | H2_SEND_MAGIC, 13 | H2_SEND_SETTINGS, 14 | H2_SEND_PING, 15 | H2_SEND_HEADERS, 16 | H2_SEND_DATA_FRAME_HD, 17 | H2_SEND_DATA, 18 | H2_SEND_DONE, 19 | 20 | H2_WANT_SEND, 21 | H2_WANT_RECV, 22 | 23 | H2_RECV_SETTINGS, 24 | H2_RECV_PING, 25 | H2_RECV_HEADERS, 26 | H2_RECV_DATA, 27 | }; 28 | 29 | class Http2Parser : public HttpParser { 30 | public: 31 | static nghttp2_session_callbacks* cbs; 32 | nghttp2_session* session; 33 | http2_session_state state; 34 | HttpMessage* submited; 35 | HttpMessage* parsed; 36 | int error; 37 | int stream_id; 38 | int stream_closed; 39 | int frame_type_when_stream_closed; 40 | // http2_frame_hd + grpc_message_hd 41 | // at least HTTP2_FRAME_HDLEN + GRPC_MESSAGE_HDLEN = 9 + 5 = 14 42 | unsigned char frame_hdbuf[32]; 43 | 44 | Http2Parser(http_session_type type = HTTP_CLIENT); 45 | virtual ~Http2Parser(); 46 | 47 | virtual int GetSendData(char** data, size_t* len); 48 | virtual int FeedRecvData(const char* data, size_t len); 49 | 50 | virtual int GetState() { 51 | return (int)state; 52 | } 53 | 54 | virtual bool WantRecv() { 55 | return state == H2_WANT_RECV; 56 | } 57 | 58 | virtual bool WantSend() { 59 | return state <= H2_WANT_SEND; 60 | } 61 | 62 | virtual bool IsComplete() { 63 | return stream_closed && (frame_type_when_stream_closed == HTTP2_DATA || frame_type_when_stream_closed == HTTP2_HEADERS); 64 | } 65 | 66 | virtual int GetError() { 67 | return error; 68 | } 69 | 70 | virtual const char* StrError(int error) { 71 | //return nghttp2_http2_strerror(error); 72 | return nghttp2_strerror(error); 73 | } 74 | 75 | // client 76 | // SubmitRequest -> while(GetSendData) {send} -> InitResponse -> do {recv -> FeedRecvData} while(WantRecv) 77 | virtual int SubmitRequest(HttpRequest* req); 78 | virtual int InitResponse(HttpResponse* res); 79 | 80 | // server 81 | // InitRequest -> do {recv -> FeedRecvData} while(WantRecv) -> SubmitResponse -> while(GetSendData) {send} 82 | virtual int InitRequest(HttpRequest* req); 83 | virtual int SubmitResponse(HttpResponse* res); 84 | 85 | }; 86 | 87 | #endif 88 | 89 | #endif // HV_HTTP2_PARSER_H_ 90 | -------------------------------------------------------------------------------- /http/HttpParser.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpParser.h" 2 | 3 | #include "Http1Parser.h" 4 | #include "Http2Parser.h" 5 | #include "hlog.h" 6 | 7 | HttpParser* HttpParser::New(http_session_type type, http_version version) { 8 | HttpParser* hp = NULL; 9 | if (version == HTTP_V1) { 10 | hp = new Http1Parser(type); 11 | } 12 | else if (version == HTTP_V2) { 13 | #ifdef WITH_NGHTTP2 14 | hp = new Http2Parser(type); 15 | #else 16 | hlogi("Please recompile WITH_NGHTTP2!\n"); 17 | #endif 18 | } 19 | 20 | if (hp) { 21 | hp->version = version; 22 | hp->type = type; 23 | } 24 | 25 | return hp; 26 | } 27 | -------------------------------------------------------------------------------- /http/HttpParser.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_HTTP_PARSER_H_ 2 | #define HV_HTTP_PARSER_H_ 3 | 4 | #include "hexport.h" 5 | #include "HttpMessage.h" 6 | 7 | class HV_EXPORT HttpParser { 8 | public: 9 | http_version version; 10 | http_session_type type; 11 | 12 | static HttpParser* New(http_session_type type = HTTP_CLIENT, http_version version = HTTP_V1); 13 | virtual ~HttpParser() {} 14 | 15 | virtual int GetSendData(char** data, size_t* len) = 0; 16 | virtual int FeedRecvData(const char* data, size_t len) = 0; 17 | 18 | // Http1Parser: http_parser_state 19 | // Http2Parser: http2_session_state 20 | virtual int GetState() = 0; 21 | 22 | // Http1Parser: GetState() != HP_MESSAGE_COMPLETE 23 | // Http2Parser: GetState() == H2_WANT_RECV 24 | virtual bool WantRecv() = 0; 25 | 26 | // Http1Parser: GetState() == HP_MESSAGE_COMPLETE 27 | // Http2Parser: GetState() == H2_WANT_SEND 28 | virtual bool WantSend() = 0; 29 | 30 | // IsComplete: Is recved HttpRequest or HttpResponse complete? 31 | // Http1Parser: GetState() == HP_MESSAGE_COMPLETE 32 | // Http2Parser: (state == H2_RECV_HEADERS || state == H2_RECV_DATA) && stream_closed 33 | virtual bool IsComplete() = 0; 34 | 35 | virtual bool IsEof() { return false; } 36 | 37 | // client 38 | // SubmitRequest -> while(GetSendData) {send} -> InitResponse -> do {recv -> FeedRecvData} while(WantRecv) 39 | virtual int SubmitRequest(HttpRequest* req) = 0; 40 | virtual int InitResponse(HttpResponse* res) = 0; 41 | 42 | // server 43 | // InitRequest -> do {recv -> FeedRecvData} while(WantRecv) -> SubmitResponse -> while(GetSendData) {send} 44 | virtual int InitRequest(HttpRequest* req) = 0; 45 | virtual int SubmitResponse(HttpResponse* res) = 0; 46 | 47 | virtual int GetError() = 0; 48 | virtual const char* StrError(int error) = 0; 49 | }; 50 | 51 | typedef std::shared_ptr<HttpParser> HttpParserPtr; 52 | 53 | #endif // HV_HTTP_PARSER_H_ 54 | -------------------------------------------------------------------------------- /http/README.md: -------------------------------------------------------------------------------- 1 | ## 目录结构 2 | 3 | ``` 4 | . 5 | ├── client 6 | │ ├── HttpClient.h http客户端对外头文件 7 | │ ├── requests.h 模拟python requests api 8 | │ └── axios.h 模拟nodejs axios api 9 | ├── httpdef.h http定义 10 | ├── http2def.h http2定义 11 | ├── grpcdef.h grpc定义 12 | ├── HttpParser.h http解析基类 13 | ├── Http1Parser.h http1解析类 14 | ├── Http2Parser.h http2解析类 15 | ├── HttpMessage.h http请求响应类 16 | ├── http_content.h http Content-Type 17 | ├── http_parser.h http1解析实现 18 | ├── multipart_parser.h multipart解析 19 | └── server 20 | ├── HttpServer.h http服务端对外头文件 21 | ├── HttpHandler.h http处理类 22 | ├── FileCache.h 文件缓存类 23 | ├── http_page.h http页面构造 24 | └── HttpService.h http业务类 (包括api service、web service、indexof service) 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /http/WebSocketChannel.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_WEBSOCKET_CHANNEL_H_ 2 | #define HV_WEBSOCKET_CHANNEL_H_ 3 | 4 | #include <mutex> 5 | 6 | #include "Channel.h" 7 | 8 | #include "wsdef.h" 9 | #include "hmath.h" 10 | 11 | namespace hv { 12 | 13 | class HV_EXPORT WebSocketChannel : public SocketChannel { 14 | public: 15 | ws_session_type type; 16 | WebSocketChannel(hio_t* io, ws_session_type type = WS_CLIENT) 17 | : SocketChannel(io) 18 | , type(type) 19 | , opcode(WS_OPCODE_CLOSE) 20 | {} 21 | ~WebSocketChannel() {} 22 | 23 | // isConnected, send, close 24 | 25 | int send(const std::string& msg, enum ws_opcode opcode = WS_OPCODE_TEXT, bool fin = true) { 26 | return send(msg.c_str(), msg.size(), opcode, fin); 27 | } 28 | 29 | int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY, bool fin = true); 30 | 31 | // websocket fragment 32 | int send(const char* buf, int len, int fragment, enum ws_opcode opcode = WS_OPCODE_BINARY); 33 | 34 | int sendPing(); 35 | int sendPong(); 36 | 37 | int close() { 38 | return SocketChannel::close(type == WS_SERVER); 39 | } 40 | 41 | protected: 42 | int sendFrame(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY, bool fin = true); 43 | 44 | public: 45 | enum ws_opcode opcode; 46 | private: 47 | Buffer sendbuf_; 48 | std::mutex mutex_; 49 | }; 50 | 51 | } 52 | 53 | typedef std::shared_ptr<hv::WebSocketChannel> WebSocketChannelPtr; 54 | 55 | #endif // HV_WEBSOCKET_CHANNEL_H_ 56 | -------------------------------------------------------------------------------- /http/WebSocketParser.cpp: -------------------------------------------------------------------------------- 1 | #include "WebSocketParser.h" 2 | 3 | #include "websocket_parser.h" 4 | #include "hdef.h" 5 | 6 | #define MAX_PAYLOAD_LENGTH (1 << 24) // 16M 7 | 8 | static int on_frame_header(websocket_parser* parser) { 9 | WebSocketParser* wp = (WebSocketParser*)parser->data; 10 | int opcode = parser->flags & WS_OP_MASK; 11 | // printf("on_frame_header opcode=%d\n", opcode); 12 | if (opcode != WS_OP_CONTINUE) { 13 | wp->opcode = opcode; 14 | } 15 | int length = parser->length; 16 | int reserve_length = MIN(length + 1, MAX_PAYLOAD_LENGTH); 17 | if (reserve_length > wp->message.capacity()) { 18 | wp->message.reserve(reserve_length); 19 | } 20 | if (wp->state == WS_FRAME_BEGIN || 21 | wp->state == WS_FRAME_FIN) { 22 | wp->message.clear(); 23 | } 24 | wp->state = WS_FRAME_HEADER; 25 | return 0; 26 | } 27 | 28 | static int on_frame_body(websocket_parser* parser, const char * at, size_t length) { 29 | // printf("on_frame_body length=%d\n", (int)length); 30 | WebSocketParser* wp = (WebSocketParser*)parser->data; 31 | wp->state = WS_FRAME_BODY; 32 | if (wp->parser->flags & WS_HAS_MASK) { 33 | websocket_parser_decode((char*)at, at, length, wp->parser); 34 | } 35 | wp->message.append(at, length); 36 | return 0; 37 | } 38 | 39 | static int on_frame_end(websocket_parser* parser) { 40 | // printf("on_frame_end\n"); 41 | WebSocketParser* wp = (WebSocketParser*)parser->data; 42 | wp->state = WS_FRAME_END; 43 | if (wp->parser->flags & WS_FIN) { 44 | wp->state = WS_FRAME_FIN; 45 | if (wp->onMessage) { 46 | wp->onMessage(wp->opcode, wp->message); 47 | } 48 | } 49 | return 0; 50 | } 51 | 52 | static websocket_parser_settings cbs = { 53 | on_frame_header, 54 | on_frame_body, 55 | on_frame_end 56 | }; 57 | 58 | WebSocketParser::WebSocketParser() { 59 | parser = (websocket_parser*)malloc(sizeof(websocket_parser)); 60 | websocket_parser_init(parser); 61 | parser->data = this; 62 | state = WS_FRAME_BEGIN; 63 | opcode = WS_OP_CLOSE; 64 | } 65 | 66 | WebSocketParser::~WebSocketParser() { 67 | if (parser) { 68 | free(parser); 69 | parser = NULL; 70 | } 71 | } 72 | 73 | int WebSocketParser::FeedRecvData(const char* data, size_t len) { 74 | return websocket_parser_execute(parser, &cbs, data, len); 75 | } 76 | -------------------------------------------------------------------------------- /http/WebSocketParser.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_WEBSOCKET_PARSER_H_ 2 | #define HV_WEBSOCKET_PARSER_H_ 3 | 4 | #include "hexport.h" 5 | 6 | #include <string> 7 | #include <memory> 8 | #include <functional> 9 | 10 | enum websocket_parser_state { 11 | WS_FRAME_BEGIN, 12 | WS_FRAME_HEADER, 13 | WS_FRAME_BODY, 14 | WS_FRAME_END, 15 | WS_FRAME_FIN, 16 | }; 17 | 18 | struct websocket_parser; 19 | class HV_EXPORT WebSocketParser { 20 | public: 21 | websocket_parser* parser; 22 | websocket_parser_state state; 23 | int opcode; 24 | std::string message; 25 | std::function<void(int opcode, const std::string& msg)> onMessage; 26 | 27 | WebSocketParser(); 28 | ~WebSocketParser(); 29 | 30 | int FeedRecvData(const char* data, size_t len); 31 | }; 32 | 33 | typedef std::shared_ptr<WebSocketParser> WebSocketParserPtr; 34 | 35 | #endif // HV_WEBSOCKET_PARSER_H_ 36 | -------------------------------------------------------------------------------- /http/client/WebSocketClient.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_WEBSOCKET_CLIENT_H_ 2 | #define HV_WEBSOCKET_CLIENT_H_ 3 | 4 | /* 5 | * @demo examples/websocket_client_test.cpp 6 | */ 7 | 8 | #include "hexport.h" 9 | 10 | #include "TcpClient.h" 11 | #include "WebSocketChannel.h" 12 | 13 | #include "HttpParser.h" 14 | #include "WebSocketParser.h" 15 | 16 | namespace hv { 17 | 18 | class HV_EXPORT WebSocketClient : public TcpClientTmpl<WebSocketChannel> { 19 | public: 20 | std::string url; 21 | std::function<void()> onopen; 22 | std::function<void()> onclose; 23 | std::function<void(const std::string& msg)> onmessage; 24 | // PATCH: onmessage not given opcode 25 | enum ws_opcode opcode() { return channel ? channel->opcode : WS_OPCODE_CLOSE; } 26 | 27 | WebSocketClient(EventLoopPtr loop = NULL); 28 | virtual ~WebSocketClient(); 29 | 30 | // url = ws://ip:port/path 31 | // url = wss://ip:port/path 32 | int open(const char* url, const http_headers& headers = DefaultHeaders); 33 | int close(); 34 | int send(const std::string& msg); 35 | int send(const char* buf, int len, enum ws_opcode opcode = WS_OPCODE_BINARY); 36 | 37 | // setConnectTimeout / setPingInterval / setReconnect 38 | void setPingInterval(int ms) { 39 | ping_interval = ms; 40 | } 41 | 42 | // NOTE: call before open 43 | void setHttpRequest(const HttpRequestPtr& req) { 44 | http_req_ = req; 45 | } 46 | 47 | // NOTE: call when onopen 48 | const HttpResponsePtr& getHttpResponse() { 49 | return http_resp_; 50 | } 51 | 52 | private: 53 | enum State { 54 | CONNECTING, 55 | CONNECTED, 56 | WS_UPGRADING, 57 | WS_OPENED, 58 | WS_CLOSED, 59 | } state; 60 | HttpParserPtr http_parser_; 61 | HttpRequestPtr http_req_; 62 | HttpResponsePtr http_resp_; 63 | WebSocketParserPtr ws_parser_; 64 | // ping/pong 65 | int ping_interval; 66 | int ping_cnt; 67 | }; 68 | 69 | } 70 | 71 | #endif // HV_WEBSOCKET_CLIENT_H_ 72 | -------------------------------------------------------------------------------- /http/http2def.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_HTTP2_DEF_H_ 2 | #define HV_HTTP2_DEF_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | 9 | #define HTTP2_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" 10 | #define HTTP2_MAGIC_LEN 24 11 | 12 | // length:3bytes + type:1byte + flags:1byte + stream_id:4bytes = 9bytes 13 | #define HTTP2_FRAME_HDLEN 9 14 | 15 | #define HTTP2_UPGRADE_RESPONSE \ 16 | "HTTP/1.1 101 Switching Protocols\r\n"\ 17 | "Connection: Upgrade\r\n"\ 18 | "Upgrade: h2c\r\n\r\n" 19 | 20 | typedef enum { 21 | HTTP2_DATA = 0, 22 | HTTP2_HEADERS = 0x01, 23 | HTTP2_PRIORITY = 0x02, 24 | HTTP2_RST_STREAM = 0x03, 25 | HTTP2_SETTINGS = 0x04, 26 | HTTP2_PUSH_PROMISE = 0x05, 27 | HTTP2_PING = 0x06, 28 | HTTP2_GOAWAY = 0x07, 29 | HTTP2_WINDOW_UPDATE = 0x08, 30 | HTTP2_CONTINUATION = 0x09, 31 | HTTP2_ALTSVC = 0x0a, 32 | HTTP2_ORIGIN = 0x0c 33 | } http2_frame_type; 34 | 35 | typedef enum { 36 | HTTP2_FLAG_NONE = 0, 37 | HTTP2_FLAG_END_STREAM = 0x01, 38 | HTTP2_FLAG_END_HEADERS = 0x04, 39 | HTTP2_FLAG_PADDED = 0x08, 40 | HTTP2_FLAG_PRIORITY = 0x20 41 | } http2_flag; 42 | 43 | typedef struct { 44 | int length; 45 | http2_frame_type type; 46 | http2_flag flags; 47 | int stream_id; 48 | } http2_frame_hd; 49 | 50 | static inline void http2_frame_hd_pack(const http2_frame_hd* hd, unsigned char* buf) { 51 | // hton 52 | int length = hd->length; 53 | int stream_id = hd->stream_id; 54 | unsigned char* p = buf; 55 | *p++ = (length >> 16) & 0xFF; 56 | *p++ = (length >> 8) & 0xFF; 57 | *p++ = length & 0xFF; 58 | *p++ = (unsigned char)hd->type; 59 | *p++ = (unsigned char)hd->flags; 60 | *p++ = (stream_id >> 24) & 0xFF; 61 | *p++ = (stream_id >> 16) & 0xFF; 62 | *p++ = (stream_id >> 8) & 0xFF; 63 | *p++ = stream_id & 0xFF; 64 | } 65 | 66 | static inline void http2_frame_hd_unpack(const unsigned char* buf, http2_frame_hd* hd) { 67 | // ntoh 68 | const unsigned char* p = buf; 69 | hd->length = *p++ << 16; 70 | hd->length += *p++ << 8; 71 | hd->length += *p++; 72 | 73 | hd->type = (http2_frame_type)*p++; 74 | hd->flags = (http2_flag)*p++; 75 | 76 | hd->stream_id = *p++ << 24; 77 | hd->stream_id += *p++ << 16; 78 | hd->stream_id += *p++ << 8; 79 | hd->stream_id += *p++; 80 | } 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif 85 | 86 | #endif // HV_HTTP2_DEF_H_ 87 | -------------------------------------------------------------------------------- /http/http_content.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_HTTP_CONTENT_H_ 2 | #define HV_HTTP_CONTENT_H_ 3 | 4 | #include "hexport.h" 5 | #include "hstring.h" 6 | 7 | // NOTE: WITHOUT_HTTP_CONTENT 8 | // ndk-r10e no std::to_string and can't compile modern json.hpp 9 | #ifndef WITHOUT_HTTP_CONTENT 10 | #include "json.hpp" // https://github.com/nlohmann/json 11 | #endif 12 | 13 | BEGIN_NAMESPACE_HV 14 | 15 | // QueryParams 16 | using QueryParams = hv::KeyValue; 17 | HV_EXPORT std::string dump_query_params(const QueryParams& query_params); 18 | HV_EXPORT int parse_query_params(const char* query_string, QueryParams& query_params); 19 | 20 | #ifndef WITHOUT_HTTP_CONTENT 21 | 22 | /**************multipart/form-data************************************* 23 | --boundary 24 | Content-Disposition: form-data; name="user" 25 | 26 | content 27 | --boundary 28 | Content-Disposition: form-data; name="avatar"; filename="user.jpg" 29 | Content-Type: image/jpeg 30 | 31 | content 32 | --boundary-- 33 | ***********************************************************************/ 34 | // FormData 35 | struct FormData { 36 | std::string filename; 37 | std::string content; 38 | 39 | FormData(const char* content = NULL, const char* filename = NULL) { 40 | if (content) { 41 | this->content = content; 42 | } 43 | if (filename) { 44 | this->filename = filename; 45 | } 46 | } 47 | template<typename T> 48 | FormData(T num) { 49 | content = hv::to_string(num); 50 | } 51 | }; 52 | // FormFile 53 | struct FormFile : public FormData { 54 | FormFile(const char* filename = NULL) { 55 | if (filename) { 56 | this->filename = filename; 57 | } 58 | } 59 | }; 60 | 61 | // MultiPart 62 | // name => FormData 63 | typedef HV_MAP<std::string, FormData> MultiPart; 64 | #define DEFAULT_MULTIPART_BOUNDARY "----WebKitFormBoundary7MA4YWxkTrZu0gW" 65 | HV_EXPORT std::string dump_multipart(MultiPart& mp, const char* boundary = DEFAULT_MULTIPART_BOUNDARY); 66 | HV_EXPORT int parse_multipart(const std::string& str, MultiPart& mp, const char* boundary); 67 | 68 | // Json 69 | using Json = nlohmann::json; 70 | // using Json = nlohmann::ordered_json; 71 | 72 | HV_EXPORT std::string dump_json(const hv::Json& json, int indent = -1); 73 | HV_EXPORT int parse_json(const char* str, hv::Json& json, std::string& errmsg); 74 | #endif 75 | 76 | END_NAMESPACE_HV 77 | 78 | #endif // HV_HTTP_CONTENT_H_ 79 | -------------------------------------------------------------------------------- /http/multipart_parser.h: -------------------------------------------------------------------------------- 1 | /* Based on node-formidable by Felix Geisendörfer 2 | * Igor Afonov - afonov@gmail.com - 2012 3 | * MIT License - http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | #ifndef _multipart_parser_h 6 | #define _multipart_parser_h 7 | 8 | #ifdef __cplusplus 9 | extern "C" 10 | { 11 | #endif 12 | 13 | #include <stdlib.h> 14 | #include <ctype.h> 15 | 16 | typedef struct multipart_parser multipart_parser; 17 | typedef struct multipart_parser_settings multipart_parser_settings; 18 | typedef struct multipart_parser_state multipart_parser_state; 19 | 20 | typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length); 21 | typedef int (*multipart_notify_cb) (multipart_parser*); 22 | 23 | struct multipart_parser_settings { 24 | multipart_data_cb on_header_field; 25 | multipart_data_cb on_header_value; 26 | multipart_data_cb on_part_data; 27 | 28 | multipart_notify_cb on_part_data_begin; 29 | multipart_notify_cb on_headers_complete; 30 | multipart_notify_cb on_part_data_end; 31 | multipart_notify_cb on_body_end; 32 | }; 33 | 34 | multipart_parser* multipart_parser_init 35 | (const char *boundary, const multipart_parser_settings* settings); 36 | 37 | void multipart_parser_free(multipart_parser* p); 38 | 39 | size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len); 40 | 41 | void multipart_parser_set_data(multipart_parser* p, void* data); 42 | void * multipart_parser_get_data(multipart_parser* p); 43 | 44 | #ifdef __cplusplus 45 | } /* extern "C" */ 46 | #endif 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /http/server/HttpMiddleware.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpMiddleware.h" 2 | #include "HttpService.h" 3 | 4 | BEGIN_NAMESPACE_HV 5 | 6 | int HttpMiddleware::CORS(HttpRequest* req, HttpResponse* resp) { 7 | resp->headers["Access-Control-Allow-Origin"] = req->GetHeader("Origin", "*"); 8 | if (req->method == HTTP_OPTIONS) { 9 | resp->headers["Access-Control-Allow-Methods"] = req->GetHeader("Access-Control-Request-Method", "OPTIONS, HEAD, GET, POST, PUT, DELETE, PATCH"); 10 | resp->headers["Access-Control-Allow-Headers"] = req->GetHeader("Access-Control-Request-Headers", "Content-Type"); 11 | return HTTP_STATUS_NO_CONTENT; 12 | } 13 | return HTTP_STATUS_NEXT; 14 | } 15 | 16 | END_NAMESPACE_HV 17 | -------------------------------------------------------------------------------- /http/server/HttpMiddleware.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_HTTP_MIDDLEWARE_H_ 2 | #define HV_HTTP_MIDDLEWARE_H_ 3 | 4 | #include "hexport.h" 5 | #include "HttpContext.h" 6 | 7 | BEGIN_NAMESPACE_HV 8 | 9 | class HttpMiddleware { 10 | public: 11 | static int CORS(HttpRequest* req, HttpResponse* resp); 12 | }; 13 | 14 | END_NAMESPACE_HV 15 | 16 | #endif // HV_HTTP_MIDDLEWARE_H_ 17 | -------------------------------------------------------------------------------- /http/server/WebSocketServer.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_WEBSOCKET_SERVER_H_ 2 | #define HV_WEBSOCKET_SERVER_H_ 3 | 4 | /* 5 | * @demo examples/websocket_server_test.cpp 6 | */ 7 | 8 | #include "HttpServer.h" 9 | #include "WebSocketChannel.h" 10 | 11 | #define websocket_server_t http_server_t 12 | #define websocket_server_run http_server_run 13 | #define websocket_server_stop http_server_stop 14 | 15 | namespace hv { 16 | 17 | struct WebSocketService { 18 | std::function<void(const WebSocketChannelPtr&, const HttpRequestPtr&)> onopen; 19 | std::function<void(const WebSocketChannelPtr&, const std::string&)> onmessage; 20 | std::function<void(const WebSocketChannelPtr&)> onclose; 21 | int ping_interval; 22 | 23 | WebSocketService() : ping_interval(0) {} 24 | 25 | void setPingInterval(int ms) { 26 | ping_interval = ms; 27 | } 28 | }; 29 | 30 | class WebSocketServer : public HttpServer { 31 | public: 32 | WebSocketServer(WebSocketService* service = NULL) 33 | : HttpServer() 34 | { 35 | this->ws = service; 36 | } 37 | ~WebSocketServer() { stop(); } 38 | 39 | void registerWebSocketService(WebSocketService* service) { 40 | this->ws = service; 41 | } 42 | }; 43 | 44 | } 45 | 46 | #endif // HV_WEBSOCKET_SERVER_H_ 47 | -------------------------------------------------------------------------------- /http/server/http_page.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_HTTP_PAGE_H_ 2 | #define HV_HTTP_PAGE_H_ 3 | 4 | #include <string> 5 | 6 | #include "httpdef.h" 7 | 8 | /* 9 | <!DOCTYPE html> 10 | <html> 11 | <head> 12 | <title>404 Not Found</title> 13 | </head> 14 | <body> 15 | <center><h1>404 Not Found</h1></center> 16 | <hr> 17 | </body> 18 | </html> 19 | */ 20 | void make_http_status_page(http_status status_code, std::string& page); 21 | 22 | /* 23 | <!DOCTYPE html> 24 | <html> 25 | <head> 26 | <title>Index of /downloads/</title> 27 | </head> 28 | <body> 29 | <h1>Index of /downloads/</h1> 30 | <hr> 31 | <table border="0"> 32 | <tr> 33 | <th align="left" width="30%">Name</th> 34 | <th align="left" width="20%">Date</th> 35 | <th align="left" width="20%">Size</th> 36 | </tr> 37 | <tr> 38 | <td><a href="../">../</a></td> 39 | </tr> 40 | <tr> 41 | <td><a href="libhv-vs-nginx.png">libhv-vs-nginx.png</a></td> 42 | <td>2021-03-10 12:33:57</td> 43 | <td>211.4K</td> 44 | </tr> 45 | <td><a href="中文.html">中文.html</a></td> 46 | <td>2022-04-25 15:37:12</td> 47 | <td>191</td> 48 | </tr> 49 | </table> 50 | <hr> 51 | </body> 52 | </html> 53 | */ 54 | void make_index_of_page(const char* dir, std::string& page, const char* url = ""); 55 | 56 | #endif // HV_HTTP_PAGE_H_ 57 | -------------------------------------------------------------------------------- /http/wsdef.c: -------------------------------------------------------------------------------- 1 | #include "wsdef.h" 2 | 3 | #include <string.h> 4 | 5 | #include "sha1.h" 6 | #include "base64.h" 7 | 8 | #include "websocket_parser.h" 9 | 10 | // base64_encode( SHA1(key + magic) ) 11 | void ws_encode_key(const char* key, char accept[]) { 12 | char magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 13 | unsigned char digest[20] = {0}; 14 | HV_SHA1_CTX ctx; 15 | HV_SHA1Init(&ctx); 16 | HV_SHA1Update(&ctx, (unsigned char*)key, (uint32_t)strlen(key)); 17 | HV_SHA1Update(&ctx, (unsigned char*)magic, (uint32_t)strlen(magic)); 18 | HV_SHA1Final(digest, &ctx); 19 | hv_base64_encode(digest, 20, accept); 20 | } 21 | 22 | // fix-header[2] + var-length[2/8] + mask[4] + data[data_len] 23 | int ws_calc_frame_size(int data_len, bool has_mask) { 24 | int size = data_len + 2; 25 | if (data_len >=126) { 26 | if (data_len > 0xFFFF) { 27 | size += 8; 28 | } else { 29 | size += 2; 30 | } 31 | } 32 | if (has_mask) size += 4; 33 | return size; 34 | } 35 | 36 | int ws_build_frame( 37 | char* out, 38 | const char* data, int data_len, 39 | const char mask[4], bool has_mask, 40 | enum ws_opcode opcode, 41 | bool fin) { 42 | int flags = opcode; 43 | if (fin) flags |= WS_FIN; 44 | if (has_mask) flags |= WS_HAS_MASK; 45 | return (int)websocket_build_frame(out, (websocket_flags)flags, mask, data, data_len); 46 | } 47 | -------------------------------------------------------------------------------- /hv.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_H_ 2 | #define HV_H_ 3 | 4 | /** 5 | * @copyright 2018 HeWei, all rights reserved. 6 | */ 7 | 8 | // platform 9 | #include "hconfig.h" 10 | #include "hexport.h" 11 | #include "hplatform.h" 12 | 13 | // c 14 | #include "hdef.h" // <stddef.h> 15 | #include "hatomic.h"// <stdatomic.h> 16 | #include "herr.h" // <errno.h> 17 | #include "htime.h" // <time.h> 18 | #include "hmath.h" // <math.h> 19 | 20 | #include "hbase.h" 21 | #include "hversion.h" 22 | #include "hsysinfo.h" 23 | #include "hproc.h" 24 | #include "hthread.h" 25 | #include "hmutex.h" 26 | #include "hsocket.h" 27 | 28 | #include "hlog.h" 29 | #include "hbuf.h" 30 | 31 | // cpp 32 | #ifdef __cplusplus 33 | #include "hmap.h" // <map> 34 | #include "hstring.h" // <string> 35 | #include "hfile.h" 36 | #include "hpath.h" 37 | #include "hdir.h" 38 | #include "hurl.h" 39 | #endif 40 | 41 | #endif // HV_H_ 42 | -------------------------------------------------------------------------------- /hv.rc.in: -------------------------------------------------------------------------------- 1 | #include <winresrc.h> 2 | 3 | VS_VERSION_INFO VERSIONINFO 4 | FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 5 | PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 6 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 7 | #ifdef _DEBUG 8 | FILEFLAGS VS_FF_DEBUG 9 | #else 10 | FILEFLAGS 0x0L 11 | #endif 12 | FILEOS VOS_NT 13 | FILETYPE VFT_DLL 14 | FILESUBTYPE VFT2_UNKNOWN 15 | BEGIN 16 | BLOCK "StringFileInfo" 17 | BEGIN 18 | BLOCK "080404B0" 19 | BEGIN 20 | VALUE "CompanyName", "https://github.com/ithewei/libhv" 21 | VALUE "FileDescription", "${PROJECT_NAME} Library" 22 | VALUE "FileVersion", "${PROJECT_VERSION}" 23 | VALUE "InternalName", "${PROJECT_NAME}" 24 | VALUE "LegalCopyright", "Copyright (C) 2020 ithewei All rights reserved." 25 | VALUE "LegalTrademarks", "${PROJECT_NAME}" 26 | VALUE "OriginalFilename", "${PROJECT_NAME}.dll" 27 | VALUE "ProductName", "${PROJECT_NAME}" 28 | VALUE "ProductVersion", "${PROJECT_VERSION}" 29 | END 30 | END 31 | BLOCK "VarFileInfo" 32 | BEGIN 33 | VALUE "Translation", 0x0804, 0x04B0 34 | END 35 | END 36 | -------------------------------------------------------------------------------- /misc/grpc_server.h: -------------------------------------------------------------------------------- 1 | #ifndef GRPC_SERVER_H 2 | #define GRPC_SERVER_H 3 | 4 | #include "grpcpp/grpcpp.h" 5 | using grpc::ServerBuilder; 6 | using grpc::Server; 7 | using grpc::Service; 8 | using grpc::ServerCompletionQueue; 9 | 10 | // GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH = 4M 11 | // GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH = 4M 12 | #define GRPC_MAX_RECV_MESSAGE_LENGTH 1<<24 // 16M 13 | #define GRPC_MAX_SEND_MESSAGE_LENGTH 1<<24 // 16M 14 | 15 | class GrpcServer { 16 | public: 17 | GrpcServer(int port) : _port(port) { 18 | char srvaddr[128] = {0}; 19 | snprintf(srvaddr, sizeof(srvaddr), "%s:%d", "0.0.0.0", _port); 20 | build_.AddListeningPort(srvaddr, grpc::InsecureServerCredentials()); 21 | build_.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIME_MS, 30000); 22 | build_.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 5000); 23 | build_.AddChannelArgument(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1); 24 | build_.SetMaxReceiveMessageSize(GRPC_MAX_RECV_MESSAGE_LENGTH); 25 | build_.SetMaxSendMessageSize(GRPC_MAX_SEND_MESSAGE_LENGTH); 26 | } 27 | 28 | void RegisterService(Service* service) { 29 | build_.RegisterService(service); 30 | } 31 | 32 | void Run(bool wait=true) { 33 | server_ = build_.BuildAndStart(); 34 | if (wait) { 35 | server_->Wait(); 36 | } 37 | } 38 | 39 | public: 40 | int _port; 41 | ServerBuilder build_; 42 | std::unique_ptr<Server> server_; 43 | }; 44 | 45 | #endif // GRPC_SERVER_H 46 | -------------------------------------------------------------------------------- /mqtt/mqtt_protocol.c: -------------------------------------------------------------------------------- 1 | #include "mqtt_protocol.h" 2 | #include "hmath.h" 3 | 4 | int mqtt_head_pack(mqtt_head_t* head, unsigned char buf[]) { 5 | buf[0] = (head->type << 4) | 6 | (head->dup << 3) | 7 | (head->qos << 1) | 8 | (head->retain); 9 | int bytes = varint_encode(head->length, buf + 1); 10 | return 1 + bytes; 11 | } 12 | 13 | int mqtt_head_unpack(mqtt_head_t* head, const unsigned char* buf, int len) { 14 | head->type = (buf[0] >> 4) & 0x0F; 15 | head->dup = (buf[0] >> 3) & 0x01; 16 | head->qos = (buf[0] >> 1) & 0x03; 17 | head->retain = buf[0] & 0x01; 18 | int bytes = len - 1; 19 | head->length = varint_decode(buf + 1, &bytes); 20 | if (bytes <= 0) return bytes; 21 | return 1 + bytes; 22 | } 23 | -------------------------------------------------------------------------------- /mqtt/mqtt_protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_MQTT_PROTOCOL_H_ 2 | #define HV_MQTT_PROTOCOL_H_ 3 | 4 | #include "hexport.h" 5 | 6 | #define DEFAULT_MQTT_PORT 1883 7 | 8 | #define MQTT_PROTOCOL_V31 3 9 | #define MQTT_PROTOCOL_V311 4 10 | #define MQTT_PROTOCOL_V5 5 // Not yet supproted 11 | 12 | #define MQTT_PROTOCOL_NAME "MQTT" 13 | #define MQTT_PROTOCOL_NAME_v31 "MQIsdp" 14 | 15 | /* 16 | * connect flags 17 | * 0 1 2 3-4 5 6 7 18 | * reserved clean_session has_will will_qos will_retain has_password has_username 19 | */ 20 | #define MQTT_CONN_CLEAN_SESSION 0x02 21 | #define MQTT_CONN_HAS_WILL 0x04 22 | #define MQTT_CONN_WILL_RETAIN 0x20 23 | #define MQTT_CONN_HAS_PASSWORD 0x40 24 | #define MQTT_CONN_HAS_USERNAME 0x80 25 | 26 | typedef enum { 27 | MQTT_TYPE_CONNECT = 1, 28 | MQTT_TYPE_CONNACK = 2, 29 | MQTT_TYPE_PUBLISH = 3, 30 | MQTT_TYPE_PUBACK = 4, 31 | MQTT_TYPE_PUBREC = 5, 32 | MQTT_TYPE_PUBREL = 6, 33 | MQTT_TYPE_PUBCOMP = 7, 34 | MQTT_TYPE_SUBSCRIBE = 8, 35 | MQTT_TYPE_SUBACK = 9, 36 | MQTT_TYPE_UNSUBSCRIBE = 10, 37 | MQTT_TYPE_UNSUBACK = 11, 38 | MQTT_TYPE_PINGREQ = 12, 39 | MQTT_TYPE_PINGRESP = 13, 40 | MQTT_TYPE_DISCONNECT = 14, 41 | } mqtt_type_e; 42 | 43 | typedef enum { 44 | MQTT_CONNACK_ACCEPTED = 0, 45 | MQTT_CONNACK_REFUSED_PROTOCOL_VERSION = 1, 46 | MQTT_CONNACK_REFUSED_IDENTIFIER_REJECTED = 2, 47 | MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE = 3, 48 | MQTT_CONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4, 49 | MQTT_CONNACK_REFUSED_NOT_AUTHORIZED = 5, 50 | } mqtt_connack_e; 51 | 52 | typedef struct mqtt_head_s { 53 | unsigned char type: 4; 54 | unsigned char dup: 1; 55 | unsigned char qos: 2; 56 | unsigned char retain: 1; 57 | unsigned int length; 58 | } mqtt_head_t; 59 | 60 | typedef struct mqtt_message_s { 61 | unsigned int topic_len; 62 | const char* topic; 63 | unsigned int payload_len; 64 | const char* payload; 65 | unsigned char qos; 66 | unsigned char retain; 67 | } mqtt_message_t; 68 | 69 | BEGIN_EXTERN_C 70 | 71 | #define DEFAULT_MQTT_PACKAGE_MAX_LENGTH (1 << 28) // 256M 72 | HV_INLINE int mqtt_estimate_length(mqtt_head_t* head) { 73 | // 28 bits => 4*7 bits varint 74 | return 1 + 4 + head->length; 75 | } 76 | 77 | HV_EXPORT int mqtt_head_pack(mqtt_head_t* head, unsigned char buf[]); 78 | HV_EXPORT int mqtt_head_unpack(mqtt_head_t* head, const unsigned char* buf, int len); 79 | 80 | END_EXTERN_C 81 | 82 | #endif // HV_MQTT_PROTOCOL_H_ 83 | -------------------------------------------------------------------------------- /protocol/README.md: -------------------------------------------------------------------------------- 1 | ## 目录结构 2 | 3 | ``` 4 | . 5 | ├── dns.h DNS协议 6 | ├── ftp.h FTP协议 7 | ├── icmp.h ICMP协议 8 | └── smtp.h SMTP协议 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /protocol/icmp.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_ICMP_H_ 2 | #define HV_ICMP_H_ 3 | 4 | #include "hexport.h" 5 | 6 | BEGIN_EXTERN_C 7 | 8 | // @param cnt: ping count 9 | // @return: ok count 10 | // @note: printd $CC -DPRINT_DEBUG 11 | HV_EXPORT int ping(const char* host, int cnt DEFAULT(4)); 12 | 13 | END_EXTERN_C 14 | 15 | #endif // HV_ICMP_H_ 16 | -------------------------------------------------------------------------------- /protocol/smtp.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_SMTP_H_ 2 | #define HV_SMTP_H_ 3 | 4 | #include "hexport.h" 5 | 6 | #define SMTP_PORT 25 7 | #define SMTPS_PORT 465 8 | #define SMTP_EOB "\r\n.\r\n" 9 | #define SMTP_EOB_LEN 5 10 | 11 | // smtp_command 12 | // XX(name, string) 13 | #define SMTP_COMMAND_MAP(XX)\ 14 | XX(HELO, HELO) \ 15 | XX(EHLO, EHLO) \ 16 | XX(AUTH, AUTH) \ 17 | XX(MAIL, MAIL FROM:) \ 18 | XX(RCPT, RCPT TO:) \ 19 | XX(DATA, DATA) \ 20 | XX(QUIT, QUIT) \ 21 | 22 | enum smtp_command { 23 | #define XX(name, string) SMTP_##name, 24 | SMTP_COMMAND_MAP(XX) 25 | #undef XX 26 | }; 27 | 28 | // smtp_status 29 | // XXX(code, name, string) 30 | #define SMTP_STATUS_MAP(XXX) \ 31 | XXX(220, READY, Ready) \ 32 | XXX(221, BYE, Bye) \ 33 | XXX(235, AUTH_SUCCESS, Authentication success) \ 34 | XXX(250, OK, OK) \ 35 | XXX(334, AUTH, Auth input) \ 36 | XXX(354, DATA, End with <CR><LF>.<CR><LF>) \ 37 | XXX(500, BAD_SYNTAX, Bad syntax) \ 38 | XXX(502, NOT_IMPLEMENTED,Command not implemented) \ 39 | XXX(503, BAD_SEQUENCE, Bad sequence of commands) \ 40 | XXX(504, UNRECOGNIZED_AUTH_TYPE, Unrecognized authentication type) \ 41 | XXX(535, AUTH_FAILED, Authentication failed) \ 42 | XXX(553, ERR_MAIL, Mailbox name not allowed) \ 43 | XXX(554, ERR_DATA, Transaction failed) \ 44 | 45 | enum smtp_status { 46 | #define XXX(code, name, string) SMTP_STATUS_##name = code, 47 | SMTP_STATUS_MAP(XXX) 48 | #undef XXX 49 | }; 50 | 51 | typedef struct mail_s { 52 | char* from; 53 | char* to; 54 | char* subject; 55 | char* body; 56 | } mail_t; 57 | 58 | BEGIN_EXTERN_C 59 | 60 | HV_EXPORT const char* smtp_command_str(enum smtp_command cmd); 61 | HV_EXPORT const char* smtp_status_str(enum smtp_status status); 62 | 63 | // cmd param\r\n 64 | HV_EXPORT int smtp_build_command(enum smtp_command cmd, const char* param, char* buf, int buflen); 65 | // status_code status_message\r\n 66 | 67 | HV_EXPORT int sendmail(const char* smtp_server, 68 | const char* username, 69 | const char* password, 70 | mail_t* mail); 71 | 72 | END_EXTERN_C 73 | 74 | #endif // HV_SMTP_H_ 75 | -------------------------------------------------------------------------------- /scripts/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(cd `dirname $0`; pwd) 4 | ROOT_DIR=${SCRIPT_DIR}/.. 5 | cd ${ROOT_DIR} 6 | 7 | bin/httpd -c etc/httpd.conf -s restart -d 8 | ps aux | grep httpd 9 | HTTPS=`netstat -atn | grep 8443 | wc -l` 10 | 11 | bin/http_client_test 12 | bin/curl -v http://127.0.0.1:8080/ 13 | if [ $HTTPS -gt 0 ]; then 14 | bin/curl -v https://127.0.0.1:8443/ 15 | fi 16 | bin/wrk -c 100 -t 2 -d 10s http://127.0.0.1:8080/ping 17 | -------------------------------------------------------------------------------- /scripts/cmake_cross_compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(cd `dirname $0`; pwd) 4 | ROOT_DIR=${SCRIPT_DIR}/.. 5 | 6 | if [ $# -gt 0 ]; then 7 | CROSS_COMPILE=$1 8 | else 9 | sudo apt install g++-arm-linux-gnueabi 10 | CROSS_COMPILE=arm-linux-gnueabi- 11 | fi 12 | echo CROSS_COMPILE=${CROSS_COMPILE} 13 | 14 | cd ${ROOT_DIR} 15 | . scripts/toolchain.sh export ${CROSS_COMPILE} 16 | BUILD_DIR=build/${HV_TARGET_OS}/${HV_TARGET_ARCH} 17 | echo BUILD_DIR=${BUILD_DIR} 18 | mkdir -p ${BUILD_DIR} 19 | cd ${BUILD_DIR} 20 | cmake ../../.. -DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_SYSTEM_NAME=$HV_TARGET_OS -DCMAKE_SYSTEM_PROCESSOR=$HV_TARGET_ARCH 21 | make libhv libhv_static 22 | cd ${ROOT_DIR} 23 | . scripts/toolchain.sh unset ${CROSS_COMPILE} 24 | 25 | echo 'Completed => ${BUILD_DIR}' 26 | -------------------------------------------------------------------------------- /scripts/consul_agent.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -r consul 4 | rm nohup.out 5 | mkdir consul 6 | 7 | print_help() { 8 | cat <<EOF 9 | Usage:cmd bind_ip 10 | 11 | example: 12 | ./consul_agent.sh 192.168.1.123 13 | EOF 14 | } 15 | 16 | main() { 17 | if [ $# -lt 1 ]; then 18 | print_help 19 | return 20 | fi 21 | bind_ip=$1 22 | nohup consul agent -server -ui -bootstrap-expect=1 -node=s1 -bind=${bind_ip} -client=0.0.0.0 -data-dir=/var/lib/consul -pid-file=consul/consul.pid -log-file=consul/consul.log & 23 | } 24 | 25 | main $@ 26 | -------------------------------------------------------------------------------- /scripts/coredump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | print_conf() { 4 | echo /proc/sys/kernel/core_uses_pid 5 | cat /proc/sys/kernel/core_uses_pid 6 | echo /proc/sys/kernel/core_pattern 7 | cat /proc/sys/kernel/core_pattern 8 | echo /proc/sys/fs/suid_dumpable 9 | cat /proc/sys/fs/suid_dumpable 10 | } 11 | 12 | print_conf 13 | echo "1" > /proc/sys/kernel/core_uses_pid 14 | echo "/tmp/core-%e-%p-%t" > /proc/sys/kernel/core_pattern 15 | echo "1" > /proc/sys/fs/suid_dumpable 16 | print_conf 17 | 18 | ulimit -c unlimited 19 | ulimit -a 20 | -------------------------------------------------------------------------------- /scripts/create_repo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p include lib src bin doc etc 3rd/include 3rd/lib dist 4 | touch README.md BUILD.md RELEASE.md CHANGELOG.md Makefile .gitignore 5 | git init 6 | 7 | # personal 8 | git submodule add https://github.com/ithewei/libhv.git src/hv 9 | cp src/hv/.gitignore . 10 | cp src/hv/.clang-format . 11 | cp src/hv/Makefile . 12 | cp -r src/hv/etc/* etc 13 | cp src/hv/examples/hmain_test.cpp src/main.cpp 14 | -------------------------------------------------------------------------------- /scripts/libhv.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_C_COMPILER $CC) 2 | set(CMAKE_CXX_COMPILER $CXX) 3 | 4 | set(CMAKE_SYSTEM_NAME $HV_TARGET_OS) 5 | set(CMAKE_SYSTEM_PROCESSOR $HV_TARGET_ARCH) 6 | 7 | if(WIN32) 8 | add_definitions(-D_WIN32_WINNT=0x600) 9 | endif() 10 | -------------------------------------------------------------------------------- /scripts/test-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(cd `dirname $0`; pwd) 4 | ROOT_DIR=${SCRIPT_DIR}/.. 5 | cd ${ROOT_DIR} 6 | 7 | bin/httpd -c etc/httpd.conf -s restart -d 8 | 9 | bin/ls 10 | bin/date 11 | bin/ifconfig 12 | bin/mkdir_p 123/456 13 | bin/rmdir_p 123/456 14 | 15 | bin/defer_test 16 | bin/hstring_test 17 | bin/hpath_test 18 | bin/hatomic_test 19 | bin/hatomic_cpp_test 20 | bin/hmutex_test 21 | bin/socketpair_test 22 | bin/threadpool_test 23 | bin/objectpool_test 24 | 25 | bin/curl -v localhost:8080 26 | bin/curl -v localhost:8080/ping 27 | bin/curl -v localhost:8080/echo -d "hello,world!" 28 | bin/curl -v localhost:8080/query?page_no=1\&page_size=10 29 | bin/curl -v localhost:8080/kv -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456' 30 | bin/curl -v localhost:8080/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}' 31 | bin/curl -v localhost:8080/form -F "user=admin pswd=123456" 32 | bin/curl -v localhost:8080/upload -F "file=@LICENSE" 33 | bin/curl -v localhost:8080/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello' 34 | bin/curl -v localhost:8080/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}' 35 | bin/curl -v localhost:8080/test -F 'bool=1 int=123 float=3.14 string=hello' 36 | bin/curl -v -X DELETE localhost:8080/group/test/user/123 37 | 38 | bin/httpd -s stop 39 | 40 | bin/htimer_test 41 | -------------------------------------------------------------------------------- /scripts/unittest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(cd `dirname $0`; pwd) 4 | ROOT_DIR=${SCRIPT_DIR}/.. 5 | cd ${ROOT_DIR} 6 | 7 | bin/rbtree_test 8 | bin/hbase_test 9 | bin/date 10 | bin/ifconfig 11 | bin/mkdir_p 123/456 12 | bin/ls 13 | bin/rmdir_p 123/456 14 | bin/hlog_test 15 | 16 | bin/base64 17 | bin/md5 18 | bin/sha1 19 | 20 | bin/defer_test 21 | bin/hstring_test 22 | bin/hpath_test 23 | bin/hurl_test 24 | # bin/hatomic_test 25 | # bin/hatomic_cpp_test 26 | # bin/hthread_test 27 | # bin/hmutex_test 28 | bin/socketpair_test 29 | # bin/threadpool_test 30 | # bin/objectpool_test 31 | bin/sizeof_test 32 | -------------------------------------------------------------------------------- /scripts/websocket_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # pip3 install websockets 4 | 5 | import asyncio 6 | import websockets 7 | 8 | async def echo(websocket): 9 | async for message in websocket: 10 | print(message) 11 | await websocket.send(message) 12 | 13 | async def serve(port): 14 | async with websockets.serve(echo, "0.0.0.0", port): 15 | await asyncio.Future() 16 | 17 | if __name__ == "__main__": 18 | asyncio.run(serve(9999)) 19 | -------------------------------------------------------------------------------- /ssl/hssl.c: -------------------------------------------------------------------------------- 1 | #include "hssl.h" 2 | 3 | hssl_ctx_t g_ssl_ctx = NULL; 4 | 5 | hssl_ctx_t hssl_ctx_init(hssl_ctx_init_param_t* param) { 6 | if (g_ssl_ctx == NULL) { 7 | g_ssl_ctx = hssl_ctx_new(param); 8 | } 9 | return g_ssl_ctx; 10 | } 11 | 12 | void hssl_ctx_cleanup(hssl_ctx_t ssl_ctx) { 13 | hssl_ctx_free(ssl_ctx); 14 | if (g_ssl_ctx == ssl_ctx) { 15 | g_ssl_ctx = NULL; 16 | } 17 | } 18 | 19 | hssl_ctx_t hssl_ctx_instance() { 20 | if (g_ssl_ctx == NULL) { 21 | g_ssl_ctx = hssl_ctx_new(NULL); 22 | } 23 | return g_ssl_ctx; 24 | } 25 | -------------------------------------------------------------------------------- /ssl/hssl.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_SSL_H_ 2 | #define HV_SSL_H_ 3 | 4 | #include "hexport.h" 5 | 6 | #include "hplatform.h" 7 | #if !defined(WITH_OPENSSL) && \ 8 | !defined(WITH_GNUTLS) && \ 9 | !defined(WITH_MBEDTLS) 10 | #ifdef OS_WIN 11 | #define WITH_WINTLS 12 | #ifdef _MSC_VER 13 | #pragma comment(lib, "secur32.lib") 14 | #pragma comment(lib, "crypt32.lib") 15 | #endif 16 | #elif defined(OS_DARWIN) 17 | #define WITH_APPLETLS 18 | #else 19 | #define HV_WITHOUT_SSL 20 | #endif 21 | #endif 22 | 23 | typedef void* hssl_ctx_t; ///> SSL_CTX 24 | typedef void* hssl_t; ///> SSL 25 | 26 | enum { 27 | HSSL_SERVER = 0, 28 | HSSL_CLIENT = 1, 29 | }; 30 | 31 | enum { 32 | HSSL_OK = 0, 33 | HSSL_ERROR = -1, 34 | HSSL_WANT_READ = -2, 35 | HSSL_WANT_WRITE = -3, 36 | HSSL_WOULD_BLOCK = -4, 37 | }; 38 | 39 | typedef struct { 40 | const char* crt_file; 41 | const char* key_file; 42 | const char* ca_file; 43 | const char* ca_path; 44 | short verify_peer; 45 | short endpoint; // HSSL_SERVER / HSSL_CLIENT 46 | } hssl_ctx_opt_t, hssl_ctx_init_param_t; 47 | 48 | BEGIN_EXTERN_C 49 | 50 | /* 51 | const char* hssl_backend() { 52 | #ifdef WITH_OPENSSL 53 | return "openssl"; 54 | #elif defined(WITH_GNUTLS) 55 | return "gnutls"; 56 | #elif defined(WITH_MBEDTLS) 57 | return "mbedtls"; 58 | #else 59 | return "nossl"; 60 | #endif 61 | } 62 | */ 63 | HV_EXPORT const char* hssl_backend(); 64 | #define HV_WITH_SSL (strcmp(hssl_backend(), "nossl") != 0) 65 | 66 | HV_EXPORT extern hssl_ctx_t g_ssl_ctx; 67 | HV_EXPORT hssl_ctx_t hssl_ctx_init(hssl_ctx_init_param_t* param); 68 | HV_EXPORT void hssl_ctx_cleanup(hssl_ctx_t ssl_ctx); 69 | HV_EXPORT hssl_ctx_t hssl_ctx_instance(); 70 | 71 | HV_EXPORT hssl_ctx_t hssl_ctx_new(hssl_ctx_opt_t* opt); 72 | HV_EXPORT void hssl_ctx_free(hssl_ctx_t ssl_ctx); 73 | 74 | HV_EXPORT hssl_t hssl_new(hssl_ctx_t ssl_ctx, int fd); 75 | HV_EXPORT void hssl_free(hssl_t ssl); 76 | 77 | HV_EXPORT int hssl_accept(hssl_t ssl); 78 | HV_EXPORT int hssl_connect(hssl_t ssl); 79 | HV_EXPORT int hssl_read(hssl_t ssl, void* buf, int len); 80 | HV_EXPORT int hssl_write(hssl_t ssl, const void* buf, int len); 81 | HV_EXPORT int hssl_close(hssl_t ssl); 82 | 83 | HV_EXPORT int hssl_set_sni_hostname(hssl_t ssl, const char* hostname); 84 | 85 | #ifdef WITH_OPENSSL 86 | HV_EXPORT int hssl_ctx_set_alpn_protos(hssl_ctx_t ssl_ctx, const unsigned char* protos, unsigned int protos_len); 87 | #endif 88 | 89 | END_EXTERN_C 90 | 91 | #endif // HV_SSL_H_ 92 | -------------------------------------------------------------------------------- /ssl/nossl.c: -------------------------------------------------------------------------------- 1 | #include "hssl.h" 2 | 3 | #ifdef HV_WITHOUT_SSL 4 | 5 | const char* hssl_backend() { 6 | return "nossl"; 7 | } 8 | 9 | hssl_ctx_t hssl_ctx_new(hssl_ctx_opt_t* opt) { 10 | fprintf(stderr, "Please recompile WITH_SSL.\n"); 11 | return NULL; 12 | } 13 | 14 | void hssl_ctx_free(hssl_ctx_t ssl_ctx) { 15 | } 16 | 17 | hssl_t hssl_new(hssl_ctx_t ssl_ctx, int fd) { 18 | return (void*)(intptr_t)fd; 19 | } 20 | 21 | void hssl_free(hssl_t ssl) { 22 | } 23 | 24 | int hssl_accept(hssl_t ssl) { 25 | return 0; 26 | } 27 | 28 | int hssl_connect(hssl_t ssl) { 29 | return 0; 30 | } 31 | 32 | int hssl_read(hssl_t ssl, void* buf, int len) { 33 | int fd = (intptr_t)ssl; 34 | return read(fd, buf, len); 35 | } 36 | 37 | int hssl_write(hssl_t ssl, const void* buf, int len) { 38 | int fd = (intptr_t)ssl; 39 | return write(fd, buf, len); 40 | } 41 | 42 | int hssl_close(hssl_t ssl) { 43 | return 0; 44 | } 45 | 46 | int hssl_set_sni_hostname(hssl_t ssl, const char* hostname) { 47 | return 0; 48 | } 49 | 50 | #endif // HV_WITHOUT_SSL 51 | -------------------------------------------------------------------------------- /unittest/connect_test.c: -------------------------------------------------------------------------------- 1 | #include "hsocket.h" 2 | #include "htime.h" 3 | 4 | int main(int argc, char* argv[]) { 5 | if (argc < 3) { 6 | printf("Usage: cmd ip port\n"); 7 | return -10; 8 | } 9 | 10 | const char* ip = argv[1]; 11 | int port = atoi(argv[2]); 12 | 13 | unsigned int start_time = gettick_ms(); 14 | int ret = ConnectNonblock(ip, port); 15 | unsigned int end_time = gettick_ms(); 16 | printf("ConnectNonblock[%s:%d] retval=%d cost=%ums\n", ip, port, ret, end_time-start_time); 17 | 18 | start_time = gettick_ms(); 19 | ret = ConnectTimeout(ip, port, 3000); 20 | end_time = gettick_ms(); 21 | printf("ConnectTimeout[%s:%d] retval=%d cost=%ums\n", ip, port, ret, end_time-start_time); 22 | 23 | start_time = gettick_ms(); 24 | ret = Connect(ip, port, 0); 25 | end_time = gettick_ms(); 26 | printf("ConnectBlock[%s:%d] retval=%d cost=%ums\n", ip, port, ret, end_time-start_time); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /unittest/date_test.c: -------------------------------------------------------------------------------- 1 | #include "htime.h" 2 | 3 | int main(int argc, char* argv[]) { 4 | datetime_t dt = datetime_now(); 5 | char buf1[DATETIME_FMT_BUFLEN]; 6 | datetime_fmt(&dt, buf1); 7 | puts(buf1); 8 | datetime_fmt_iso(&dt, buf1); 9 | puts(buf1); 10 | 11 | time_t ts = datetime_mktime(&dt); 12 | char buf2[GMTIME_FMT_BUFLEN]; 13 | gmtime_fmt(ts, buf2); 14 | puts(buf2); 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /unittest/defer_test.cpp: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "hscope.h" 4 | 5 | int main() { 6 | defer ( 7 | printf("1\n"); 8 | printf("2\n"); 9 | ) 10 | 11 | defer ( 12 | printf("3\n"); 13 | printf("4\n"); 14 | ) 15 | 16 | defer(printf("hello\n");) 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /unittest/hatomic_test.c: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "hatomic.h" 4 | #include "hthread.h" 5 | 6 | hatomic_flag_t flag = HATOMIC_FLAG_INIT; 7 | hatomic_t cnt = HATOMIC_VAR_INIT(0); 8 | 9 | HTHREAD_ROUTINE(test_hatomic_flag) { 10 | if (!hatomic_flag_test_and_set(&flag)) { 11 | printf("tid=%ld flag 0=>1\n", hv_gettid()); 12 | } 13 | else { 14 | printf("tid=%ld flag=1\n", hv_gettid()); 15 | } 16 | return 0; 17 | } 18 | 19 | HTHREAD_ROUTINE(test_hatomic) { 20 | for (int i = 0; i < 10; ++i) { 21 | long n = hatomic_inc(&cnt); 22 | printf("tid=%ld cnt=%ld\n", hv_gettid(), n); 23 | hv_delay(1); 24 | } 25 | return 0; 26 | } 27 | 28 | int main() { 29 | for (int i = 0; i < 10; ++i) { 30 | hthread_create(test_hatomic_flag, NULL); 31 | } 32 | 33 | for (int i = 0; i < 10; ++i) { 34 | hthread_create(test_hatomic, NULL); 35 | } 36 | 37 | hv_delay(1000); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /unittest/hatomic_test.cpp: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "hatomic.h" 4 | #include "hthread.h" 5 | 6 | hatomic_flag_t flag = HATOMIC_FLAG_INIT; 7 | hatomic_t cnt = HATOMIC_VAR_INIT(0); 8 | 9 | HTHREAD_ROUTINE(test_hatomic_flag) { 10 | if (!hatomic_flag_test_and_set(&flag)) { 11 | printf("tid=%ld flag 0=>1\n", hv_gettid()); 12 | } 13 | else { 14 | printf("tid=%ld flag=1\n", hv_gettid()); 15 | } 16 | return 0; 17 | } 18 | 19 | HTHREAD_ROUTINE(test_hatomic) { 20 | for (int i = 0; i < 10; ++i) { 21 | long n = hatomic_inc(&cnt); 22 | printf("tid=%ld cnt=%ld\n", hv_gettid(), n); 23 | hv_delay(1); 24 | } 25 | return 0; 26 | } 27 | 28 | int main() { 29 | for (int i = 0; i < 10; ++i) { 30 | hthread_create(test_hatomic_flag, NULL); 31 | } 32 | 33 | for (int i = 0; i < 10; ++i) { 34 | hthread_create(test_hatomic, NULL); 35 | } 36 | 37 | hv_delay(1000); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /unittest/hlog_test.c: -------------------------------------------------------------------------------- 1 | #include "hlog.h" 2 | 3 | int main(int argc, char* argv[]) { 4 | char logfile[] = "hlog_test.log"; 5 | hlog_set_file(logfile); 6 | hlog_set_level(LOG_LEVEL_INFO); 7 | 8 | // test log max filesize 9 | hlog_set_max_filesize_by_str("1M"); 10 | for (int i = 100000; i <= 999999; ++i) { 11 | hlogi("[%d] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", i); 12 | } 13 | 14 | // test log level 15 | hlogd("%s", "not show debug"); 16 | hlogi("%s", "show info"); 17 | hlogw("%s", "show warn"); 18 | hloge("%s", "show error"); 19 | hlogf("%s", "show fatal"); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /unittest/hpath_test.cpp: -------------------------------------------------------------------------------- 1 | #include "hpath.h" 2 | 3 | int main(int argc, char** argv) { 4 | std::string filepath = HPath::join("/mnt/share/image", "test.jpg"); 5 | std::string basename = HPath::basename(filepath); 6 | std::string dirname = HPath::dirname(filepath); 7 | std::string filename = HPath::filename(filepath); 8 | std::string suffixname = HPath::suffixname(filepath); 9 | printf("filepath = %s\n", filepath.c_str()); 10 | printf("basename = %s\n", basename.c_str()); 11 | printf("dirname = %s\n", dirname.c_str()); 12 | printf("filename = %s\n", filename.c_str()); 13 | printf("suffixname = %s\n", suffixname.c_str()); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /unittest/hstring_test.cpp: -------------------------------------------------------------------------------- 1 | #include "hstring.h" 2 | using namespace hv; 3 | 4 | int main(int argc, char** argv) { 5 | std::string str1 = "a1B2*C3d4=="; 6 | std::string str2 = "a1B2*C3d4=="; 7 | println("toupper=" + toupper(str1)); 8 | println("tolower=" + tolower(str2)); 9 | std::string str3 = "abcdefg"; 10 | println("reverse=" + reverse(str3)); 11 | 12 | std::string str4 = "123456789"; 13 | printf("startswith=%d\nendswith=%d\ncontains=%d\n", 14 | (int)startswith(str4, "123"), 15 | (int)endswith(str4, "789"), 16 | (int)contains(str4, "456")); 17 | 18 | std::string str5 = asprintf("%s%d", "hello", 5); 19 | println("asprintf=" + str5); 20 | 21 | std::string str6("123,456,789"); 22 | StringList strlist = split(str6, ','); 23 | println("split " + str6); 24 | for (auto& str : strlist) { 25 | println(str); 26 | } 27 | 28 | std::string str7("user=admin&pswd=123456"); 29 | hv::KeyValue kv = splitKV(str7, '&', '='); 30 | for (auto& pair : kv) { 31 | printf("%s=%s\n", pair.first.c_str(), pair.second.c_str()); 32 | } 33 | 34 | std::string str8("<stdio.h>"); 35 | std::string str9 = trim_pairs(str8); 36 | println("trim_pairs=" + str9); 37 | 38 | std::string str10("<title>{{title}}</title>"); 39 | std::string str11 = replace(str10, "{{title}}", "Home"); 40 | println("replace=" + str11); 41 | 42 | NetAddr addr1("0.0.0.0:8080"); 43 | println(addr1.to_string()); 44 | 45 | NetAddr addr2("[::0]:8080"); 46 | println(addr2.to_string()); 47 | 48 | NetAddr addr3(":8080"); 49 | println(addr3.to_string()); 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /unittest/hthread_test.cpp: -------------------------------------------------------------------------------- 1 | #include "hthread.h" 2 | #include "htime.h" 3 | 4 | HTHREAD_ROUTINE(test_thread1) { 5 | int cnt = 10; 6 | while (cnt-- > 0) { 7 | printf("tid=%ld time=%llums\n", hv_gettid(), gettimeofday_ms()); 8 | hv_msleep(100); 9 | } 10 | return 0; 11 | } 12 | 13 | class TestThread2 : public HThread { 14 | protected: 15 | virtual void run() { 16 | int cnt = 10; 17 | while (cnt-- > 0) { 18 | printf("tid=%ld time=%llums\n", hv_gettid(), gettimeofday_ms()); 19 | hv_msleep(100); 20 | } 21 | } 22 | }; 23 | 24 | class TestThread3 : public HThread { 25 | protected: 26 | virtual bool doPrepare() { 27 | printf("doPrepare\n"); 28 | return true; 29 | } 30 | 31 | virtual void doTask() { 32 | printf("tid=%ld time=%llums\n", hv_gettid(), gettimeofday_ms()); 33 | } 34 | 35 | virtual bool doFinish() { 36 | printf("doFinish\n"); 37 | return true; 38 | } 39 | }; 40 | 41 | int main() { 42 | printf("c-style hthread_create\n"); 43 | hthread_t thread1 = hthread_create(test_thread1, NULL); 44 | hthread_join(thread1); 45 | 46 | printf("cpp-style override HThread::run\n"); 47 | TestThread2 thread2; 48 | thread2.start(); 49 | thread2.stop(); 50 | 51 | printf("cpp-style override HThread::doTask\n"); 52 | TestThread3 thread3; 53 | thread3.setSleepPolicy(HThread::SLEEP_UNTIL, 100); 54 | thread3.start(); 55 | hv_sleep(1); 56 | thread3.stop(); 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /unittest/hurl_test.cpp: -------------------------------------------------------------------------------- 1 | #include <assert.h> 2 | 3 | #include "hurl.h" 4 | 5 | int main(int argc, char** argv) { 6 | std::string strURL = "http://www.example.com/path?query#fragment"; 7 | HUrl url; 8 | if (!url.parse(strURL)) { 9 | printf("parse url %s error!\n", strURL.c_str()); 10 | return -1; 11 | } 12 | std::string dumpURL = url.dump(); 13 | printf("%s =>\n%s\n", strURL.c_str(), dumpURL.c_str()); 14 | assert(strURL == dumpURL); 15 | 16 | const char* str = "中 文"; 17 | std::string escaped = HUrl::escape(str); 18 | std::string unescaped = HUrl::unescape(escaped.c_str()); 19 | printf("%s => %s\n", str, escaped.c_str()); 20 | assert(str == unescaped); 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /unittest/ifconfig_test.cpp: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "ifconfig.h" 4 | 5 | int main() { 6 | std::vector<ifconfig_t> ifcs; 7 | ifconfig(ifcs); 8 | for (auto& item : ifcs) { 9 | printf("%s\nip: %s\nmask: %s\nbroadcast: %s\nmac: %s\n\n", 10 | item.name, 11 | item.ip, 12 | item.mask, 13 | item.broadcast, 14 | item.mac); 15 | } 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /unittest/listdir_test.cpp: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "hdir.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | const char* dir = "."; 7 | if (argc > 1) { 8 | dir = argv[1]; 9 | } 10 | std::list<hdir_t> dirs; 11 | listdir(dir, dirs); 12 | for (auto& item : dirs) { 13 | printf("%c%c%c%c%c%c%c%c%c%c\t", 14 | item.type, 15 | (item.mode & 0400) ? 'r' : '-', 16 | (item.mode & 0200) ? 'w' : '-', 17 | (item.mode & 0100) ? 'x' : '-', 18 | (item.mode & 0040) ? 'r' : '-', 19 | (item.mode & 0020) ? 'w' : '-', 20 | (item.mode & 0010) ? 'x' : '-', 21 | (item.mode & 0004) ? 'r' : '-', 22 | (item.mode & 0002) ? 'w' : '-', 23 | (item.mode & 0001) ? 'x' : '-'); 24 | float hsize; 25 | if (item.size < 1024) { 26 | printf("%lu\t", item.size); 27 | } 28 | else if ((hsize = item.size/1024.0f) < 1024.0f) { 29 | printf("%.1fK\t", hsize); 30 | } 31 | else if ((hsize /= 1024.0f) < 1024.0f) { 32 | printf("%.1fM\t", hsize); 33 | } 34 | else { 35 | hsize /= 1024.0f; 36 | printf("%.1fG\t", hsize); 37 | } 38 | struct tm* tm = localtime(&item.mtime); 39 | printf("%04d-%02d-%02d %02d:%02d:%02d\t", 40 | tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); 41 | printf("%s%s\n", item.name, item.type == 'd' ? "/" : ""); 42 | } 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /unittest/md5_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @build: gcc -o bin/md5 unittest/md5_test.c util/md5.c -I. -Iutil 3 | * 4 | */ 5 | 6 | #include <stdio.h> 7 | #include <stdlib.h> 8 | #include <string.h> 9 | #include <assert.h> 10 | 11 | #include "md5.h" 12 | 13 | static void test() { 14 | unsigned char ch = '1'; 15 | char md5[33] = {0}; 16 | hv_md5_hex(&ch, 1, md5, sizeof(md5)); 17 | assert(strcmp(md5, "c4ca4238a0b923820dcc509a6f75849b") == 0); 18 | } 19 | 20 | int main(int argc, char* argv[]) { 21 | test(); 22 | 23 | if (argc < 2) { 24 | printf("Usage: md5 file\n"); 25 | printf(" md5 -s string\n"); 26 | return -10; 27 | } 28 | 29 | char md5[33] = {0}; 30 | 31 | if (argc == 2) { 32 | const char* filepath = argv[1]; 33 | FILE* fp = fopen(filepath, "rb"); 34 | if (fp == NULL) { 35 | printf("Open file '%s' failed!\n", filepath); 36 | return -20; 37 | } 38 | fseek(fp, 0, SEEK_END); 39 | long filesize = ftell(fp); 40 | // printf("filesize=%ld\n", filesize); 41 | fseek(fp, 0, SEEK_SET); 42 | unsigned char* filebuf = (unsigned char*)malloc(filesize); 43 | size_t nread = fread(filebuf, 1, filesize, fp); 44 | assert(nread == filesize); 45 | hv_md5_hex(filebuf, filesize, md5, sizeof(md5)); 46 | 47 | free(filebuf); 48 | fclose(fp); 49 | } 50 | else if (argc == 3) { 51 | const char* flags = argv[1]; 52 | if (flags[0] == '-' && flags[1] == 's') { 53 | hv_md5_hex((unsigned char*)argv[2], strlen(argv[2]), md5, sizeof(md5)); 54 | } 55 | else { 56 | printf("Unrecognized flags '%s'\n", flags); 57 | return -40; 58 | } 59 | } 60 | 61 | puts(md5); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /unittest/mkdir_test.c: -------------------------------------------------------------------------------- 1 | #include "hbase.h" 2 | 3 | int main(int argc, char* argv[]) { 4 | if (argc < 2) { 5 | printf("Usage: mkdir_p dir\n"); 6 | return -1; 7 | } 8 | const char* dir = argv[1]; 9 | return hv_mkdir_p(dir); 10 | } 11 | -------------------------------------------------------------------------------- /unittest/nslookup_test.c: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "hplatform.h" // inet_ntop 4 | #include "dns.h" // nslookup 5 | 6 | int main(int argc, char* argv[]) { 7 | if (argc < 2) { 8 | printf("Usage: nslookup domain [nameserver]\n"); 9 | return -1; 10 | } 11 | 12 | const char* domain = argv[1]; 13 | const char* nameserver = "127.0.1.1"; 14 | #ifndef OS_LINUX 15 | nameserver = "114.114.114.114"; 16 | // nameserver = "8.8.8.8"; 17 | #endif 18 | 19 | if (argc > 2) { 20 | nameserver = argv[2]; 21 | } 22 | 23 | uint32_t addrs[16]; 24 | int naddr = nslookup(domain, addrs, 16, nameserver); 25 | if (naddr < 0) { 26 | return naddr; 27 | } 28 | char ip[16]; 29 | for (int i = 0; i < naddr; ++i) { 30 | inet_ntop(AF_INET, (void*)&addrs[i], ip, 16); 31 | printf("%s\n", ip); 32 | } 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /unittest/objectpool_test.cpp: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | #include <thread> 3 | 4 | #include "hobjectpool.h" 5 | 6 | #ifdef _WIN32 7 | #include <windows.h> 8 | #else 9 | #include <unistd.h> 10 | #endif 11 | 12 | void msleep(unsigned int ms) { 13 | #ifdef _WIN32 14 | Sleep(ms); 15 | #else 16 | usleep(ms*1000); 17 | #endif 18 | } 19 | 20 | class Task { 21 | public: 22 | Task() {printf("Task()\n");} 23 | ~Task() {printf("~Task()\n");} 24 | 25 | void Do() { 26 | printf("%p start do...\n", this); 27 | msleep(4000); 28 | printf("%p end do\n", this); 29 | } 30 | }; 31 | 32 | HObjectPool<Task> task_pool(1, 5); 33 | 34 | void task_thread(int id) { 35 | printf("thread %d run...\n", id); 36 | HPoolObject<Task> pTask(task_pool); 37 | if (pTask) { 38 | pTask->Do(); 39 | } 40 | else { 41 | printf("No available task in pool\n"); 42 | } 43 | printf("thread %d exit\n", id); 44 | } 45 | 46 | int main(int argc, char** argv) { 47 | for (int i = 0; i < 10; ++i) { 48 | new std::thread(task_thread, i); 49 | } 50 | msleep(5000); 51 | for (int i = 10; i < 20; ++i) { 52 | new std::thread(task_thread, i); 53 | } 54 | msleep(10000); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /unittest/ping_test.c: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | #include "icmp.h" 3 | #include "hplatform.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | if (argc < 2) { 7 | printf("Usage: ping host|ip\n"); 8 | return -10; 9 | } 10 | 11 | char* host = argv[1]; 12 | int ping_cnt = 4; 13 | int ok_cnt = ping(host, ping_cnt); 14 | printf("ping %d count, %d ok.\n", ping_cnt, ok_cnt); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /unittest/rmdir_test.c: -------------------------------------------------------------------------------- 1 | #include "hbase.h" 2 | 3 | int main(int argc, char* argv[]) { 4 | if (argc < 2) { 5 | printf("Usage: rmdir_p dir\n"); 6 | return -1; 7 | } 8 | const char* dir = argv[1]; 9 | return hv_rmdir_p(dir); 10 | } 11 | -------------------------------------------------------------------------------- /unittest/sendmail_test.c: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "smtp.h" 4 | 5 | int main(int argc, char** argv) { 6 | if (argc < 8) { 7 | printf("Usage: sendmail smtp_server username password from to subject body\n"); 8 | return -10; 9 | } 10 | 11 | const char* smtp_server = argv[1]; 12 | const char* username = argv[2]; 13 | const char* password = argv[3]; 14 | mail_t mail; 15 | mail.from = argv[4]; 16 | mail.to = argv[5]; 17 | mail.subject = argv[6]; 18 | mail.body = argv[7]; 19 | 20 | int status_code = sendmail(smtp_server, username, password, &mail); 21 | printf("sendmail: %d %s\n", status_code, smtp_status_str((enum smtp_status)status_code)); 22 | 23 | return status_code == SMTP_STATUS_OK ? 0 : status_code; 24 | } 25 | -------------------------------------------------------------------------------- /unittest/sha1_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @build: gcc -o bin/sha1 unittest/sha1_test.c util/sha1.c -I. -Iutil 3 | * 4 | */ 5 | 6 | #include <stdio.h> 7 | #include <stdlib.h> 8 | #include <string.h> 9 | #include <assert.h> 10 | 11 | #include "sha1.h" 12 | 13 | static void test() { 14 | unsigned char ch = '1'; 15 | char sha1[41] = {0}; 16 | hv_sha1_hex(&ch, 1, sha1, sizeof(sha1)); 17 | assert(strcmp(sha1, "356a192b7913b04c54574d18c28d46e6395428ab") == 0); 18 | } 19 | 20 | int main(int argc, char* argv[]) { 21 | test(); 22 | 23 | if (argc < 2) { 24 | printf("Usage: sha1 file\n"); 25 | printf(" sha1 -s string\n"); 26 | return -10; 27 | } 28 | 29 | char sha1[41] = {0}; 30 | 31 | if (argc == 2) { 32 | const char* filepath = argv[1]; 33 | FILE* fp = fopen(filepath, "rb"); 34 | if (fp == NULL) { 35 | printf("Open file '%s' failed!\n", filepath); 36 | return -20; 37 | } 38 | fseek(fp, 0, SEEK_END); 39 | long filesize = ftell(fp); 40 | // printf("filesize=%ld\n", filesize); 41 | fseek(fp, 0, SEEK_SET); 42 | unsigned char* filebuf = (unsigned char*)malloc(filesize); 43 | size_t nread = fread(filebuf, 1, filesize, fp); 44 | assert(nread == filesize); 45 | hv_sha1_hex(filebuf, filesize, sha1, sizeof(sha1)); 46 | 47 | free(filebuf); 48 | fclose(fp); 49 | } 50 | else if (argc == 3) { 51 | const char* flags = argv[1]; 52 | if (flags[0] == '-' && flags[1] == 's') { 53 | hv_sha1_hex((unsigned char*)argv[2], strlen(argv[2]), sha1, sizeof(sha1)); 54 | } 55 | else { 56 | printf("Unrecognized flags '%s'\n", flags); 57 | return -40; 58 | } 59 | } 60 | 61 | puts(sha1); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /unittest/socketpair_test.c: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "hsocket.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | int sockfds[2]; 7 | if (Socketpair(AF_INET, SOCK_STREAM, 0, sockfds) != 0) { 8 | printf("socketpair failed!\n"); 9 | return -1; 10 | } 11 | printf("Socketpair %d<=>%d\n", sockfds[0], sockfds[1]); 12 | 13 | char sendbuf[] = "hello,world!"; 14 | char recvbuf[1460]; 15 | int nsend = send(sockfds[0], sendbuf, strlen(sendbuf), 0); 16 | printf("sockfd:%d send %d bytes: %s\n", sockfds[0], nsend, sendbuf); 17 | memset(recvbuf, 0, sizeof(recvbuf)); 18 | int nrecv = recv(sockfds[1], recvbuf, sizeof(recvbuf), 0); 19 | printf("sockfd:%d recv %d bytes: %s\n", sockfds[1], nrecv, recvbuf); 20 | 21 | closesocket(sockfds[0]); 22 | closesocket(sockfds[1]); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /unittest/synchronized_test.cpp: -------------------------------------------------------------------------------- 1 | #include "hthread.h" 2 | #include "hmutex.h" 3 | 4 | #define THREAD_NUM 10 5 | std::mutex g_mutex; 6 | 7 | HTHREAD_ROUTINE(test_synchronized) { 8 | synchronized(g_mutex) { 9 | hv_delay(1000); 10 | printf("tid=%ld time=%llus\n", hv_gettid(), (unsigned long long)time(NULL)); 11 | } 12 | return 0; 13 | } 14 | 15 | int main() { 16 | hthread_t threads[THREAD_NUM]; 17 | for (int i = 0; i < THREAD_NUM; ++i) { 18 | threads[i] = hthread_create(test_synchronized, NULL); 19 | } 20 | for (int i = 0; i < THREAD_NUM; ++i) { 21 | hthread_join(threads[i]); 22 | } 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /unittest/threadpool_test.cpp: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | 3 | #include "hthreadpool.h" 4 | #include "hthread.h" 5 | #include "htime.h" 6 | 7 | void print_task(int i) { 8 | printf("thread[%ld]: task[%d]\n", hv_gettid(), i); 9 | hv_sleep(1); 10 | } 11 | 12 | int main(int argc, char** argv) { 13 | HThreadPool tp(1, 4); 14 | tp.start(); 15 | 16 | int i = 0; 17 | for (; i < 10; ++i) { 18 | tp.commit(print_task, i); 19 | } 20 | 21 | tp.wait(); 22 | 23 | for (; i < 20; ++i) { 24 | tp.commit(print_task, i); 25 | } 26 | 27 | tp.wait(); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /util/README.md: -------------------------------------------------------------------------------- 1 | ## 目录结构 2 | 3 | ``` 4 | . 5 | ├── base64.h BASE64编解码 6 | ├── md5.h MD5数字摘要 7 | └── sha1.h SHA1安全散列算法 8 | 9 | ``` 10 | -------------------------------------------------------------------------------- /util/base64.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_BASE64_H_ 2 | #define HV_BASE64_H_ 3 | 4 | #include "hexport.h" 5 | 6 | #define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4) 7 | #define BASE64_DECODE_OUT_SIZE(s) (((s) + 3) / 4 * 3) 8 | 9 | BEGIN_EXTERN_C 10 | 11 | // @return encoded size 12 | HV_EXPORT int hv_base64_encode(const unsigned char *in, unsigned int inlen, char *out); 13 | 14 | // @return decoded size 15 | HV_EXPORT int hv_base64_decode(const char *in, unsigned int inlen, unsigned char *out); 16 | 17 | END_EXTERN_C 18 | 19 | #ifdef __cplusplus 20 | 21 | #include <string.h> 22 | #include <string> 23 | 24 | namespace hv { 25 | 26 | HV_INLINE std::string Base64Encode(const unsigned char* data, unsigned int len) { 27 | int encoded_size = BASE64_ENCODE_OUT_SIZE(len); 28 | std::string encoded_str(encoded_size + 1, 0); 29 | encoded_size = hv_base64_encode(data, len, (char*)encoded_str.data()); 30 | encoded_str.resize(encoded_size); 31 | return encoded_str; 32 | } 33 | 34 | HV_INLINE std::string Base64Decode(const char* str, unsigned int len = 0) { 35 | if (len == 0) len = strlen(str); 36 | int decoded_size = BASE64_DECODE_OUT_SIZE(len); 37 | std::string decoded_buf(decoded_size + 1, 0); 38 | decoded_size = hv_base64_decode(str, len, (unsigned char*)decoded_buf.data()); 39 | if (decoded_size > 0) { 40 | decoded_buf.resize(decoded_size); 41 | } else { 42 | decoded_buf.clear(); 43 | } 44 | return decoded_buf; 45 | } 46 | 47 | } 48 | #endif 49 | 50 | #endif // HV_BASE64_H_ 51 | -------------------------------------------------------------------------------- /util/md5.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_MD5_H_ 2 | #define HV_MD5_H_ 3 | 4 | #include "hexport.h" 5 | 6 | typedef struct { 7 | unsigned int count[2]; 8 | unsigned int state[4]; 9 | unsigned char buffer[64]; 10 | } HV_MD5_CTX; 11 | 12 | BEGIN_EXTERN_C 13 | 14 | HV_EXPORT void HV_MD5Init(HV_MD5_CTX *ctx); 15 | HV_EXPORT void HV_MD5Update(HV_MD5_CTX *ctx, unsigned char *input, unsigned int inputlen); 16 | HV_EXPORT void HV_MD5Final(HV_MD5_CTX *ctx, unsigned char digest[16]); 17 | 18 | HV_EXPORT void hv_md5(unsigned char* input, unsigned int inputlen, unsigned char digest[16]); 19 | 20 | // NOTE: if outputlen > 32: output[32] = '\0' 21 | HV_EXPORT void hv_md5_hex(unsigned char* input, unsigned int inputlen, char* output, unsigned int outputlen); 22 | 23 | END_EXTERN_C 24 | 25 | #endif // HV_MD5_H_ 26 | -------------------------------------------------------------------------------- /util/sha1.h: -------------------------------------------------------------------------------- 1 | #ifndef HV_SHA1_H_ 2 | #define HV_SHA1_H_ 3 | 4 | /* 5 | SHA-1 in C 6 | By Steve Reid <steve@edmweb.com> 7 | 100% Public Domain 8 | */ 9 | 10 | /* for uint32_t */ 11 | #include <stdint.h> 12 | 13 | #include "hexport.h" 14 | 15 | typedef struct { 16 | uint32_t state[5]; 17 | uint32_t count[2]; 18 | unsigned char buffer[64]; 19 | } HV_SHA1_CTX; 20 | 21 | BEGIN_EXTERN_C 22 | 23 | HV_EXPORT void HV_SHA1Transform( 24 | uint32_t state[5], 25 | const unsigned char buffer[64] 26 | ); 27 | 28 | HV_EXPORT void HV_SHA1Init( 29 | HV_SHA1_CTX * context 30 | ); 31 | 32 | HV_EXPORT void HV_SHA1Update( 33 | HV_SHA1_CTX * context, 34 | const unsigned char *data, 35 | uint32_t len 36 | ); 37 | 38 | HV_EXPORT void HV_SHA1Final( 39 | unsigned char digest[20], 40 | HV_SHA1_CTX * context 41 | ); 42 | 43 | HV_EXPORT void HV_SHA1( 44 | char *hash_out, 45 | const char *str, 46 | uint32_t len); 47 | 48 | HV_EXPORT void hv_sha1(unsigned char* input, uint32_t inputlen, unsigned char digest[20]); 49 | 50 | // NOTE: if outputlen > 40: output[40] = '\0' 51 | HV_EXPORT void hv_sha1_hex(unsigned char* input, uint32_t inputlen, char* output, uint32_t outputlen); 52 | 53 | END_EXTERN_C 54 | 55 | #endif // HV_SHA1_H_ 56 | --------------------------------------------------------------------------------