The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .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*)&num;
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 | 


--------------------------------------------------------------------------------