├── .gitignore ├── CMakeLists.txt ├── HIREDIS_LICENSE ├── HTTP_PARSER_LICENSE ├── LICENSE ├── README.md ├── src ├── CMakeLists.txt ├── Makefile ├── acceptor.cpp ├── acceptor.h ├── address.cpp ├── address.h ├── connection.cpp ├── connection.h ├── connector.h ├── connector.inl ├── http │ ├── CMakeLists.txt │ ├── http_parser.c │ ├── http_parser.h │ ├── httpclient.cpp │ ├── httpclient.h │ ├── httpconnection.cpp │ ├── httpconnection.h │ ├── httpconnector.cpp │ ├── httpconnector.h │ ├── httpparser.cpp │ ├── httpparser.h │ ├── httprequest.cpp │ ├── httprequest.h │ ├── httpresponse.cpp │ ├── httpresponse.h │ ├── httpserver.cpp │ ├── httpserver.h │ ├── httputil.cpp │ ├── httputil.h │ ├── tnet_http.h │ ├── wsclient.cpp │ ├── wsclient.h │ ├── wsconnection.cpp │ ├── wsconnection.h │ ├── wsutil.cpp │ └── wsutil.h ├── ioevent.h ├── ioloop.cpp ├── ioloop.h ├── log.cpp ├── log.h ├── nocopyable.h ├── notifier.cpp ├── notifier.h ├── polarssl │ ├── base64.c │ ├── base64.h │ ├── md5.c │ ├── md5.h │ ├── polarssl_1.2.7 │ ├── sha1.c │ └── sha1.h ├── poller.cpp ├── poller.h ├── process.cpp ├── process.h ├── redis │ ├── CMakeLists.txt │ ├── hiredis.c │ ├── hiredis.h │ ├── redisclient.cpp │ ├── redisclient.h │ ├── redisconnection.cpp │ ├── redisconnection.h │ ├── redistrans.cpp │ ├── redistrans.h │ ├── sds.c │ ├── sds.h │ └── tnet_redis.h ├── signaler.cpp ├── signaler.h ├── sockutil.cpp ├── sockutil.h ├── spinlock.h ├── stringutil.cpp ├── stringutil.h ├── tcpserver.cpp ├── tcpserver.h ├── timer.cpp ├── timer.h ├── timingwheel.cpp ├── timingwheel.h └── tnet.h └── test ├── CMakeLists.txt ├── cometclient_test.cpp ├── cometserver_test.cpp ├── echoclient_test.cpp ├── echoserver_test.cpp ├── httpclient_test.cpp ├── httpserver_test.cpp ├── notifier_test.cpp ├── redisclient_test.cpp ├── signaler_test.cpp ├── timer_test.cpp ├── timingwheel_test.cpp ├── wsclient_test.cpp └── wsserver_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(TNET) 4 | 5 | add_subdirectory(src) 6 | add_subdirectory(test) 7 | -------------------------------------------------------------------------------- /HIREDIS_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2011, Salvatore Sanfilippo 2 | Copyright (c) 2010-2011, Pieter Noordhuis 3 | 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, 10 | this 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 Redis nor the names of its contributors may be used 17 | to endorse or promote products derived from this software without specific 18 | prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /HTTP_PARSER_LICENSE: -------------------------------------------------------------------------------- 1 | http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright 2 | Igor Sysoev. 3 | 4 | Additional changes are licensed under the same terms as NGINX and 5 | copyright Joyent, Inc. and other Node contributors. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to 9 | deal in the Software without restriction, including without limitation the 10 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 siddontang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | libtnet is a tiny high performance network lib, purpose is to simplify the network programming. 4 | 5 | # Install 6 | 7 | go to root source, then 8 | 9 | mkdir -p build 10 | cd build 11 | cmake .. 12 | make 13 | make install 14 | 15 | # Requirement 16 | 17 | - gcc >= 4.4, supports c++ 0x 18 | - linux, supports eventfd, signalfd and timerfd 19 | 20 | # Not supported! 21 | 22 | - unix socket domain 23 | - ssl 24 | 25 | # Other 26 | 27 | please see [wiki](https://github.com/siddontang/libtnet/wiki) for more information. If you have any problem when using libtnet, you can contact me through below. 28 | 29 | - Gmail: siddontang@gmail.com 30 | - Twitter: siddontang 31 | 32 | I thank you very much for your feedback! 33 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS}") 2 | 3 | set (SRCS 4 | acceptor.cpp 5 | address.cpp 6 | connection.cpp 7 | notifier.cpp 8 | log.cpp 9 | ioloop.cpp 10 | poller.cpp 11 | process.cpp 12 | signaler.cpp 13 | sockutil.cpp 14 | stringutil.cpp 15 | tcpserver.cpp 16 | timer.cpp 17 | timingwheel.cpp 18 | polarssl/base64.c 19 | polarssl/md5.c 20 | polarssl/sha1.c 21 | ) 22 | 23 | set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) 24 | 25 | add_library(tnet ${SRCS}) 26 | target_link_libraries(tnet rt) 27 | 28 | install(TARGETS tnet DESTINATION lib) 29 | 30 | set (HEADERS 31 | address.h 32 | connection.h 33 | connector.h 34 | connector.inl 35 | log.h 36 | nocopyable.h 37 | notifier.h 38 | signaler.h 39 | sockutil.h 40 | spinlock.h 41 | stringutil.h 42 | tcpserver.h 43 | timer.h 44 | timingwheel.h 45 | tnet.h 46 | ) 47 | 48 | install(FILES ${HEADERS} DESTINATION include/tnet) 49 | 50 | 51 | add_subdirectory(http) 52 | add_subdirectory(redis) 53 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "Unix Makefiles" Generator, CMake Version 2.8 3 | 4 | # Default target executed when no arguments are given to make. 5 | default_target: all 6 | .PHONY : default_target 7 | 8 | #============================================================================= 9 | # Special targets provided by cmake. 10 | 11 | # Disable implicit rules so canonical targets will work. 12 | .SUFFIXES: 13 | 14 | # Remove some rules from gmake that .SUFFIXES does not remove. 15 | SUFFIXES = 16 | 17 | .SUFFIXES: .hpux_make_needs_suffix_list 18 | 19 | # Suppress display of executed commands. 20 | $(VERBOSE).SILENT: 21 | 22 | # A target that is always out of date. 23 | cmake_force: 24 | .PHONY : cmake_force 25 | 26 | #============================================================================= 27 | # Set environment variables for the build. 28 | 29 | # The shell in which to execute make rules. 30 | SHELL = /bin/sh 31 | 32 | # The CMake executable. 33 | CMAKE_COMMAND = /usr/local/bin/cmake 34 | 35 | # The command to remove a file. 36 | RM = /usr/local/bin/cmake -E remove -f 37 | 38 | # Escaping for special characters. 39 | EQUALS = = 40 | 41 | # The program to use to edit the cache. 42 | CMAKE_EDIT_COMMAND = /usr/local/bin/ccmake 43 | 44 | # The top-level source directory on which CMake was run. 45 | CMAKE_SOURCE_DIR = /home/tangliu/program/libtnet 46 | 47 | # The top-level build directory on which CMake was run. 48 | CMAKE_BINARY_DIR = /home/tangliu/program/libtnet/src 49 | 50 | #============================================================================= 51 | # Targets provided globally by CMake. 52 | 53 | # Special rule for the target edit_cache 54 | edit_cache: 55 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..." 56 | /usr/local/bin/ccmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) 57 | .PHONY : edit_cache 58 | 59 | # Special rule for the target edit_cache 60 | edit_cache/fast: edit_cache 61 | .PHONY : edit_cache/fast 62 | 63 | # Special rule for the target install 64 | install: preinstall 65 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." 66 | /usr/local/bin/cmake -P cmake_install.cmake 67 | .PHONY : install 68 | 69 | # Special rule for the target install 70 | install/fast: preinstall/fast 71 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." 72 | /usr/local/bin/cmake -P cmake_install.cmake 73 | .PHONY : install/fast 74 | 75 | # Special rule for the target install/local 76 | install/local: preinstall 77 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." 78 | /usr/local/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake 79 | .PHONY : install/local 80 | 81 | # Special rule for the target install/local 82 | install/local/fast: install/local 83 | .PHONY : install/local/fast 84 | 85 | # Special rule for the target install/strip 86 | install/strip: preinstall 87 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." 88 | /usr/local/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake 89 | .PHONY : install/strip 90 | 91 | # Special rule for the target install/strip 92 | install/strip/fast: install/strip 93 | .PHONY : install/strip/fast 94 | 95 | # Special rule for the target list_install_components 96 | list_install_components: 97 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\"" 98 | .PHONY : list_install_components 99 | 100 | # Special rule for the target list_install_components 101 | list_install_components/fast: list_install_components 102 | .PHONY : list_install_components/fast 103 | 104 | # Special rule for the target rebuild_cache 105 | rebuild_cache: 106 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." 107 | /usr/local/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) 108 | .PHONY : rebuild_cache 109 | 110 | # Special rule for the target rebuild_cache 111 | rebuild_cache/fast: rebuild_cache 112 | .PHONY : rebuild_cache/fast 113 | 114 | # The main all target 115 | all: cmake_check_build_system 116 | $(CMAKE_COMMAND) -E cmake_progress_start /home/tangliu/program/libtnet/src/CMakeFiles /home/tangliu/program/libtnet/src/CMakeFiles/progress.marks 117 | $(MAKE) -f CMakeFiles/Makefile2 all 118 | $(CMAKE_COMMAND) -E cmake_progress_start /home/tangliu/program/libtnet/src/CMakeFiles 0 119 | .PHONY : all 120 | 121 | # The main clean target 122 | clean: 123 | $(MAKE) -f CMakeFiles/Makefile2 clean 124 | .PHONY : clean 125 | 126 | # The main clean target 127 | clean/fast: clean 128 | .PHONY : clean/fast 129 | 130 | # Prepare targets for installation. 131 | preinstall: all 132 | $(MAKE) -f CMakeFiles/Makefile2 preinstall 133 | .PHONY : preinstall 134 | 135 | # Prepare targets for installation. 136 | preinstall/fast: 137 | $(MAKE) -f CMakeFiles/Makefile2 preinstall 138 | .PHONY : preinstall/fast 139 | 140 | # clear depends 141 | depend: 142 | $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 143 | .PHONY : depend 144 | 145 | #============================================================================= 146 | # Target rules for targets named tnet 147 | 148 | # Build rule for target. 149 | tnet: cmake_check_build_system 150 | $(MAKE) -f CMakeFiles/Makefile2 tnet 151 | .PHONY : tnet 152 | 153 | # fast build rule for target. 154 | tnet/fast: 155 | $(MAKE) -f src/CMakeFiles/tnet.dir/build.make src/CMakeFiles/tnet.dir/build 156 | .PHONY : tnet/fast 157 | 158 | #============================================================================= 159 | # Target rules for targets named tnet_http 160 | 161 | # Build rule for target. 162 | tnet_http: cmake_check_build_system 163 | $(MAKE) -f CMakeFiles/Makefile2 tnet_http 164 | .PHONY : tnet_http 165 | 166 | # fast build rule for target. 167 | tnet_http/fast: 168 | $(MAKE) -f src/http/CMakeFiles/tnet_http.dir/build.make src/http/CMakeFiles/tnet_http.dir/build 169 | .PHONY : tnet_http/fast 170 | 171 | #============================================================================= 172 | # Target rules for targets named echoserver_test 173 | 174 | # Build rule for target. 175 | echoserver_test: cmake_check_build_system 176 | $(MAKE) -f CMakeFiles/Makefile2 echoserver_test 177 | .PHONY : echoserver_test 178 | 179 | # fast build rule for target. 180 | echoserver_test/fast: 181 | $(MAKE) -f test/CMakeFiles/echoserver_test.dir/build.make test/CMakeFiles/echoserver_test.dir/build 182 | .PHONY : echoserver_test/fast 183 | 184 | #============================================================================= 185 | # Target rules for targets named httpserver_test 186 | 187 | # Build rule for target. 188 | httpserver_test: cmake_check_build_system 189 | $(MAKE) -f CMakeFiles/Makefile2 httpserver_test 190 | .PHONY : httpserver_test 191 | 192 | # fast build rule for target. 193 | httpserver_test/fast: 194 | $(MAKE) -f test/CMakeFiles/httpserver_test.dir/build.make test/CMakeFiles/httpserver_test.dir/build 195 | .PHONY : httpserver_test/fast 196 | 197 | # Help Target 198 | help: 199 | @echo "The following are some of the valid targets for this Makefile:" 200 | @echo "... all (the default if no target is provided)" 201 | @echo "... clean" 202 | @echo "... depend" 203 | @echo "... edit_cache" 204 | @echo "... install" 205 | @echo "... install/local" 206 | @echo "... install/strip" 207 | @echo "... list_install_components" 208 | @echo "... rebuild_cache" 209 | @echo "... tnet" 210 | @echo "... tnet_http" 211 | @echo "... echoserver_test" 212 | @echo "... httpserver_test" 213 | .PHONY : help 214 | 215 | 216 | 217 | #============================================================================= 218 | # Special targets to cleanup operation of make. 219 | 220 | # Special rule to run CMake to check the build system integrity. 221 | # No rule that depends on this can have commands that come from listfiles 222 | # because they might be regenerated. 223 | cmake_check_build_system: 224 | $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 225 | .PHONY : cmake_check_build_system 226 | 227 | -------------------------------------------------------------------------------- /src/acceptor.cpp: -------------------------------------------------------------------------------- 1 | #include "acceptor.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "ioloop.h" 13 | #include "address.h" 14 | #include "sockutil.h" 15 | #include "log.h" 16 | 17 | namespace tnet 18 | { 19 | int createDummyFd() 20 | { 21 | return open("/dev/null", O_RDONLY | O_CLOEXEC); 22 | } 23 | 24 | Acceptor::Acceptor(const NewConnCallback_t& callback) 25 | : m_loop(0) 26 | , m_sockFd(0) 27 | , m_dummyFd(createDummyFd()) 28 | , m_running(false) 29 | , m_callback(callback) 30 | { 31 | 32 | } 33 | 34 | Acceptor::~Acceptor() 35 | { 36 | if(m_sockFd > 0) 37 | { 38 | close(m_sockFd); 39 | } 40 | close(m_dummyFd); 41 | } 42 | 43 | int Acceptor::listen(const Address& addr) 44 | { 45 | int fd = SockUtil::bindAndListen(addr); 46 | if(fd < 0) 47 | { 48 | return fd; 49 | } 50 | 51 | m_sockFd = fd; 52 | 53 | return m_sockFd; 54 | } 55 | 56 | void Acceptor::start(IOLoop* loop) 57 | { 58 | assert(m_sockFd > 0); 59 | 60 | if(m_running) 61 | { 62 | LOG_WARN("acceptor was started"); 63 | return; 64 | } 65 | 66 | m_loop = loop; 67 | 68 | m_running = true; 69 | 70 | m_loop->addHandler(m_sockFd, TNET_READ, 71 | std::bind(&Acceptor::onAccept, this, _1, _2)); 72 | } 73 | 74 | void Acceptor::stop() 75 | { 76 | assert(m_sockFd > 0); 77 | if(!m_running) 78 | { 79 | LOG_WARN("acceptor was stoped"); 80 | return; 81 | } 82 | 83 | m_running = false; 84 | 85 | LOG_INFO("stop %d", m_sockFd); 86 | m_loop->removeHandler(m_sockFd); 87 | } 88 | 89 | void Acceptor::onAccept(IOLoop* loop, int events) 90 | { 91 | int sockFd = accept4(m_sockFd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); 92 | if(sockFd < 0) 93 | { 94 | int err = errno; 95 | if(err == EMFILE || err == ENFILE) 96 | { 97 | LOG_ERROR("accept error %s", errorMsg(err)); 98 | close(m_dummyFd); 99 | sockFd = accept(m_sockFd, NULL, NULL); 100 | close(sockFd); 101 | 102 | m_dummyFd = createDummyFd(); 103 | } 104 | return; 105 | } 106 | else 107 | { 108 | LOG_INFO("onAccept %d", sockFd); 109 | 110 | SockUtil::setNoDelay(sockFd, true); 111 | 112 | m_callback(loop, sockFd); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/acceptor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tnet.h" 4 | 5 | namespace tnet 6 | { 7 | class IOLoop; 8 | class Address; 9 | 10 | class Acceptor : public nocopyable 11 | { 12 | public: 13 | Acceptor(const NewConnCallback_t& callback); 14 | ~Acceptor(); 15 | 16 | int listen(const Address& addr); 17 | 18 | void start(IOLoop* loop); 19 | void stop(); 20 | 21 | private: 22 | void onAccept(IOLoop*, int); 23 | 24 | private: 25 | IOLoop* m_loop; 26 | 27 | int m_sockFd; 28 | int m_dummyFd; 29 | 30 | bool m_running; 31 | 32 | NewConnCallback_t m_callback; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/address.cpp: -------------------------------------------------------------------------------- 1 | #include "address.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "sockutil.h" 7 | #include "log.h" 8 | 9 | using namespace std; 10 | 11 | namespace tnet 12 | { 13 | Address::Address(uint16_t port) 14 | { 15 | bzero(&m_addr, sizeof(m_addr)); 16 | 17 | m_addr.sin_family = AF_INET; 18 | m_addr.sin_addr.s_addr = htonl(INADDR_ANY); 19 | m_addr.sin_port = htons(port); 20 | } 21 | 22 | Address::Address(const string& ip, uint16_t port) 23 | { 24 | bzero(&m_addr, sizeof(m_addr)); 25 | 26 | m_addr.sin_family = AF_INET; 27 | m_addr.sin_port = htons(port); 28 | 29 | if(inet_pton(AF_INET, ip.c_str(), &m_addr.sin_addr) <= 0) 30 | { 31 | //may be domain 32 | uint32_t ret = SockUtil::getHostByName(ip); 33 | if(ret != uint32_t(-1)) 34 | { 35 | m_addr.sin_addr.s_addr = ret; 36 | return; 37 | } 38 | 39 | LOG_ERROR("invalid ip %s, use 0.0.0.0 instead", ip.c_str()); 40 | //error, may log later, now use INADDR_ANY 41 | m_addr.sin_addr.s_addr = htonl(INADDR_ANY); 42 | } 43 | } 44 | 45 | Address::Address(const struct sockaddr_in& addr) 46 | { 47 | memcpy(&m_addr, &addr, sizeof(addr)); 48 | } 49 | 50 | uint32_t Address::ip() const 51 | { 52 | return ntohl(m_addr.sin_addr.s_addr); 53 | } 54 | 55 | string Address::ipstr() const 56 | { 57 | char buf[32] = {'\0'}; 58 | inet_ntop(AF_INET, &m_addr.sin_addr, buf, static_cast (sizeof(buf))); 59 | 60 | return string(buf, 32); 61 | } 62 | 63 | uint16_t Address::port() const 64 | { 65 | return ntohs(m_addr.sin_port); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/address.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tnet 8 | { 9 | 10 | class Address 11 | { 12 | public: 13 | Address(uint16_t port); 14 | Address(const std::string& ip, uint16_t port); 15 | Address(const struct sockaddr_in& addr); 16 | 17 | //host byte order 18 | uint32_t ip() const; 19 | 20 | //host byte order 21 | uint16_t port() const; 22 | 23 | const struct sockaddr_in& sockAddr() const { return m_addr; } 24 | 25 | std::string ipstr() const; 26 | 27 | private: 28 | struct sockaddr_in m_addr; 29 | }; 30 | 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "tnet.h" 6 | 7 | namespace tnet 8 | { 9 | class IOLoop; 10 | class Address; 11 | 12 | class Connection : public nocopyable 13 | , public std::enable_shared_from_this 14 | { 15 | public: 16 | friend class TcpServer; 17 | 18 | enum Status 19 | { 20 | None, 21 | Connecting, 22 | Connected, 23 | Disconnecting, 24 | Disconnected, 25 | }; 26 | 27 | Connection(IOLoop* loop, int fd); 28 | ~Connection(); 29 | 30 | int getSockFd() { return m_fd; } 31 | 32 | void setEventCallback(const ConnEventCallback_t& callback) { m_callback = callback; } 33 | void clearEventCallback(); 34 | 35 | //after is milliseconds, if after is 0, close immediately 36 | void shutDown(int after = 0); 37 | 38 | //-1 when connection is not connected 39 | int send(const std::string& data); 40 | 41 | void onEstablished(); 42 | void connect(const Address& addr); 43 | 44 | uint64_t lastActiveTime() { return m_lastActiveTime; } 45 | 46 | bool isConnected() { return m_status == Connected; } 47 | bool isConnecting() { return m_status == Connecting; } 48 | 49 | private: 50 | void onHandler(IOLoop*, int); 51 | void handleRead(); 52 | void handleWrite(); 53 | void handleWrite(const std::string& data); 54 | void handleError(); 55 | void handleClose(); 56 | void handleConnect(); 57 | 58 | void updateActiveTime(); 59 | bool disconnect(); 60 | 61 | private: 62 | ConnEventCallback_t m_callback; 63 | 64 | IOLoop* m_loop; 65 | int m_fd; 66 | int m_status; 67 | 68 | //seconds 69 | uint64_t m_lastActiveTime; 70 | 71 | std::string m_sendBuffer; 72 | }; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/connector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "tnet.h" 6 | 7 | namespace tnet 8 | { 9 | template 10 | class Connector : public nocopyable 11 | , public std::enable_shared_from_this 12 | { 13 | public: 14 | Connector(); 15 | Connector(const std::string& device); 16 | ~Connector(); 17 | 18 | typedef std::shared_ptr DerivedPtr_t; 19 | typedef std::function ConnectCallback_t; 20 | 21 | int connect(IOLoop* loop, const Address& addr, const ConnectCallback_t& callback, const std::string& device = ""); 22 | 23 | WeakConnectionPtr_t getConn() { return m_conn; } 24 | ConnectionPtr_t lockConn() { return m_conn.lock(); } 25 | 26 | void send(const std::string& data); 27 | 28 | void shutDown(); 29 | 30 | protected: 31 | void handleRead(const char*, size_t) {} 32 | void handleWriteComplete(const void*) {} 33 | void handleError(const void*) {} 34 | void handleClose(const void*) {} 35 | 36 | private: 37 | void onConnConnectEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context, const ConnectCallback_t& callback); 38 | 39 | void onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context); 40 | 41 | private: 42 | WeakConnectionPtr_t m_conn; 43 | }; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/connector.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "connection.h" 4 | #include "log.h" 5 | #include "sockutil.h" 6 | 7 | namespace tnet 8 | { 9 | template 10 | Connector::Connector() 11 | { 12 | 13 | } 14 | 15 | template 16 | Connector::~Connector() 17 | { 18 | } 19 | 20 | template 21 | int Connector::connect(IOLoop* loop, const Address& addr, const ConnectCallback_t& callback, const std::string& device) 22 | { 23 | int fd = SockUtil::create(); 24 | if(fd < 0) 25 | { 26 | return fd; 27 | } 28 | 29 | if(!device.empty()) 30 | { 31 | SockUtil::bindDevice(fd, device); 32 | } 33 | 34 | ConnectionPtr_t conn = std::make_shared(loop, fd); 35 | 36 | m_conn = conn; 37 | 38 | conn->setEventCallback(std::bind(&Connector::onConnConnectEvent, this->shared_from_this(), _1, _2, _3, callback)); 39 | 40 | conn->connect(addr); 41 | 42 | return 0; 43 | } 44 | 45 | template 46 | void Connector::onConnConnectEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context, const ConnectCallback_t& callback) 47 | { 48 | switch(event) 49 | { 50 | case Conn_ConnectingEvent: 51 | return; 52 | case Conn_ConnectEvent: 53 | { 54 | ConnectCallback_t cb = std::move(callback); 55 | 56 | conn->setEventCallback(std::bind(&Connector::onConnEvent, this->shared_from_this(), _1, _2, _3)); 57 | 58 | cb(this->shared_from_this(), true); 59 | } 60 | break; 61 | default: 62 | //here we think error 63 | callback(this->shared_from_this(), false); 64 | return; 65 | break; 66 | } 67 | } 68 | 69 | template 70 | void Connector::onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context) 71 | { 72 | DerivedPtr_t t = this->shared_from_this(); 73 | switch(event) 74 | { 75 | case Conn_ReadEvent: 76 | { 77 | const StackBuffer* buf = (const StackBuffer*)(context); 78 | t->handleRead(buf->buffer, buf->count); 79 | } 80 | break; 81 | case Conn_WriteCompleteEvent: 82 | t->handleWriteComplete(context); 83 | break; 84 | case Conn_ErrorEvent: 85 | t->handleError(context); 86 | break; 87 | case Conn_CloseEvent: 88 | t->handleClose(context); 89 | break; 90 | default: 91 | break; 92 | } 93 | } 94 | 95 | template 96 | void Connector::send(const std::string& data) 97 | { 98 | ConnectionPtr_t conn = m_conn.lock(); 99 | 100 | if(conn) 101 | { 102 | conn->send(data); 103 | } 104 | } 105 | 106 | template 107 | void Connector::shutDown() 108 | { 109 | ConnectionPtr_t conn = m_conn.lock(); 110 | 111 | if(conn) 112 | { 113 | conn->shutDown(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/http/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS}") 2 | 3 | set (SRCS 4 | http_parser.c 5 | httpclient.cpp 6 | httpconnection.cpp 7 | httpconnector.cpp 8 | httpparser.cpp 9 | httprequest.cpp 10 | httpresponse.cpp 11 | httpserver.cpp 12 | httputil.cpp 13 | wsclient.cpp 14 | wsconnection.cpp 15 | wsutil.cpp 16 | ) 17 | 18 | add_definitions(-DHTTP_PARSER_STRICT=0) 19 | 20 | include_directories(../) 21 | 22 | set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) 23 | 24 | add_library(tnet_http ${SRCS}) 25 | target_link_libraries(tnet_http tnet) 26 | 27 | install(TARGETS tnet_http DESTINATION lib) 28 | 29 | set (HEADERS 30 | httpclient.h 31 | httpconnection.h 32 | http_parser.h 33 | httpparser.h 34 | httprequest.h 35 | httpresponse.h 36 | httpserver.h 37 | httputil.h 38 | tnet_http.h 39 | wsclient.h 40 | wsconnection.h 41 | ) 42 | 43 | install(FILES ${HEADERS} DESTINATION include/tnet/http) 44 | 45 | -------------------------------------------------------------------------------- /src/http/httpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "httpclient.h" 2 | 3 | #include "ioloop.h" 4 | #include "httprequest.h" 5 | #include "connection.h" 6 | #include "sockutil.h" 7 | #include "log.h" 8 | #include "address.h" 9 | #include "httpconnector.h" 10 | #include "connector.inl" 11 | 12 | using namespace std; 13 | 14 | namespace tnet 15 | { 16 | 17 | HttpClient::HttpClient(IOLoop* loop, int maxClients) 18 | : m_loop(loop) 19 | , m_maxClients(maxClients) 20 | { 21 | } 22 | 23 | HttpClient::~HttpClient() 24 | { 25 | IpConn_t::iterator iter = m_conns.begin(); 26 | while(iter != m_conns.end()) 27 | { 28 | HttpConnectorPtr_t conn = iter->second.lock(); 29 | if(conn) 30 | { 31 | conn->shutDown(); 32 | } 33 | ++iter; 34 | } 35 | 36 | m_conns.clear(); 37 | } 38 | 39 | void HttpClient::request(const string& url, const ResponseCallback_t& callback, enum http_method method) 40 | { 41 | HttpRequest req; 42 | req.url = url; 43 | req.method = method; 44 | 45 | request(req, callback); 46 | } 47 | 48 | void HttpClient::request(const string& url, const Headers_t& headers, const ResponseCallback_t& callback, enum http_method method) 49 | { 50 | HttpRequest req; 51 | req.url = url; 52 | req.headers = headers; 53 | req.method = method; 54 | 55 | request(req, callback); 56 | } 57 | 58 | void HttpClient::request(const string& url, const Headers_t& headers, const string& body, const ResponseCallback_t& callback, enum http_method method) 59 | { 60 | HttpRequest req; 61 | req.url = url; 62 | req.headers = headers; 63 | req.method = method; 64 | req.body = body; 65 | 66 | request(req, callback); 67 | } 68 | 69 | 70 | void HttpClient::pushConn(const HttpConnectorPtr_t& conn) 71 | { 72 | conn->clearCallback(); 73 | 74 | ConnectionPtr_t c = conn->lockConn(); 75 | if(!c) 76 | { 77 | return; 78 | } 79 | 80 | if(m_conns.size() >= m_maxClients) 81 | { 82 | conn->shutDown(); 83 | return; 84 | } 85 | 86 | Address addr(0); 87 | if(SockUtil::getRemoteAddr(c->getSockFd(), addr) != 0) 88 | { 89 | conn->shutDown(); 90 | return; 91 | } 92 | 93 | m_conns.insert(make_pair(addr.ip(), conn)); 94 | } 95 | 96 | HttpConnectorPtr_t HttpClient::popConn(uint32_t ip) 97 | { 98 | while(true) 99 | { 100 | IpConn_t::iterator iter = m_conns.find(ip); 101 | if(iter == m_conns.end()) 102 | { 103 | return HttpConnectorPtr_t(); 104 | } 105 | else 106 | { 107 | WeakHttpConnectorPtr_t conn = iter->second; 108 | m_conns.erase(iter); 109 | HttpConnectorPtr_t c = conn.lock(); 110 | if(c && c->lockConn()) 111 | { 112 | return c; 113 | } 114 | } 115 | } 116 | } 117 | 118 | void HttpClient::onResponse(const HttpConnectorPtr_t& conn, const HttpResponse& response, ResponseEvent event, const ResponseCallback_t& callback) 119 | { 120 | //add refer 121 | HttpConnectorPtr_t c = conn->shared_from_this(); 122 | auto cb = std::move(callback); 123 | 124 | if(event == Response_Complete) 125 | { 126 | pushConn(conn); 127 | } 128 | 129 | cb(response); 130 | } 131 | 132 | void HttpClient::request(HttpRequest& request, const ResponseCallback_t& callback) 133 | { 134 | request.parseUrl(); 135 | 136 | Address addr(request.host, request.port); 137 | 138 | //now we only support ip 139 | HttpConnectorPtr_t conn = popConn(addr.ip()); 140 | if(conn) 141 | { 142 | conn->setCallback(std::bind(&HttpClient::onResponse, shared_from_this(), _1, _2, _3, callback)); 143 | conn->send(request.dump()); 144 | } 145 | else 146 | { 147 | conn = std::make_shared(); 148 | 149 | conn->connect(m_loop, addr, std::bind(&HttpClient::onConnect, shared_from_this(), _1, _2, request.dump(), callback), m_device); 150 | } 151 | } 152 | 153 | void HttpClient::onConnect(const HttpConnectorPtr_t& conn, bool connected, const string& requestData, const ResponseCallback_t& callback) 154 | { 155 | if(!connected) 156 | { 157 | LOG_ERROR("httpclient connect error"); 158 | return; 159 | } 160 | 161 | string data = std::move(requestData); 162 | ResponseCallback_t cb = std::move(callback); 163 | 164 | conn->setCallback(std::bind(&HttpClient::onResponse, shared_from_this(), _1, _2, _3, cb)); 165 | conn->send(data); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/http/httpclient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | extern "C" 8 | { 9 | #include "http_parser.h" 10 | } 11 | 12 | #include "tnet_http.h" 13 | 14 | namespace tnet 15 | { 16 | class HttpClient : public nocopyable 17 | , public std::enable_shared_from_this 18 | { 19 | public: 20 | HttpClient(IOLoop* loop, int maxClients = 10); 21 | ~HttpClient(); 22 | 23 | void bindDevice(const std::string& device) { m_device = device; } 24 | 25 | //now only support ip:port 26 | void request(const std::string& url, const ResponseCallback_t& callback, enum http_method method = HTTP_GET); 27 | void request(const std::string& url, const Headers_t& headers, const ResponseCallback_t& callback, enum http_method method = HTTP_GET); 28 | void request(const std::string& url, const Headers_t& headers, const std::string& body, const ResponseCallback_t& callback, enum http_method method = HTTP_POST); 29 | 30 | private: 31 | void request(HttpRequest& request, const ResponseCallback_t& callback); 32 | 33 | void onResponse(const HttpConnectorPtr_t& conn, const HttpResponse& response, ResponseEvent event, const ResponseCallback_t& callback); 34 | 35 | void onConnect(const HttpConnectorPtr_t&, bool connected, const std::string& requestData, const ResponseCallback_t& callback); 36 | 37 | void pushConn(const HttpConnectorPtr_t& conn); 38 | 39 | HttpConnectorPtr_t popConn(uint32_t ip); 40 | 41 | private: 42 | IOLoop* m_loop; 43 | 44 | int m_maxClients; 45 | typedef std::multimap IpConn_t; 46 | IpConn_t m_conns; 47 | 48 | std::string m_device; 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/http/httpconnection.cpp: -------------------------------------------------------------------------------- 1 | #include "httpconnection.h" 2 | #include "httpserver.h" 3 | #include "log.h" 4 | #include "httprequest.h" 5 | #include "httpresponse.h" 6 | #include "connection.h" 7 | #include "httpparser.h" 8 | 9 | using namespace std; 10 | 11 | namespace tnet 12 | { 13 | size_t HttpConnection::ms_maxHeaderSize = 80 * 1024; 14 | size_t HttpConnection::ms_maxBodySize = 10 * 1024 * 1024; 15 | 16 | static void dummyCallback() 17 | {} 18 | 19 | HttpConnection::HttpConnection(const ConnectionPtr_t& conn, const RequestCallback_t& callback) 20 | : HttpParser(HTTP_REQUEST) 21 | , m_conn(conn) 22 | , m_callback(callback) 23 | { 24 | m_fd = conn->getSockFd(); 25 | m_sendCallback = std::bind(&dummyCallback); 26 | } 27 | 28 | HttpConnection::~HttpConnection() 29 | { 30 | LOG_INFO("httpconnection destroyed"); 31 | } 32 | 33 | void HttpConnection::onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context) 34 | { 35 | HttpConnectionPtr_t httpConn = shared_from_this(); 36 | 37 | switch(event) 38 | { 39 | case Conn_ReadEvent: 40 | { 41 | const StackBuffer* buf = (const StackBuffer*)context; 42 | 43 | execute(buf->buffer, buf->count); 44 | } 45 | break; 46 | case Conn_WriteCompleteEvent: 47 | { 48 | m_sendCallback(); 49 | m_sendCallback = std::bind(&dummyCallback); 50 | } 51 | break; 52 | default: 53 | break; 54 | } 55 | } 56 | 57 | void HttpConnection::shutDown(int after) 58 | { 59 | ConnectionPtr_t conn = m_conn.lock(); 60 | if(conn) 61 | { 62 | conn->shutDown(after); 63 | } 64 | } 65 | 66 | void HttpConnection::send(HttpResponse& resp) 67 | { 68 | ConnectionPtr_t conn = m_conn.lock(); 69 | if(!conn) 70 | { 71 | return; 72 | } 73 | 74 | conn->send(resp.dump()); 75 | } 76 | 77 | void HttpConnection::send(int statusCode) 78 | { 79 | HttpResponse resp; 80 | resp.statusCode = statusCode; 81 | 82 | send(resp); 83 | } 84 | 85 | void HttpConnection::send(int statusCode, const string& body) 86 | { 87 | HttpResponse resp; 88 | resp.statusCode = statusCode; 89 | resp.body = body; 90 | 91 | send(resp); 92 | } 93 | 94 | void HttpConnection::send(int statusCode, const string& body, const Headers_t& headers) 95 | { 96 | HttpResponse resp; 97 | resp.statusCode = statusCode; 98 | resp.body = body; 99 | 100 | resp.headers = headers; 101 | 102 | send(resp); 103 | } 104 | 105 | void HttpConnection::send(HttpResponse& resp, const Callback_t& callback) 106 | { 107 | m_sendCallback = callback; 108 | send(resp); 109 | } 110 | 111 | void HttpConnection::send(int statusCode, const Callback_t& callback) 112 | { 113 | m_sendCallback = callback; 114 | send(statusCode); 115 | } 116 | 117 | void HttpConnection::send(int statusCode, const std::string& body, const Callback_t& callback) 118 | { 119 | m_sendCallback = callback; 120 | send(statusCode, body); 121 | } 122 | 123 | void HttpConnection::send(int statusCode, const std::string& body, const Headers_t& headers, const Callback_t& callback) 124 | { 125 | m_sendCallback = callback; 126 | send(statusCode, body, headers); 127 | } 128 | 129 | int HttpConnection::onMessageBegin() 130 | { 131 | m_request.clear(); 132 | return 0; 133 | } 134 | 135 | int HttpConnection::onUrl(const char* at, size_t length) 136 | { 137 | m_request.url.append(at, length); 138 | return 0; 139 | } 140 | 141 | int HttpConnection::onHeader(const string& field, const string& value) 142 | { 143 | if(m_parser.nread >= ms_maxHeaderSize) 144 | { 145 | m_errorCode = 413; 146 | return -1; 147 | } 148 | 149 | 150 | m_request.headers.insert(make_pair(field, value)); 151 | return 0; 152 | } 153 | 154 | int HttpConnection::onBody(const char* at, size_t length) 155 | { 156 | if(m_request.body.size() + length > ms_maxBodySize) 157 | { 158 | m_errorCode = 413; 159 | return -1; 160 | } 161 | 162 | m_request.body.append(at, length); 163 | return 0; 164 | } 165 | 166 | int HttpConnection::onHeadersComplete() 167 | { 168 | m_request.majorVersion = m_parser.http_major; 169 | m_request.minorVersion = m_parser.http_minor; 170 | 171 | m_request.method = (http_method)m_parser.method; 172 | 173 | m_request.parseUrl(); 174 | 175 | return 0; 176 | } 177 | 178 | int HttpConnection::onMessageComplete() 179 | { 180 | if(!m_parser.upgrade) 181 | { 182 | m_callback(shared_from_this(), m_request, Request_Complete, 0); 183 | } 184 | return 0; 185 | } 186 | 187 | int HttpConnection::onUpgrade(const char* at, size_t length) 188 | { 189 | StackBuffer buffer(at, length); 190 | m_callback(shared_from_this(), m_request, Request_Upgrade, &buffer); 191 | return 0; 192 | } 193 | 194 | int HttpConnection::onError(const HttpError& error) 195 | { 196 | m_callback(shared_from_this(), m_request, Request_Error, (void*)&error); 197 | return 0; 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /src/http/httpconnection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern "C" 7 | { 8 | #include "http_parser.h" 9 | } 10 | 11 | #include "tnet_http.h" 12 | #include "httpparser.h" 13 | #include "httprequest.h" 14 | 15 | namespace tnet 16 | { 17 | class HttpServer; 18 | class HttpRequest; 19 | class HttpResponse; 20 | 21 | //http server connection 22 | class HttpConnection : public HttpParser 23 | , public std::enable_shared_from_this 24 | { 25 | public: 26 | friend class HttpServer; 27 | 28 | typedef std::function RequestCallback_t; 29 | HttpConnection(const ConnectionPtr_t& conn, const RequestCallback_t& callback); 30 | 31 | ~HttpConnection(); 32 | 33 | int getSockFd() { return m_fd; } 34 | 35 | void send(HttpResponse& resp); 36 | void send(int statusCode); 37 | void send(int statusCode, const std::string& body); 38 | void send(int statusCode, const std::string& body, const Headers_t& headers); 39 | 40 | //send completely callback, called when all send buffers are send. 41 | //If there was a previous callback, that callback will be overwritten 42 | void send(HttpResponse& resp, const Callback_t& callback); 43 | void send(int statusCode, const Callback_t& callback); 44 | void send(int statusCode, const std::string& body, const Callback_t& callback); 45 | void send(int statusCode, const std::string& body, const Headers_t& headers, const Callback_t& callback); 46 | 47 | //after is milliseconds 48 | void shutDown(int after); 49 | 50 | ConnectionPtr_t lockConn() { return m_conn.lock(); } 51 | WeakConnectionPtr_t getConn() { return m_conn; } 52 | 53 | static void setMaxHeaderSize(size_t size) { ms_maxHeaderSize = size; } 54 | static void setMaxBodySize(size_t size) { ms_maxBodySize = size; } 55 | 56 | private: 57 | int onMessageBegin(); 58 | int onUrl(const char* at, size_t length); 59 | int onHeader(const std::string& field, const std::string& value); 60 | int onHeadersComplete(); 61 | int onBody(const char* at, size_t length); 62 | int onMessageComplete(); 63 | int onUpgrade(const char* at, size_t length); 64 | int onError(const HttpError& error); 65 | 66 | void onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context); 67 | 68 | private: 69 | std::weak_ptr m_conn; 70 | int m_fd; 71 | 72 | HttpRequest m_request; 73 | 74 | RequestCallback_t m_callback; 75 | 76 | Callback_t m_sendCallback; 77 | 78 | static size_t ms_maxHeaderSize; 79 | static size_t ms_maxBodySize; 80 | }; 81 | } 82 | 83 | -------------------------------------------------------------------------------- /src/http/httpconnector.cpp: -------------------------------------------------------------------------------- 1 | #include "httpconnector.h" 2 | 3 | #include "connection.h" 4 | #include "log.h" 5 | 6 | #include "connector.inl" 7 | 8 | using namespace std; 9 | 10 | namespace tnet 11 | { 12 | size_t HttpConnector::ms_maxHeaderSize = 80 * 1024; 13 | size_t HttpConnector::ms_maxBodySize = 10 * 1024 * 1024; 14 | 15 | void dummyCallback(const HttpConnectorPtr_t&, const HttpResponse&, ResponseEvent) 16 | { 17 | 18 | } 19 | 20 | HttpConnector::HttpConnector() 21 | : HttpParser(HTTP_RESPONSE) 22 | , Connector() 23 | { 24 | m_callback = std::bind(&dummyCallback, _1, _2, _3); 25 | } 26 | 27 | HttpConnector::~HttpConnector() 28 | { 29 | } 30 | 31 | void HttpConnector::clearCallback() 32 | { 33 | m_callback = std::bind(&dummyCallback, _1, _2, _3); 34 | } 35 | 36 | void HttpConnector::handleRead(const char* buffer, size_t count) 37 | { 38 | HttpConnectorPtr_t conn = shared_from_this(); 39 | 40 | execute(buffer, count); 41 | } 42 | 43 | int HttpConnector::onMessageBegin() 44 | { 45 | m_response.clear(); 46 | return 0; 47 | } 48 | 49 | int HttpConnector::onHeader(const string& field, const string& value) 50 | { 51 | if(m_parser.nread >= ms_maxHeaderSize) 52 | { 53 | m_errorCode = 413; 54 | return -1; 55 | } 56 | 57 | m_response.headers.insert(make_pair(field, value)); 58 | return 0; 59 | } 60 | 61 | int HttpConnector::onHeadersComplete() 62 | { 63 | m_response.statusCode = m_parser.status_code; 64 | return 0; 65 | } 66 | 67 | int HttpConnector::onBody(const char* at, size_t length) 68 | { 69 | if(m_response.body.size() + length >= ms_maxBodySize) 70 | { 71 | m_errorCode = 413; 72 | return -1; 73 | } 74 | 75 | m_response.body.append(at, length); 76 | return 0; 77 | } 78 | 79 | int HttpConnector::onMessageComplete() 80 | { 81 | m_callback(shared_from_this(), m_response, Response_Complete); 82 | return 0; 83 | } 84 | 85 | int HttpConnector::onError(const HttpError& error) 86 | { 87 | HttpResponse resp(error.statusCode); 88 | resp.body = error.message; 89 | m_callback(shared_from_this(), resp, Response_Error); 90 | return 0; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/http/httpconnector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tnet_http.h" 4 | #include "httpparser.h" 5 | #include "httpresponse.h" 6 | #include "connector.h" 7 | 8 | namespace tnet 9 | { 10 | 11 | class HttpConnector : public HttpParser 12 | , public Connector 13 | { 14 | public: 15 | friend class HttpClient; 16 | friend class WsClient; 17 | friend class Connector; 18 | 19 | typedef std::function ResponseCallback_t; 20 | 21 | HttpConnector(); 22 | ~HttpConnector(); 23 | 24 | void setCallback(const ResponseCallback_t& callback) { m_callback = callback; } 25 | void clearCallback(); 26 | 27 | static void setMaxHeaderSize(size_t size) { ms_maxHeaderSize = size; } 28 | static void setMaxBodySize(size_t size) { ms_maxBodySize = size; } 29 | 30 | private: 31 | void handleRead(const char* buffer, size_t count); 32 | 33 | int onMessageBegin(); 34 | int onHeader(const std::string& field, const std::string& value); 35 | int onHeadersComplete(); 36 | int onBody(const char* at, size_t length); 37 | int onMessageComplete(); 38 | int onError(const HttpError& error); 39 | 40 | private: 41 | HttpResponse m_response; 42 | 43 | ResponseCallback_t m_callback; 44 | 45 | static size_t ms_maxHeaderSize; 46 | static size_t ms_maxBodySize; 47 | }; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/http/httpparser.cpp: -------------------------------------------------------------------------------- 1 | #include "httpparser.h" 2 | 3 | #include "httputil.h" 4 | 5 | #include "log.h" 6 | 7 | using namespace std; 8 | 9 | namespace tnet 10 | { 11 | struct http_parser_settings ms_settings; 12 | 13 | class HttpParserSettings 14 | { 15 | public: 16 | HttpParserSettings(); 17 | 18 | static int onMessageBegin(struct http_parser*); 19 | static int onUrl(struct http_parser*, const char*, size_t); 20 | static int onStatusComplete(struct http_parser*); 21 | static int onHeaderField(struct http_parser*, const char*, size_t); 22 | static int onHeaderValue(struct http_parser*, const char*, size_t); 23 | static int onHeadersComplete(struct http_parser*); 24 | static int onBody(struct http_parser*, const char*, size_t); 25 | static int onMessageComplete(struct http_parser*); 26 | }; 27 | 28 | HttpParserSettings::HttpParserSettings() 29 | { 30 | ms_settings.on_message_begin = &HttpParserSettings::onMessageBegin; 31 | ms_settings.on_url = &HttpParserSettings::onUrl; 32 | ms_settings.on_status_complete = &HttpParserSettings::onStatusComplete; 33 | ms_settings.on_header_field = &HttpParserSettings::onHeaderField; 34 | ms_settings.on_header_value = &HttpParserSettings::onHeaderValue; 35 | ms_settings.on_headers_complete = &HttpParserSettings::onHeadersComplete; 36 | ms_settings.on_body = &HttpParserSettings::onBody; 37 | ms_settings.on_message_complete = &HttpParserSettings::onMessageComplete; 38 | } 39 | 40 | static HttpParserSettings initObj; 41 | 42 | int HttpParserSettings::onMessageBegin(struct http_parser* parser) 43 | { 44 | HttpParser* p = (HttpParser*)parser->data; 45 | return p->onParser(HttpParser::Parser_MessageBegin, 0, 0); 46 | } 47 | 48 | int HttpParserSettings::onUrl(struct http_parser* parser, const char* at, size_t length) 49 | { 50 | HttpParser* p = (HttpParser*)parser->data; 51 | return p->onParser(HttpParser::Parser_Url, at, length); 52 | } 53 | 54 | int HttpParserSettings::onStatusComplete(struct http_parser* parser) 55 | { 56 | HttpParser* p = (HttpParser*)parser->data; 57 | return p->onParser(HttpParser::Parser_StatusComplete, 0, 0); 58 | } 59 | 60 | int HttpParserSettings::onHeaderField(struct http_parser* parser, const char* at, size_t length) 61 | { 62 | HttpParser* p = (HttpParser*)parser->data; 63 | return p->onParser(HttpParser::Parser_HeaderField, at, length); 64 | } 65 | 66 | int HttpParserSettings::onHeaderValue(struct http_parser* parser, const char* at, size_t length) 67 | { 68 | HttpParser* p = (HttpParser*)parser->data; 69 | return p->onParser(HttpParser::Parser_HeaderValue, at, length); 70 | } 71 | 72 | int HttpParserSettings::onHeadersComplete(struct http_parser* parser) 73 | { 74 | HttpParser* p = (HttpParser*)parser->data; 75 | return p->onParser(HttpParser::Parser_HeadersComplete, 0, 0); 76 | } 77 | 78 | int HttpParserSettings::onBody(struct http_parser* parser, const char* at, size_t length) 79 | { 80 | HttpParser* p = (HttpParser*)parser->data; 81 | return p->onParser(HttpParser::Parser_Body, at, length); 82 | } 83 | 84 | int HttpParserSettings::onMessageComplete(struct http_parser* parser) 85 | { 86 | HttpParser* p = (HttpParser*)parser->data; 87 | return p->onParser(HttpParser::Parser_MessageComplete, 0, 0); 88 | } 89 | 90 | 91 | HttpParser::HttpParser(enum http_parser_type type) 92 | { 93 | http_parser_init(&m_parser, type); 94 | 95 | m_parser.data = this; 96 | 97 | m_lastWasValue = true; 98 | } 99 | 100 | HttpParser::~HttpParser() 101 | { 102 | 103 | } 104 | 105 | int HttpParser::onParser(Event event, const char* at, size_t length) 106 | { 107 | switch(event) 108 | { 109 | case Parser_MessageBegin: 110 | return handleMessageBegin(); 111 | case Parser_Url: 112 | return onUrl(at, length); 113 | case Parser_StatusComplete: 114 | return 0; 115 | case Parser_HeaderField: 116 | return handleHeaderField(at, length); 117 | case Parser_HeaderValue: 118 | return handleHeaderValue(at, length); 119 | case Parser_HeadersComplete: 120 | return handleHeadersComplete(); 121 | case Parser_Body: 122 | return onBody(at, length); 123 | case Parser_MessageComplete: 124 | return onMessageComplete(); 125 | default: 126 | break; 127 | } 128 | 129 | return 0; 130 | } 131 | 132 | int HttpParser::handleMessageBegin() 133 | { 134 | m_curField.clear(); 135 | m_curValue.clear(); 136 | m_lastWasValue = true; 137 | 138 | m_errorCode = 0; 139 | 140 | return onMessageBegin(); 141 | } 142 | 143 | int HttpParser::handleHeaderField(const char* at, size_t length) 144 | { 145 | if(m_lastWasValue) 146 | { 147 | if(!m_curField.empty()) 148 | { 149 | onHeader(HttpUtil::normalizeHeader(m_curField), m_curValue); 150 | } 151 | 152 | m_curField.clear(); 153 | m_curValue.clear(); 154 | } 155 | 156 | m_curField.append(at, length); 157 | 158 | m_lastWasValue = 0; 159 | 160 | return 0; 161 | } 162 | 163 | int HttpParser::handleHeaderValue(const char* at, size_t length) 164 | { 165 | m_curValue.append(at, length); 166 | m_lastWasValue = 1; 167 | 168 | return 0; 169 | } 170 | 171 | int HttpParser::handleHeadersComplete() 172 | { 173 | if(!m_curField.empty()) 174 | { 175 | string field = HttpUtil::normalizeHeader(m_curField); 176 | onHeader(field, m_curValue); 177 | } 178 | 179 | return onHeadersComplete(); 180 | } 181 | 182 | int HttpParser::execute(const char* buffer, size_t count) 183 | { 184 | int n = http_parser_execute(&m_parser, &ms_settings, buffer, count); 185 | if(m_parser.upgrade) 186 | { 187 | onUpgrade(buffer + n, count - n); 188 | return 0; 189 | } 190 | else if(n != count) 191 | { 192 | int code = (m_errorCode != 0 ? m_errorCode : 400); 193 | 194 | HttpError error(code, http_errno_description((http_errno)m_parser.http_errno)); 195 | 196 | LOG_ERROR("parser error %s", error.message.c_str()); 197 | 198 | onError(error); 199 | 200 | return code; 201 | } 202 | 203 | return 0; 204 | } 205 | } 206 | 207 | -------------------------------------------------------------------------------- /src/http/httpparser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern "C" 6 | { 7 | #include "http_parser.h" 8 | } 9 | 10 | #include "tnet_http.h" 11 | 12 | namespace tnet 13 | { 14 | class HttpParser : public nocopyable 15 | { 16 | public: 17 | friend class HttpParserSettings; 18 | 19 | HttpParser(enum http_parser_type type); 20 | virtual ~HttpParser(); 21 | 22 | enum http_parser_type getType() { return (http_parser_type)m_parser.type; } 23 | 24 | enum Event 25 | { 26 | Parser_MessageBegin, 27 | Parser_Url, 28 | Parser_StatusComplete, 29 | Parser_HeaderField, 30 | Parser_HeaderValue, 31 | Parser_HeadersComplete, 32 | Parser_Body, 33 | Parser_MessageComplete, 34 | }; 35 | 36 | int execute(const char* buf, size_t count); 37 | 38 | protected: 39 | virtual int onMessageBegin() { return 0; } 40 | virtual int onUrl(const char*, size_t) { return 0; } 41 | virtual int onHeader(const std::string& field, const std::string& value) { return 0; } 42 | virtual int onHeadersComplete() { return 0; } 43 | virtual int onBody(const char*, size_t) { return 0; } 44 | virtual int onMessageComplete() { return 0; } 45 | virtual int onUpgrade(const char*, size_t) { return 0; } 46 | virtual int onError(const HttpError& error) { return 0; } 47 | 48 | private: 49 | int onParser(Event, const char*, size_t); 50 | 51 | int handleMessageBegin(); 52 | int handleHeaderField(const char*, size_t); 53 | int handleHeaderValue(const char*, size_t); 54 | int handleHeadersComplete(); 55 | 56 | protected: 57 | struct http_parser m_parser; 58 | 59 | std::string m_curField; 60 | std::string m_curValue; 61 | bool m_lastWasValue; 62 | 63 | int m_errorCode; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/http/httprequest.cpp: -------------------------------------------------------------------------------- 1 | #include "httprequest.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | extern "C" 8 | { 9 | #include "http_parser.h" 10 | } 11 | 12 | #include "httputil.h" 13 | #include "stringutil.h" 14 | #include "log.h" 15 | 16 | using namespace std; 17 | 18 | namespace tnet 19 | { 20 | 21 | HttpRequest::HttpRequest() 22 | { 23 | majorVersion = 1; 24 | minorVersion = 1; 25 | method = HTTP_GET; 26 | } 27 | 28 | HttpRequest::~HttpRequest() 29 | { 30 | } 31 | 32 | void HttpRequest::clear() 33 | { 34 | url.clear(); 35 | schema.clear(); 36 | host.clear(); 37 | path.clear(); 38 | query.clear(); 39 | body.clear(); 40 | 41 | headers.clear(); 42 | params.clear(); 43 | 44 | majorVersion = 1; 45 | minorVersion = 1; 46 | method = HTTP_GET; 47 | port = 80; 48 | } 49 | 50 | void HttpRequest::parseUrl() 51 | { 52 | if(!schema.empty()) 53 | { 54 | return; 55 | } 56 | 57 | struct http_parser_url u; 58 | if(http_parser_parse_url(url.c_str(), url.size(), 0, &u) != 0) 59 | { 60 | LOG_ERROR("parseurl error %s", url.c_str()); 61 | return; 62 | } 63 | 64 | if(u.field_set & (1 << UF_SCHEMA)) 65 | { 66 | schema = url.substr(u.field_data[UF_SCHEMA].off, u.field_data[UF_SCHEMA].len); 67 | } 68 | 69 | if(u.field_set & (1 << UF_HOST)) 70 | { 71 | host = url.substr(u.field_data[UF_HOST].off, u.field_data[UF_HOST].len); 72 | } 73 | 74 | if(u.field_set & (1 << UF_PORT)) 75 | { 76 | port = u.port; 77 | } 78 | else 79 | { 80 | if(strcasecmp(schema.c_str(), "https") == 0 || strcasecmp(schema.c_str(), "wss") == 0) 81 | { 82 | port = 443; 83 | } 84 | else 85 | { 86 | port = 80; 87 | } 88 | } 89 | 90 | if(u.field_set & (1 << UF_PATH)) 91 | { 92 | path = url.substr(u.field_data[UF_PATH].off, u.field_data[UF_PATH].len); 93 | } 94 | 95 | if(u.field_set & (1 << UF_QUERY)) 96 | { 97 | query = url.substr(u.field_data[UF_QUERY].off, u.field_data[UF_QUERY].len); 98 | parseQuery(); 99 | } 100 | 101 | } 102 | 103 | void HttpRequest::parseQuery() 104 | { 105 | if(query.empty() || !params.empty()) 106 | { 107 | return; 108 | } 109 | 110 | static string sep1 = "&"; 111 | static string sep2 = "="; 112 | 113 | vector args = StringUtil::split(query, sep1); 114 | string key; 115 | string value; 116 | for(size_t i = 0; i < args.size(); ++i) 117 | { 118 | vector p = StringUtil::split(args[i], sep2); 119 | if(p.size() == 2) 120 | { 121 | key = p[0]; 122 | value = p[1]; 123 | } 124 | else if(p.size() == 1) 125 | { 126 | key = p[0]; 127 | value = ""; 128 | } 129 | else 130 | { 131 | //invalid, ignore 132 | continue; 133 | } 134 | 135 | params.insert(make_pair(HttpUtil::unescape(key), HttpUtil::unescape(value))); 136 | } 137 | 138 | } 139 | 140 | static const string HostKey = "Host"; 141 | static const string ContentLengthKey = "Content-Length"; 142 | 143 | string HttpRequest::dump() 144 | { 145 | string str; 146 | 147 | parseUrl(); 148 | 149 | char buf[1024]; 150 | 151 | int n = 0; 152 | if(path.empty()) 153 | { 154 | path = "/"; 155 | } 156 | 157 | if(query.empty()) 158 | { 159 | n = snprintf(buf, sizeof(buf), "%s %s HTTP/%d.%d\r\n", 160 | http_method_str(method), path.c_str(), majorVersion, minorVersion); 161 | } 162 | else 163 | { 164 | n = snprintf(buf, sizeof(buf), "%s %s?%s HTTP/%d.%d\r\n", 165 | http_method_str(method), path.c_str(), query.c_str(), majorVersion, minorVersion); 166 | } 167 | 168 | str.append(buf, n); 169 | 170 | headers.erase(HostKey); 171 | 172 | if(port == 80 || port == 443) 173 | { 174 | headers.insert(make_pair(HostKey, host)); 175 | } 176 | else 177 | { 178 | n = snprintf(buf, sizeof(buf), "%s:%d", host.c_str(), port); 179 | headers.insert(make_pair(HostKey, string(buf, n))); 180 | } 181 | 182 | if(method == HTTP_POST || method == HTTP_PUT) 183 | { 184 | headers.erase(ContentLengthKey); 185 | 186 | n = snprintf(buf, sizeof(buf), "%d", int(body.size())); 187 | headers.insert(make_pair(ContentLengthKey, string(buf, n))); 188 | } 189 | 190 | auto iter = headers.cbegin(); 191 | while(iter != headers.cend()) 192 | { 193 | int n = snprintf(buf, sizeof(buf), "%s: %s\r\n", iter->first.c_str(), iter->second.c_str()); 194 | str.append(buf, n); 195 | ++iter; 196 | } 197 | 198 | str.append("\r\n"); 199 | 200 | str.append(body); 201 | 202 | return str; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/http/httprequest.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | extern "C" 8 | { 9 | #include "http_parser.h" 10 | } 11 | 12 | #include "tnet_http.h" 13 | 14 | namespace tnet 15 | { 16 | class HttpRequest 17 | { 18 | public: 19 | HttpRequest(); 20 | ~HttpRequest(); 21 | 22 | void clear(); 23 | void parseUrl(); 24 | std::string dump(); 25 | 26 | std::string url; 27 | std::string body; 28 | 29 | std::string schema; 30 | 31 | std::string host; 32 | std::string path; 33 | std::string query; 34 | 35 | Headers_t headers; 36 | 37 | Params_t params; 38 | 39 | unsigned short majorVersion; 40 | unsigned short minorVersion; 41 | 42 | http_method method; 43 | 44 | uint16_t port; 45 | 46 | void parseQuery(); 47 | }; 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/http/httpresponse.cpp: -------------------------------------------------------------------------------- 1 | #include "httpresponse.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "httputil.h" 8 | 9 | using namespace std; 10 | 11 | namespace tnet 12 | { 13 | 14 | HttpResponse::HttpResponse() 15 | : statusCode(200) 16 | { 17 | } 18 | 19 | HttpResponse::HttpResponse(int code, const Headers_t& headers, const string& body) 20 | : statusCode(code) 21 | , body(body) 22 | , headers(headers) 23 | { 24 | 25 | } 26 | 27 | HttpResponse::~HttpResponse() 28 | { 29 | 30 | } 31 | 32 | string HttpResponse::dump() 33 | { 34 | string str; 35 | 36 | char buf[1024]; 37 | int n = snprintf(buf, sizeof(buf), "HTTP/1.1 %d %s\r\n", statusCode, HttpUtil::codeReason(statusCode).c_str()); 38 | 39 | str.append(buf, n); 40 | 41 | n = snprintf(buf, sizeof(buf), "%d", int(body.size())); 42 | static const string ContentLength = "Content-Length"; 43 | headers.insert(make_pair(ContentLength, string(buf, n))); 44 | 45 | auto it = headers.cbegin(); 46 | while(it != headers.cend()) 47 | { 48 | n = snprintf(buf, sizeof(buf), "%s: %s\r\n", it->first.c_str(), it->second.c_str()); 49 | str.append(buf, n); 50 | ++it; 51 | } 52 | 53 | str.append("\r\n"); 54 | str.append(body); 55 | 56 | return str; 57 | } 58 | 59 | void HttpResponse::setContentType(const std::string& contentType) 60 | { 61 | static const string ContentTypeKey = "Content-Type"; 62 | headers.insert(make_pair(ContentTypeKey, contentType)); 63 | } 64 | 65 | void HttpResponse::setKeepAlive(bool on) 66 | { 67 | static const string ConnectionKey = "Connection"; 68 | if(on) 69 | { 70 | static const string KeepAliveValue = "Keep-Alive"; 71 | headers.insert(make_pair(ConnectionKey, KeepAliveValue)); 72 | } 73 | else 74 | { 75 | static const string CloseValue = "close"; 76 | headers.insert(make_pair(ConnectionKey, CloseValue)); 77 | } 78 | } 79 | 80 | void HttpResponse::enableDate() 81 | { 82 | time_t now = time(NULL); 83 | struct tm t; 84 | gmtime_r(&now, &t); 85 | char buf[128]; 86 | int n = strftime(buf, sizeof buf, "%a, %d %b %Y %H:%M:%S %Z", &t); 87 | static const string DateKey = "Date"; 88 | headers.insert(make_pair(DateKey, string(buf, n))); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/http/httpresponse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "tnet_http.h" 7 | 8 | namespace tnet 9 | { 10 | class HttpResponse 11 | { 12 | public: 13 | HttpResponse(); 14 | 15 | HttpResponse(int code, const Headers_t& headers = Headers_t(), const std::string& body = ""); 16 | ~HttpResponse(); 17 | 18 | void clear() 19 | { 20 | statusCode = 200; 21 | body.clear(); 22 | headers.clear(); 23 | } 24 | 25 | void setContentType(const std::string& contentType); 26 | void setKeepAlive(bool on); 27 | 28 | void enableDate(); 29 | 30 | //generate http response text 31 | std::string dump(); 32 | 33 | int statusCode; 34 | std::string body; 35 | 36 | Headers_t headers; 37 | }; 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/http/httpserver.cpp: -------------------------------------------------------------------------------- 1 | #include "httpserver.h" 2 | 3 | #include 4 | 5 | #include "tcpserver.h" 6 | #include "log.h" 7 | #include "httpconnection.h" 8 | #include "httprequest.h" 9 | #include "httpresponse.h" 10 | #include "wsconnection.h" 11 | #include "connection.h" 12 | #include "httpparser.h" 13 | #include "wsutil.h" 14 | 15 | using namespace std; 16 | 17 | namespace tnet 18 | { 19 | static string rootPath = "/"; 20 | 21 | void httpNotFoundCallback(const HttpConnectionPtr_t& conn, const HttpRequest& request) 22 | { 23 | HttpResponse resp; 24 | resp.statusCode = 404; 25 | 26 | conn->send(resp); 27 | } 28 | 29 | 30 | HttpServer::HttpServer(TcpServer* server) 31 | : m_server(server) 32 | { 33 | } 34 | 35 | HttpServer::~HttpServer() 36 | { 37 | 38 | } 39 | 40 | int HttpServer::listen(const Address& addr) 41 | { 42 | return m_server->listen(addr, std::bind(&HttpServer::onConnEvent, this, _1, _2, _3)); 43 | } 44 | 45 | void HttpServer::onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context) 46 | { 47 | switch(event) 48 | { 49 | case Conn_EstablishedEvent: 50 | { 51 | HttpConnectionPtr_t httpConn = std::make_shared(conn, 52 | std::bind(&HttpServer::onRequest, this, _1, _2, _3, _4)); 53 | 54 | conn->setEventCallback(std::bind(&HttpConnection::onConnEvent, httpConn, _1, _2, _3)); 55 | } 56 | break; 57 | default: 58 | LOG_INFO("error when enter this"); 59 | return; 60 | } 61 | } 62 | 63 | void HttpServer::setHttpCallback(const string& path, const HttpCallback_t& callback) 64 | { 65 | m_httpCallbacks[path] = callback; 66 | } 67 | 68 | void HttpServer::setWsCallback(const string& path, const WsCallback_t& callback) 69 | { 70 | m_wsCallbacks[path] = callback; 71 | } 72 | 73 | void HttpServer::setHttpCallback(const string& path, const HttpCallback_t& callback, const AuthCallback_t& auth) 74 | { 75 | setHttpCallback(path, callback); 76 | 77 | m_authCallbacks[path] = auth; 78 | } 79 | 80 | void HttpServer::setWsCallback(const string& path, const WsCallback_t& callback, const AuthCallback_t& auth) 81 | { 82 | setWsCallback(path, callback); 83 | 84 | m_authCallbacks[path] = auth; 85 | } 86 | 87 | void HttpServer::onError(const HttpConnectionPtr_t& conn, const HttpError& error) 88 | { 89 | conn->send(error.statusCode, error.message); 90 | conn->shutDown(1000); 91 | } 92 | 93 | bool HttpServer::authRequest(const HttpConnectionPtr_t& conn, const HttpRequest& request) 94 | { 95 | auto it = m_authCallbacks.find(request.path); 96 | if(it == m_authCallbacks.end()) 97 | { 98 | return true; 99 | } 100 | 101 | HttpError err = (it->second)(request); 102 | if(err.statusCode != 200) 103 | { 104 | onError(conn, err); 105 | return false; 106 | } 107 | else 108 | { 109 | return true; 110 | } 111 | } 112 | 113 | void HttpServer::onRequest(const HttpConnectionPtr_t& conn, const HttpRequest& request, RequestEvent event, const void* context) 114 | { 115 | switch(event) 116 | { 117 | case Request_Upgrade: 118 | onWebsocket(conn, request, context); 119 | break; 120 | case Request_Error: 121 | onError(conn, *(HttpError*)context); 122 | break; 123 | case Request_Complete: 124 | { 125 | map::iterator iter = m_httpCallbacks.find(request.path); 126 | if(iter == m_httpCallbacks.end()) 127 | { 128 | httpNotFoundCallback(conn, request); 129 | } 130 | else 131 | { 132 | if(authRequest(conn, request)) 133 | { 134 | (iter->second)(conn, request); 135 | } 136 | } 137 | } 138 | break; 139 | default: 140 | LOG_ERROR("invalid request event %d", event); 141 | break; 142 | } 143 | } 144 | 145 | 146 | void HttpServer::onWebsocket(const HttpConnectionPtr_t& conn, const HttpRequest& request, const void* context) 147 | { 148 | map::iterator iter = m_wsCallbacks.find(request.path); 149 | if(iter == m_wsCallbacks.end()) 150 | { 151 | onError(conn, 404); 152 | } 153 | else 154 | { 155 | if(!authRequest(conn, request)) 156 | { 157 | return; 158 | } 159 | 160 | const StackBuffer* buffer = (const StackBuffer*)context; 161 | 162 | ConnectionPtr_t c = conn->lockConn(); 163 | if(!c) 164 | { 165 | return; 166 | } 167 | 168 | WsConnectionPtr_t wsConn(new WsConnection(c, iter->second)); 169 | 170 | HttpResponse resp; 171 | HttpError error = WsUtil::handshake(request, resp); 172 | 173 | if(error.statusCode != 200) 174 | { 175 | onError(conn, error); 176 | return; 177 | } 178 | 179 | c->send(resp.dump()); 180 | 181 | wsConn->onOpen(&request); 182 | 183 | wsConn->onRead(c, buffer->buffer, buffer->count); 184 | c->setEventCallback(std::bind(&WsConnection::onConnEvent, wsConn, _1, _2, _3)); 185 | 186 | return; 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/http/httpserver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "tnet_http.h" 9 | 10 | extern "C" 11 | { 12 | #include "http_parser.h" 13 | } 14 | 15 | namespace tnet 16 | { 17 | class TcpServer; 18 | class Address; 19 | class WsConnection; 20 | class HttpRequest; 21 | class HttpResponse; 22 | class Connection; 23 | class HttpConnection; 24 | 25 | class HttpServer : public nocopyable 26 | { 27 | public: 28 | friend class HttpConnection; 29 | 30 | HttpServer(TcpServer* server); 31 | ~HttpServer(); 32 | 33 | int listen(const Address& addr); 34 | 35 | void setHttpCallback(const std::string& path, const HttpCallback_t& callback); 36 | void setWsCallback(const std::string& path, const WsCallback_t& callback); 37 | 38 | void setHttpCallback(const std::string& path, const HttpCallback_t& callback, const AuthCallback_t& auth); 39 | void setWsCallback(const std::string& path, const WsCallback_t& callback, const AuthCallback_t& auth); 40 | 41 | private: 42 | void onConnEvent(const ConnectionPtr_t&, ConnEvent, const void* context); 43 | 44 | void onRequest(const HttpConnectionPtr_t& conn, const HttpRequest& request, RequestEvent event, const void* context); 45 | void onWebsocket(const HttpConnectionPtr_t& conn, const HttpRequest& request, const void* context); 46 | 47 | void onError(const HttpConnectionPtr_t& conn, const HttpError& error); 48 | 49 | bool authRequest(const HttpConnectionPtr_t& conn, const HttpRequest& request); 50 | 51 | private: 52 | TcpServer* m_server; 53 | 54 | std::map m_httpCallbacks; 55 | std::map m_wsCallbacks; 56 | 57 | std::map m_authCallbacks; 58 | }; 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /src/http/httputil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace tnet 7 | { 8 | class HttpUtil 9 | { 10 | public: 11 | static const std::string& codeReason(int code); 12 | static const char* methodStr(uint8_t method); 13 | 14 | static std::string escape(const std::string& src); 15 | static std::string unescape(const std::string& src); 16 | 17 | //http header key is Http-Head-Case format 18 | static std::string normalizeHeader(const std::string& src); 19 | }; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/http/tnet_http.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "tnet.h" 8 | 9 | extern "C" 10 | { 11 | struct http_parser; 12 | } 13 | 14 | namespace tnet 15 | { 16 | class HttpConnection; 17 | class WsConnection; 18 | class HttpRequest; 19 | class HttpResponse; 20 | 21 | class HttpParser; 22 | class HttpClientConn; 23 | class HttpClient; 24 | class WsClient; 25 | class HttpConnector; 26 | 27 | //we use 599 for our tnet error 28 | 29 | const int TNET_HTTP_ERROR = 599; 30 | 31 | class HttpError 32 | { 33 | public: 34 | HttpError(int code = 200, const std::string& m = std::string()) 35 | : statusCode(code) 36 | , message(m) 37 | {} 38 | 39 | //200 for no error 40 | int statusCode; 41 | std::string message; 42 | }; 43 | 44 | enum WsEvent 45 | { 46 | Ws_OpenEvent, 47 | Ws_CloseEvent, 48 | Ws_MessageEvent, 49 | Ws_PongEvent, 50 | Ws_ErrorEvent, 51 | }; 52 | 53 | typedef std::shared_ptr HttpConnectionPtr_t; 54 | typedef std::weak_ptr WeakHttpConnectionPtr_t; 55 | 56 | typedef std::shared_ptr WsConnectionPtr_t; 57 | typedef std::weak_ptr WeakWsConnectionPtr_t; 58 | 59 | typedef std::shared_ptr HttpParser_t; 60 | typedef std::shared_ptr HttpClientConnPtr_t; 61 | typedef std::shared_ptr HttpConnectorPtr_t; 62 | typedef std::weak_ptr WeakHttpConnectorPtr_t; 63 | 64 | typedef std::shared_ptr HttpClientPtr_t; 65 | typedef std::shared_ptr WsClientPtr_t; 66 | 67 | enum RequestEvent 68 | { 69 | Request_Upgrade, 70 | Request_Complete, 71 | Request_Error, 72 | }; 73 | 74 | //Request_Upgrade: context is &StackBuffer 75 | //Request_Complete: context is 0 76 | //Request_Error: context is &HttpError 77 | 78 | enum ResponseEvent 79 | { 80 | Response_Complete, 81 | Response_Error, 82 | }; 83 | 84 | //Response_Complete: context is 0 85 | //Response_Error: context is &HttpError 86 | 87 | typedef std::function HttpCallback_t; 88 | 89 | //Ws_OpenEvent: server side, context is &HttpRequest, client side, context is &HttpResponse 90 | //Ws_MessageEvent: context is &std::string 91 | //Other Ws Event: context is 0 92 | 93 | typedef std::function WsCallback_t; 94 | 95 | typedef std::function ResponseCallback_t; 96 | 97 | typedef std::function AuthCallback_t; 98 | 99 | struct CaseKeyCmp 100 | { 101 | bool operator() (const std::string& p1, const std::string& p2) const 102 | { 103 | return strcasecmp(p1.c_str(), p2.c_str()) < 0; 104 | } 105 | }; 106 | 107 | typedef std::multimap Headers_t; 108 | typedef std::multimap Params_t; 109 | } 110 | -------------------------------------------------------------------------------- /src/http/wsclient.cpp: -------------------------------------------------------------------------------- 1 | #include "wsclient.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "connection.h" 7 | #include "wsconnection.h" 8 | #include "address.h" 9 | #include "stringutil.h" 10 | #include "wsutil.h" 11 | #include "log.h" 12 | #include "httprequest.h" 13 | #include "httpresponse.h" 14 | #include "ioloop.h" 15 | #include "sockutil.h" 16 | #include "address.h" 17 | #include "httpconnector.h" 18 | #include "connector.inl" 19 | 20 | using namespace std; 21 | 22 | namespace tnet 23 | { 24 | WsClient::WsClient(IOLoop* loop) 25 | : m_loop(loop) 26 | { 27 | 28 | } 29 | 30 | WsClient::~WsClient() 31 | { 32 | 33 | } 34 | 35 | void WsClient::connect(const string& url, const WsCallback_t& callback) 36 | { 37 | HttpRequest req; 38 | req.url = url; 39 | connect(req, callback); 40 | } 41 | 42 | void WsClient::connect(const string& url, const Headers_t& headers, const WsCallback_t& callback) 43 | { 44 | HttpRequest req; 45 | req.url = url; 46 | req.headers = headers; 47 | 48 | connect(req, callback); 49 | } 50 | 51 | void WsClient::connect(HttpRequest& request, const WsCallback_t& callback) 52 | { 53 | WsUtil::buildRequest(request); 54 | 55 | request.parseUrl(); 56 | 57 | Address addr(request.host, request.port); 58 | 59 | HttpConnectorPtr_t conn = std::make_shared(); 60 | conn->connect(m_loop, addr, std::bind(&WsClient::onConnect, shared_from_this(), _1, _2, request.dump(), callback)); 61 | } 62 | 63 | void WsClient::onResponse(const HttpConnectorPtr_t& conn, const HttpResponse& response, ResponseEvent event, const WsCallback_t& callback) 64 | { 65 | ConnectionPtr_t c = conn->lockConn(); 66 | if(c) 67 | { 68 | WsConnectionPtr_t wsConn = std::make_shared(c, callback); 69 | if(response.statusCode != 101) 70 | { 71 | wsConn->onError(); 72 | return; 73 | } 74 | 75 | wsConn->onOpen(&response); 76 | 77 | c->setEventCallback(std::bind(&WsConnection::onConnEvent, wsConn, _1, _2, _3)); 78 | } 79 | } 80 | 81 | void WsClient::onConnect(const HttpConnectorPtr_t& conn, bool connected, 82 | const string& requestData, const WsCallback_t& callback) 83 | { 84 | if(!connected) 85 | { 86 | LOG_ERROR("wsclient connect error"); 87 | return; 88 | } 89 | 90 | string data = std::move(requestData); 91 | WsCallback_t cb = std::move(callback); 92 | 93 | conn->setCallback(std::bind(&WsClient::onResponse, shared_from_this(), _1, _2, _3, cb)); 94 | conn->send(data); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/http/wsclient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "tnet_http.h" 6 | 7 | namespace tnet 8 | { 9 | class WsClient : public nocopyable 10 | , public std::enable_shared_from_this 11 | { 12 | public: 13 | WsClient(IOLoop* loop); 14 | ~WsClient(); 15 | 16 | //now only support ip:port 17 | void connect(const std::string& url, const WsCallback_t& callback); 18 | void connect(const std::string& url, const Headers_t& headers, const WsCallback_t& callback); 19 | 20 | private: 21 | void connect(HttpRequest& request, const WsCallback_t& callback); 22 | 23 | void onResponse(const HttpConnectorPtr_t&, const HttpResponse&, ResponseEvent, const WsCallback_t&); 24 | 25 | void onConnect(const HttpConnectorPtr_t& conn, bool connected, const std::string& requestData, const WsCallback_t& callback); 26 | 27 | private: 28 | IOLoop* m_loop; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/http/wsconnection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "tnet_http.h" 8 | 9 | namespace tnet 10 | { 11 | //now we wiil only support rfc6455 12 | //refer to tornado websocket implementation 13 | 14 | class Connection; 15 | class HttpRequest; 16 | 17 | class WsConnection : public nocopyable 18 | , public std::enable_shared_from_this 19 | { 20 | public: 21 | friend class HttpServer; 22 | friend class WsClient; 23 | 24 | WsConnection(const ConnectionPtr_t& conn, const WsCallback_t& callback); 25 | ~WsConnection(); 26 | 27 | int getSockFd() const { return m_fd; } 28 | 29 | void ping(const std::string& message); 30 | void send(const std::string& message, bool binary); 31 | void send(const std::string& message); 32 | 33 | //callback likes HttpConnection send callback 34 | void send(const std::string& message, bool binary, const Callback_t& callback); 35 | void send(const std::string& message, const Callback_t& callback); 36 | 37 | void close(); 38 | 39 | void shutDown(); 40 | 41 | static void setMaxPayloadLen(size_t len) { ms_maxPayloadLen = len; } 42 | 43 | private: 44 | void onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void*); 45 | 46 | void onOpen(const void* context); 47 | void onError(); 48 | 49 | ssize_t onRead(const ConnectionPtr_t& conn, const char* data, size_t count); 50 | 51 | bool isFinalFrame() { return m_final; } 52 | bool isMaskFrame() { return m_mask; } 53 | bool isControlFrame() { return m_opcode & 0x08; } 54 | bool isTextFrame() { return (m_opcode == 0) ? (m_lastOpcode == 0x1) : (m_opcode == 0x1); } 55 | bool isBinaryFrame() { return (m_opcode == 0) ? (m_lastOpcode == 0x2) : (m_opcode == 0x2); } 56 | 57 | ssize_t onFrameStart(const char* data, size_t count); 58 | ssize_t onFramePayloadLen(const char* data, size_t count); 59 | ssize_t onFramePayloadLen16(const char* data, size_t count); 60 | ssize_t onFramePayloadLen64(const char* data, size_t count); 61 | 62 | ssize_t onFrameMaskingKey(const char* data, size_t count); 63 | ssize_t onFrameData(const char* data, size_t count); 64 | 65 | ssize_t handleFramePayloadLen(size_t payloadLen); 66 | ssize_t handleFrameData(const ConnectionPtr_t& conn); 67 | ssize_t handleMessage(const ConnectionPtr_t& conn, uint8_t opcode, const std::string& message); 68 | ssize_t tryRead(const char* data, size_t count, size_t tryReadData); 69 | 70 | void sendFrame(bool finalFrame, char opcode, const std::string& message = std::string()); 71 | 72 | private: 73 | enum FrameStatus 74 | { 75 | FrameStart, 76 | FramePayloadLen, 77 | FramePayloadLen16, 78 | FramePayloadLen64, 79 | FrameMaskingKey, 80 | FrameData, 81 | FrameFinal, 82 | FrameError, 83 | }; 84 | 85 | WeakConnectionPtr_t m_conn; 86 | 87 | size_t m_payloadLen; 88 | 89 | FrameStatus m_status; 90 | 91 | uint8_t m_maskingKey[4]; 92 | 93 | uint8_t m_final; 94 | uint8_t m_opcode; 95 | uint8_t m_mask; 96 | uint8_t m_lastOpcode; 97 | 98 | std::string m_cache; 99 | 100 | std::string m_appData; 101 | 102 | WsCallback_t m_callback; 103 | 104 | Callback_t m_sendCallback; 105 | 106 | int m_fd; 107 | 108 | static size_t ms_maxPayloadLen; 109 | }; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /src/http/wsutil.cpp: -------------------------------------------------------------------------------- 1 | #include "wsutil.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "httprequest.h" 7 | #include "httpresponse.h" 8 | #include "stringutil.h" 9 | #include "log.h" 10 | 11 | using namespace std; 12 | 13 | namespace tnet 14 | { 15 | static const string upgradeKey = "Upgrade"; 16 | static const string upgradeValue = "websocket"; 17 | static const string connectionKey = "Connection"; 18 | static const string connectionValue = "Upgrade"; 19 | static const string wsVersionKey = "Sec-Websocket-Version"; 20 | static const string wsVersionValue = "13"; 21 | static const string wsProtocolKey = "Sec-Websocket-Protocol"; 22 | static const string wsProtocolValue = "chat"; 23 | static const string wsAcceptKey = "Sec-Websocket-Accept"; 24 | static const string wsKey = "Sec-Websocket-Key"; 25 | static const string originKey = "Origin"; 26 | static const string wsMagicKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 27 | 28 | string calcKey() 29 | { 30 | int key[4]; 31 | for(int i = 0; i < 4; ++i) 32 | { 33 | key[i] = (int)random(); 34 | } 35 | 36 | return StringUtil::base64Encode(string((char*)key, 16)); 37 | } 38 | 39 | string getOrigin(HttpRequest& request) 40 | { 41 | string str; 42 | char buffer[1024]; 43 | int n = 0; 44 | request.parseUrl(); 45 | if(strcasecmp(request.schema.c_str(), "ws") == 0) 46 | { 47 | n = snprintf(buffer, sizeof(buffer), "http://%s", request.host.c_str()); 48 | } 49 | else if(strcasecmp(request.schema.c_str(), "wss") == 0) 50 | { 51 | n = snprintf(buffer, sizeof(buffer), "https://%s", request.host.c_str()); 52 | } 53 | else 54 | { 55 | n = snprintf(buffer, sizeof(buffer), "%s://%s", request.schema.c_str(), request.host.c_str()); 56 | } 57 | 58 | str.append(buffer, n); 59 | 60 | if(request.port != 80 && request.port != 443) 61 | { 62 | n = snprintf(buffer, sizeof(buffer), ":%d", request.port); 63 | str.append(buffer, n); 64 | } 65 | 66 | return str; 67 | } 68 | 69 | int WsUtil::buildRequest(HttpRequest& req) 70 | { 71 | req.headers.insert(make_pair(upgradeKey, upgradeValue)); 72 | req.headers.insert(make_pair(connectionKey, connectionValue)); 73 | 74 | req.headers.insert(make_pair(wsProtocolKey, wsProtocolValue)); 75 | 76 | req.headers.insert(make_pair(wsVersionKey, wsVersionValue)); 77 | req.headers.insert(make_pair(wsKey, calcKey())); 78 | req.headers.insert(make_pair(originKey, getOrigin(req))); 79 | return 0; 80 | } 81 | 82 | HttpError checkHeader(const HttpRequest& request) 83 | { 84 | if(request.method != HTTP_GET) 85 | { 86 | return HttpError(405); 87 | } 88 | 89 | auto iter = request.headers.find(upgradeKey); 90 | if(iter == request.headers.end() || strcasestr(iter->second.c_str(), upgradeValue.c_str()) == NULL) 91 | { 92 | return HttpError(400, "Can \"Upgrade\" only to \"websocket\""); 93 | } 94 | 95 | iter = request.headers.find(connectionKey); 96 | if(iter == request.headers.end() || strcasestr(iter->second.c_str(), connectionValue.c_str()) == NULL) 97 | { 98 | return HttpError(400, "\"Connection\" must be \"upgrade\""); 99 | } 100 | 101 | iter = request.headers.find(wsVersionKey); 102 | bool validVersion = false; 103 | if(iter != request.headers.end()) 104 | { 105 | int version = atoi(iter->second.c_str()); 106 | if(version == 13 || version == 7 || version == 8) 107 | { 108 | validVersion = true; 109 | } 110 | } 111 | 112 | if(!validVersion) 113 | { 114 | return HttpError(426, "Sec-WebSocket-Version: 13"); 115 | } 116 | 117 | return HttpError(200); 118 | } 119 | 120 | static string calcAcceptKey(const HttpRequest& request) 121 | { 122 | auto iter = request.headers.find(wsKey); 123 | if(iter == request.headers.end()) 124 | { 125 | return ""; 126 | } 127 | 128 | string key = iter->second + wsMagicKey; 129 | 130 | return StringUtil::base64Encode(StringUtil::sha1Bin(key)); 131 | } 132 | 133 | HttpError WsUtil::handshake(const HttpRequest& request, HttpResponse& resp) 134 | { 135 | HttpError error = checkHeader(request); 136 | if(error.statusCode != 200) 137 | { 138 | return error; 139 | } 140 | 141 | resp.statusCode = 101; 142 | resp.headers.insert(make_pair(upgradeKey, upgradeValue)); 143 | resp.headers.insert(make_pair(connectionKey, upgradeKey)); 144 | resp.headers.insert(make_pair(wsAcceptKey, calcAcceptKey(request))); 145 | 146 | auto iter = request.headers.find(wsProtocolKey); 147 | 148 | if(iter != request.headers.end()) 149 | { 150 | static string sep = ","; 151 | vector subs = StringUtil::split(iter->second, sep); 152 | 153 | //now we only choose first subprotocol 154 | if(!subs.empty()) 155 | { 156 | resp.headers.insert(make_pair(wsProtocolKey, subs[0])); 157 | } 158 | } 159 | 160 | return error; 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/http/wsutil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tnet_http.h" 4 | 5 | namespace tnet 6 | { 7 | class WsUtil 8 | { 9 | public: 10 | static int buildRequest(HttpRequest& request); 11 | 12 | static HttpError handshake(const HttpRequest& request, HttpResponse& resp); 13 | }; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/ioevent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tnet.h" 4 | 5 | namespace tnet 6 | { 7 | class IOEvent 8 | { 9 | public: 10 | IOEvent(int fd_, int events_, const IOHandler_t& handler_) 11 | : fd(fd_) 12 | , events(events_) 13 | , handler(handler_) 14 | { 15 | 16 | } 17 | 18 | int fd; 19 | int events; 20 | IOHandler_t handler; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/ioloop.cpp: -------------------------------------------------------------------------------- 1 | #include "ioloop.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "poller.h" 9 | #include "log.h" 10 | #include "ioevent.h" 11 | #include "timer.h" 12 | #include "notifier.h" 13 | #include "timingwheel.h" 14 | 15 | using namespace std; 16 | 17 | namespace tnet 18 | { 19 | class IgnoreSigPipe 20 | { 21 | public: 22 | IgnoreSigPipe() 23 | { 24 | signal(SIGPIPE, SIG_IGN); 25 | } 26 | }; 27 | 28 | static IgnoreSigPipe initObj; 29 | 30 | const int DefaultEventsCapacity = 1024; 31 | const int MaxPollWaitTime = 1 * 1000; 32 | 33 | IOLoop::IOLoop() 34 | { 35 | m_running = false; 36 | 37 | m_poller = new Poller(this); 38 | 39 | m_notifier = std::make_shared(std::bind(&IOLoop::onWake, this, _1)); 40 | 41 | m_wheel = std::make_shared(1000, 3600); 42 | 43 | m_events.resize(DefaultEventsCapacity, 0); 44 | } 45 | 46 | IOLoop::~IOLoop() 47 | { 48 | delete m_poller; 49 | 50 | for_each(m_events.begin(), m_events.end(), 51 | default_delete()); 52 | } 53 | 54 | void IOLoop::start() 55 | { 56 | m_running = true; 57 | 58 | m_notifier->start(this); 59 | 60 | m_wheel->start(this); 61 | 62 | run(); 63 | } 64 | 65 | void IOLoop::stop() 66 | { 67 | m_running = false; 68 | 69 | m_notifier->notify(); 70 | } 71 | 72 | void IOLoop::run() 73 | { 74 | while(m_running) 75 | { 76 | m_poller->poll(MaxPollWaitTime, m_events); 77 | 78 | handleCallbacks(); 79 | } 80 | 81 | LOG_INFO("loop stop"); 82 | 83 | m_notifier->stop(); 84 | } 85 | 86 | int IOLoop::addHandler(int fd, int events, const IOHandler_t& handler) 87 | { 88 | if(m_events.size() <= fd) 89 | { 90 | m_events.resize(fd + 1, 0); 91 | } 92 | 93 | if(m_events[fd] != 0) 94 | { 95 | LOG_ERROR("add duplicate handler %d", fd); 96 | return -1; 97 | } 98 | 99 | if(m_poller->add(fd, events) != 0) 100 | { 101 | return -1; 102 | } 103 | 104 | m_events[fd] = new IOEvent(fd, events, handler); 105 | return 0; 106 | } 107 | 108 | int IOLoop::updateHandler(int fd, int events) 109 | { 110 | if(m_events.size() <= fd || m_events[fd] == 0) 111 | { 112 | LOG_ERROR("invalid fd %d", fd); 113 | return -1; 114 | } 115 | 116 | if(m_events[fd]->events == events) 117 | { 118 | return 0; 119 | } 120 | 121 | if(m_poller->update(fd, events) != 0) 122 | { 123 | return -1; 124 | } 125 | 126 | m_events[fd]->events = events; 127 | 128 | return 0; 129 | } 130 | 131 | int IOLoop::removeHandler(int fd) 132 | { 133 | if(m_events.size() <= fd || m_events[fd] == 0) 134 | { 135 | LOG_INFO("invalid fd %d", fd); 136 | return -1; 137 | } 138 | 139 | m_poller->remove(fd); 140 | 141 | delete m_events[fd]; 142 | m_events[fd] = NULL; 143 | 144 | return 0; 145 | } 146 | 147 | void onTimerHandler(const TimerPtr_t& timer, const Callback_t& callback) 148 | { 149 | callback(); 150 | timer->stop(); 151 | } 152 | 153 | TimerPtr_t IOLoop::runAfter(int after, const Callback_t& callback) 154 | { 155 | TimerPtr_t timer = std::make_shared( 156 | std::bind(&onTimerHandler, _1, callback), after, 0); 157 | timer->start(this); 158 | return timer; 159 | } 160 | 161 | void IOLoop::addCallback(const Callback_t& callback) 162 | { 163 | { 164 | SpinLockGuard g(m_lock); 165 | 166 | m_callbacks.push_back(callback); 167 | } 168 | 169 | m_notifier->notify(); 170 | } 171 | 172 | void IOLoop::handleCallbacks() 173 | { 174 | vector callbacks; 175 | { 176 | SpinLockGuard g(m_lock); 177 | callbacks.swap(m_callbacks); 178 | } 179 | 180 | for(size_t i = 0; i < callbacks.size(); ++i) 181 | { 182 | callbacks[i](); 183 | } 184 | } 185 | 186 | void IOLoop::onWake(const NotifierPtr_t& notifier) 187 | { 188 | //only to wakeup poll 189 | } 190 | 191 | void IOLoop::runInWheel(int timeout, const TimingWheelHandler_t& handler) 192 | { 193 | m_wheel->add(handler, timeout); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/ioloop.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "tnet.h" 6 | #include "spinlock.h" 7 | 8 | namespace tnet 9 | { 10 | class IOEvent; 11 | class Poller; 12 | 13 | class IOLoop 14 | { 15 | public: 16 | IOLoop(); 17 | ~IOLoop(); 18 | 19 | void start(); 20 | void stop(); 21 | 22 | int addHandler(int fd, int events, const IOHandler_t& handler); 23 | int updateHandler(int fd, int events); 24 | int removeHandler(int fd); 25 | 26 | //after is milliseconds 27 | TimerPtr_t runAfter(int after, const Callback_t& callback); 28 | 29 | //this only thread safe 30 | void addCallback(const Callback_t& callback); 31 | 32 | //timeout is milliseconds 33 | //wheel max timeout is 3600 * 1000 34 | //wheel interval is 1000 35 | void runInWheel(int timeout, const TimingWheelHandler_t& handler); 36 | 37 | private: 38 | void run(); 39 | 40 | void onWake(const NotifierPtr_t& notifier); 41 | 42 | void handleCallbacks(); 43 | 44 | private: 45 | int m_pollFd; 46 | 47 | bool m_running; 48 | 49 | std::vector m_events; 50 | 51 | Poller* m_poller; 52 | 53 | std::vector m_callbacks; 54 | SpinLock m_lock; 55 | 56 | NotifierPtr_t m_notifier; 57 | 58 | TimingWheelPtr_t m_wheel; 59 | }; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/log.cpp: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace tnet 10 | { 11 | __thread char errnoMsgBuf[1024]; 12 | 13 | const char* errorMsg(int code) 14 | { 15 | return strerror_r(code, errnoMsgBuf, sizeof(errnoMsgBuf)); 16 | } 17 | 18 | static int MaxLogMsg = 1024; 19 | 20 | static const char* LevelMsg[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; 21 | 22 | #define WRITE_LOG(level)\ 23 | if(m_level <= level)\ 24 | {\ 25 | char msg[MaxLogMsg];\ 26 | va_list ap;\ 27 | va_start(ap, fmt);\ 28 | vsnprintf(msg, sizeof(msg), fmt, ap);\ 29 | va_end(ap);\ 30 | log(LevelMsg[int(level)], file, function, line, msg);\ 31 | } 32 | 33 | 34 | 35 | static const char* DateTimeFormat = "%Y-%m-%d %H:%M:%S"; 36 | 37 | Log::Log() 38 | { 39 | m_fd = stdout; 40 | m_level = TRACE; 41 | } 42 | 43 | Log::Log(const char* fileName) 44 | { 45 | m_fd = fopen(fileName, "ab+"); 46 | m_level = TRACE; 47 | } 48 | 49 | Log::~Log() 50 | { 51 | if(m_fd != stdout) 52 | { 53 | fclose(m_fd); 54 | } 55 | } 56 | 57 | void Log::redirect(const char* fileName) 58 | { 59 | if(m_fd != stdout) 60 | { 61 | fclose(m_fd); 62 | m_fd = fopen(fileName, "ab+"); 63 | } 64 | } 65 | 66 | void Log::trace(const char* file, const char* function, int line, const char* fmt, ...) 67 | { 68 | WRITE_LOG(TRACE); 69 | } 70 | 71 | void Log::debug(const char* file, const char* function, int line, const char* fmt, ...) 72 | { 73 | WRITE_LOG(DEBUG); 74 | } 75 | 76 | void Log::info(const char* file, const char* function, int line, const char* fmt, ...) 77 | { 78 | WRITE_LOG(INFO); 79 | } 80 | 81 | void Log::warn(const char* file, const char* function, int line, const char* fmt, ...) 82 | { 83 | WRITE_LOG(WARN); 84 | } 85 | 86 | void Log::error(const char* file, const char* function, int line, const char* fmt, ...) 87 | { 88 | WRITE_LOG(ERROR); 89 | } 90 | 91 | void Log::fatal(const char* file, const char* function, int line, const char* fmt, ...) 92 | { 93 | WRITE_LOG(FATAL); 94 | } 95 | 96 | void Log::log(const char* level, const char* file, const char* function, int line, const char* msg) 97 | { 98 | char buf[64]; 99 | 100 | time_t now = time(NULL); 101 | strftime(buf, sizeof(buf), DateTimeFormat, gmtime(&now)); 102 | 103 | fprintf(m_fd, "%s %s [%d] %s %s:%d %s\n", buf, level, getpid(), function, file, line, msg); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "nocopyable.h" 7 | 8 | namespace tnet 9 | { 10 | class Log : public nocopyable 11 | { 12 | public: 13 | enum Level 14 | { 15 | TRACE = 0, 16 | DEBUG, 17 | INFO, 18 | WARN, 19 | ERROR, 20 | FATAL, 21 | }; 22 | 23 | Log(); 24 | Log(const char* fileName); 25 | ~Log(); 26 | 27 | static Log& rootLog() 28 | { 29 | static Log log; 30 | return log; 31 | } 32 | 33 | void redirect(const char* fileName); 34 | 35 | void setLevel(Level level) { m_level = level; } 36 | Level getLevel() { return m_level; } 37 | 38 | void trace(const char* file, const char* function, int line, const char* fmt, ...); 39 | void debug(const char* file, const char* function, int line, const char* fmt, ...); 40 | void info(const char* file, const char* function, int line, const char* fmt, ...); 41 | void warn(const char* file, const char* function, int line, const char* fmt, ...); 42 | void error(const char* file, const char* function, int line, const char* fmt, ...); 43 | void fatal(const char* file, const char* function, int line, const char* fmt, ...); 44 | 45 | private: 46 | void log(const char* level, const char* file, const char* function, int line, const char* msg); 47 | 48 | private: 49 | FILE* m_fd; 50 | Level m_level; 51 | }; 52 | 53 | #define LOG_TRACE(fmt, args...) Log::rootLog().trace(__FILE__, __FUNCTION__, __LINE__, fmt, ##args) 54 | #define LOG_DEBUG(fmt, args...) Log::rootLog().debug(__FILE__, __FUNCTION__, __LINE__, fmt, ##args) 55 | #define LOG_INFO(fmt, args...) Log::rootLog().info(__FILE__, __FUNCTION__, __LINE__, fmt, ##args) 56 | #define LOG_WARN(fmt, args...) Log::rootLog().warn(__FILE__, __FUNCTION__, __LINE__, fmt, ##args) 57 | #define LOG_ERROR(fmt, args...) Log::rootLog().error(__FILE__, __FUNCTION__, __LINE__, fmt, ##args) 58 | #define LOG_FATAL(fmt, args...) Log::rootLog().fatal(__FILE__, __FUNCTION__, __LINE__, fmt, ##args) 59 | 60 | const char* errorMsg(int code); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/nocopyable.h: -------------------------------------------------------------------------------- 1 | #ifndef _NOCOPYABLE_H_ 2 | #define _NOCOPYABLE_H_ 3 | 4 | namespace tnet 5 | { 6 | 7 | class nocopyable 8 | { 9 | protected: 10 | nocopyable() {} 11 | ~nocopyable() {} 12 | private: 13 | nocopyable(const nocopyable&); 14 | nocopyable& operator=(const nocopyable&); 15 | }; 16 | 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/notifier.cpp: -------------------------------------------------------------------------------- 1 | #include "notifier.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "log.h" 7 | #include "ioloop.h" 8 | 9 | namespace tnet 10 | { 11 | 12 | Notifier::Notifier(const NotifierHandler_t& handler) 13 | : m_loop(0) 14 | , m_fd(0) 15 | , m_running(false) 16 | , m_handler(handler) 17 | { 18 | m_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 19 | if(m_fd < 0) 20 | { 21 | LOG_ERROR("eventfd error %s", errorMsg(errno)); 22 | } 23 | } 24 | 25 | Notifier::~Notifier() 26 | { 27 | LOG_INFO("destroyed %d", m_fd); 28 | 29 | if(m_fd > 0) 30 | { 31 | close(m_fd); 32 | } 33 | } 34 | 35 | void Notifier::start(IOLoop* loop) 36 | { 37 | assert(m_fd > 0); 38 | if(m_running) 39 | { 40 | LOG_WARN("event was started"); 41 | return; 42 | } 43 | 44 | m_running = true; 45 | m_loop = loop; 46 | 47 | m_loop->addHandler(m_fd, TNET_READ, std::bind(&Notifier::onEvent, shared_from_this(), _1, _2)); 48 | } 49 | 50 | void Notifier::stop() 51 | { 52 | assert(m_fd > 0); 53 | if(!m_running) 54 | { 55 | LOG_WARN("event was stopped"); 56 | return; 57 | } 58 | 59 | m_running = false; 60 | m_loop->removeHandler(m_fd); 61 | } 62 | 63 | void Notifier::notify() 64 | { 65 | eventfd_t value = 1; 66 | if(eventfd_write(m_fd, value) < 0) 67 | { 68 | LOG_ERROR("eventfd_write error"); 69 | } 70 | } 71 | 72 | void Notifier::onEvent(IOLoop* loop, int events) 73 | { 74 | NotifierPtr_t notifier = shared_from_this(); 75 | eventfd_t value; 76 | 77 | if(eventfd_read(m_fd, &value) < 0) 78 | { 79 | LOG_ERROR("eventfd read error"); 80 | return; 81 | } 82 | 83 | m_handler(notifier); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/notifier.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tnet.h" 4 | 5 | namespace tnet 6 | { 7 | class Notifier : public nocopyable 8 | , public std::enable_shared_from_this 9 | { 10 | public: 11 | Notifier(const NotifierHandler_t& handler); 12 | ~Notifier(); 13 | 14 | void start(IOLoop* loop); 15 | void stop(); 16 | 17 | void notify(); 18 | 19 | IOLoop* loop() { return m_loop; } 20 | 21 | private: 22 | void onEvent(IOLoop* loop, int events); 23 | 24 | private: 25 | IOLoop* m_loop; 26 | int m_fd; 27 | bool m_running; 28 | 29 | NotifierHandler_t m_handler; 30 | }; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/polarssl/base64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * RFC 1521 base64 encoding/decoding 3 | * 4 | * Copyright (C) 2006-2010, Brainspark B.V. 5 | * 6 | * This file is part of PolarSSL (http://www.polarssl.org) 7 | * Lead Maintainer: Paul Bakker 8 | * 9 | * All rights reserved. 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License along 22 | * with this program; if not, write to the Free Software Foundation, Inc., 23 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 24 | */ 25 | 26 | #define POLARSSL_BASE64_C 27 | #if defined(POLARSSL_BASE64_C) 28 | 29 | #include "base64.h" 30 | 31 | #ifdef _MSC_VER 32 | #include 33 | typedef UINT32 uint32_t; 34 | #else 35 | #include 36 | #endif 37 | 38 | static const unsigned char base64_enc_map[64] = 39 | { 40 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 41 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 42 | 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 43 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 44 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 45 | 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 46 | '8', '9', '+', '/' 47 | }; 48 | 49 | static const unsigned char base64_dec_map[128] = 50 | { 51 | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 52 | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 53 | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 54 | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 55 | 127, 127, 127, 62, 127, 127, 127, 63, 52, 53, 56 | 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 57 | 127, 64, 127, 127, 127, 0, 1, 2, 3, 4, 58 | 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 59 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 60 | 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, 61 | 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 62 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 63 | 49, 50, 51, 127, 127, 127, 127, 127 64 | }; 65 | 66 | /* 67 | * Encode a buffer into base64 format 68 | */ 69 | int base64_encode( unsigned char *dst, size_t *dlen, 70 | const unsigned char *src, size_t slen ) 71 | { 72 | size_t i, n; 73 | int C1, C2, C3; 74 | unsigned char *p; 75 | 76 | if( slen == 0 ) 77 | return( 0 ); 78 | 79 | n = (slen << 3) / 6; 80 | 81 | switch( (slen << 3) - (n * 6) ) 82 | { 83 | case 2: n += 3; break; 84 | case 4: n += 2; break; 85 | default: break; 86 | } 87 | 88 | if( *dlen < n + 1 ) 89 | { 90 | *dlen = n + 1; 91 | return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); 92 | } 93 | 94 | n = (slen / 3) * 3; 95 | 96 | for( i = 0, p = dst; i < n; i += 3 ) 97 | { 98 | C1 = *src++; 99 | C2 = *src++; 100 | C3 = *src++; 101 | 102 | *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; 103 | *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; 104 | *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F]; 105 | *p++ = base64_enc_map[C3 & 0x3F]; 106 | } 107 | 108 | if( i < slen ) 109 | { 110 | C1 = *src++; 111 | C2 = ((i + 1) < slen) ? *src++ : 0; 112 | 113 | *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; 114 | *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; 115 | 116 | if( (i + 1) < slen ) 117 | *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F]; 118 | else *p++ = '='; 119 | 120 | *p++ = '='; 121 | } 122 | 123 | *dlen = p - dst; 124 | *p = 0; 125 | 126 | return( 0 ); 127 | } 128 | 129 | /* 130 | * Decode a base64-formatted buffer 131 | */ 132 | int base64_decode( unsigned char *dst, size_t *dlen, 133 | const unsigned char *src, size_t slen ) 134 | { 135 | size_t i, n; 136 | uint32_t j, x; 137 | unsigned char *p; 138 | 139 | for( i = j = n = 0; i < slen; i++ ) 140 | { 141 | if( ( slen - i ) >= 2 && 142 | src[i] == '\r' && src[i + 1] == '\n' ) 143 | continue; 144 | 145 | if( src[i] == '\n' ) 146 | continue; 147 | 148 | if( src[i] == '=' && ++j > 2 ) 149 | return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); 150 | 151 | if( src[i] > 127 || base64_dec_map[src[i]] == 127 ) 152 | return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); 153 | 154 | if( base64_dec_map[src[i]] < 64 && j != 0 ) 155 | return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); 156 | 157 | n++; 158 | } 159 | 160 | if( n == 0 ) 161 | return( 0 ); 162 | 163 | n = ((n * 6) + 7) >> 3; 164 | 165 | if( *dlen < n ) 166 | { 167 | *dlen = n; 168 | return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); 169 | } 170 | 171 | for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ ) 172 | { 173 | if( *src == '\r' || *src == '\n' ) 174 | continue; 175 | 176 | j -= ( base64_dec_map[*src] == 64 ); 177 | x = (x << 6) | ( base64_dec_map[*src] & 0x3F ); 178 | 179 | if( ++n == 4 ) 180 | { 181 | n = 0; 182 | if( j > 0 ) *p++ = (unsigned char)( x >> 16 ); 183 | if( j > 1 ) *p++ = (unsigned char)( x >> 8 ); 184 | if( j > 2 ) *p++ = (unsigned char)( x ); 185 | } 186 | } 187 | 188 | *dlen = p - dst; 189 | 190 | return( 0 ); 191 | } 192 | 193 | #if defined(POLARSSL_SELF_TEST) 194 | 195 | #include 196 | #include 197 | 198 | static const unsigned char base64_test_dec[64] = 199 | { 200 | 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 201 | 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01, 202 | 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 203 | 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 204 | 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31, 205 | 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 206 | 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B, 207 | 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97 208 | }; 209 | 210 | static const unsigned char base64_test_enc[] = 211 | "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK" 212 | "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw=="; 213 | 214 | /* 215 | * Checkup routine 216 | */ 217 | int base64_self_test( int verbose ) 218 | { 219 | size_t len; 220 | unsigned char *src, buffer[128]; 221 | 222 | if( verbose != 0 ) 223 | printf( " Base64 encoding test: " ); 224 | 225 | len = sizeof( buffer ); 226 | src = (unsigned char *) base64_test_dec; 227 | 228 | if( base64_encode( buffer, &len, src, 64 ) != 0 || 229 | memcmp( base64_test_enc, buffer, 88 ) != 0 ) 230 | { 231 | if( verbose != 0 ) 232 | printf( "failed\n" ); 233 | 234 | return( 1 ); 235 | } 236 | 237 | if( verbose != 0 ) 238 | printf( "passed\n Base64 decoding test: " ); 239 | 240 | len = sizeof( buffer ); 241 | src = (unsigned char *) base64_test_enc; 242 | 243 | if( base64_decode( buffer, &len, src, 88 ) != 0 || 244 | memcmp( base64_test_dec, buffer, 64 ) != 0 ) 245 | { 246 | if( verbose != 0 ) 247 | printf( "failed\n" ); 248 | 249 | return( 1 ); 250 | } 251 | 252 | if( verbose != 0 ) 253 | printf( "passed\n\n" ); 254 | 255 | return( 0 ); 256 | } 257 | 258 | #endif 259 | 260 | #endif 261 | -------------------------------------------------------------------------------- /src/polarssl/base64.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file base64.h 3 | * 4 | * \brief RFC 1521 base64 encoding/decoding 5 | * 6 | * Copyright (C) 2006-2010, Brainspark B.V. 7 | * 8 | * This file is part of PolarSSL (http://www.polarssl.org) 9 | * Lead Maintainer: Paul Bakker 10 | * 11 | * All rights reserved. 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License along 24 | * with this program; if not, write to the Free Software Foundation, Inc., 25 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 26 | */ 27 | #ifndef POLARSSL_BASE64_H 28 | #define POLARSSL_BASE64_H 29 | 30 | #include 31 | 32 | #define POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL -0x002A /**< Output buffer too small. */ 33 | #define POLARSSL_ERR_BASE64_INVALID_CHARACTER -0x002C /**< Invalid character in input. */ 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | /** 40 | * \brief Encode a buffer into base64 format 41 | * 42 | * \param dst destination buffer 43 | * \param dlen size of the buffer 44 | * \param src source buffer 45 | * \param slen amount of data to be encoded 46 | * 47 | * \return 0 if successful, or POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL. 48 | * *dlen is always updated to reflect the amount 49 | * of data that has (or would have) been written. 50 | * 51 | * \note Call this function with *dlen = 0 to obtain the 52 | * required buffer size in *dlen 53 | */ 54 | int base64_encode( unsigned char *dst, size_t *dlen, 55 | const unsigned char *src, size_t slen ); 56 | 57 | /** 58 | * \brief Decode a base64-formatted buffer 59 | * 60 | * \param dst destination buffer 61 | * \param dlen size of the buffer 62 | * \param src source buffer 63 | * \param slen amount of data to be decoded 64 | * 65 | * \return 0 if successful, POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL, or 66 | * POLARSSL_ERR_BASE64_INVALID_CHARACTER if the input data is 67 | * not correct. *dlen is always updated to reflect the amount 68 | * of data that has (or would have) been written. 69 | * 70 | * \note Call this function with *dlen = 0 to obtain the 71 | * required buffer size in *dlen 72 | */ 73 | int base64_decode( unsigned char *dst, size_t *dlen, 74 | const unsigned char *src, size_t slen ); 75 | 76 | /** 77 | * \brief Checkup routine 78 | * 79 | * \return 0 if successful, or 1 if the test failed 80 | */ 81 | int base64_self_test( int verbose ); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif /* base64.h */ 88 | -------------------------------------------------------------------------------- /src/polarssl/md5.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file md5.h 3 | * 4 | * \brief MD5 message digest algorithm (hash function) 5 | * 6 | * Copyright (C) 2006-2010, Brainspark B.V. 7 | * 8 | * This file is part of PolarSSL (http://www.polarssl.org) 9 | * Lead Maintainer: Paul Bakker 10 | * 11 | * All rights reserved. 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License along 24 | * with this program; if not, write to the Free Software Foundation, Inc., 25 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 26 | */ 27 | #ifndef POLARSSL_MD5_H 28 | #define POLARSSL_MD5_H 29 | 30 | #include 31 | 32 | #ifdef _MSC_VER 33 | #include 34 | typedef UINT32 uint32_t; 35 | #else 36 | #include 37 | #endif 38 | 39 | #define POLARSSL_ERR_MD5_FILE_IO_ERROR -0x0074 /**< Read/write error in file. */ 40 | 41 | /** 42 | * \brief MD5 context structure 43 | */ 44 | typedef struct 45 | { 46 | uint32_t total[2]; /*!< number of bytes processed */ 47 | uint32_t state[4]; /*!< intermediate digest state */ 48 | unsigned char buffer[64]; /*!< data block being processed */ 49 | 50 | unsigned char ipad[64]; /*!< HMAC: inner padding */ 51 | unsigned char opad[64]; /*!< HMAC: outer padding */ 52 | } 53 | md5_context; 54 | 55 | #ifdef __cplusplus 56 | extern "C" { 57 | #endif 58 | 59 | /** 60 | * \brief MD5 context setup 61 | * 62 | * \param ctx context to be initialized 63 | */ 64 | void md5_starts( md5_context *ctx ); 65 | 66 | /** 67 | * \brief MD5 process buffer 68 | * 69 | * \param ctx MD5 context 70 | * \param input buffer holding the data 71 | * \param ilen length of the input data 72 | */ 73 | void md5_update( md5_context *ctx, const unsigned char *input, size_t ilen ); 74 | 75 | /** 76 | * \brief MD5 final digest 77 | * 78 | * \param ctx MD5 context 79 | * \param output MD5 checksum result 80 | */ 81 | void md5_finish( md5_context *ctx, unsigned char output[16] ); 82 | 83 | /** 84 | * \brief Output = MD5( input buffer ) 85 | * 86 | * \param input buffer holding the data 87 | * \param ilen length of the input data 88 | * \param output MD5 checksum result 89 | */ 90 | void md5( const unsigned char *input, size_t ilen, unsigned char output[16] ); 91 | 92 | /** 93 | * \brief Output = MD5( file contents ) 94 | * 95 | * \param path input file name 96 | * \param output MD5 checksum result 97 | * 98 | * \return 0 if successful, or POLARSSL_ERR_MD5_FILE_IO_ERROR 99 | */ 100 | int md5_file( const char *path, unsigned char output[16] ); 101 | 102 | /** 103 | * \brief MD5 HMAC context setup 104 | * 105 | * \param ctx HMAC context to be initialized 106 | * \param key HMAC secret key 107 | * \param keylen length of the HMAC key 108 | */ 109 | void md5_hmac_starts( md5_context *ctx, 110 | const unsigned char *key, size_t keylen ); 111 | 112 | /** 113 | * \brief MD5 HMAC process buffer 114 | * 115 | * \param ctx HMAC context 116 | * \param input buffer holding the data 117 | * \param ilen length of the input data 118 | */ 119 | void md5_hmac_update( md5_context *ctx, 120 | const unsigned char *input, size_t ilen ); 121 | 122 | /** 123 | * \brief MD5 HMAC final digest 124 | * 125 | * \param ctx HMAC context 126 | * \param output MD5 HMAC checksum result 127 | */ 128 | void md5_hmac_finish( md5_context *ctx, unsigned char output[16] ); 129 | 130 | /** 131 | * \brief MD5 HMAC context reset 132 | * 133 | * \param ctx HMAC context to be reset 134 | */ 135 | void md5_hmac_reset( md5_context *ctx ); 136 | 137 | /** 138 | * \brief Output = HMAC-MD5( hmac key, input buffer ) 139 | * 140 | * \param key HMAC secret key 141 | * \param keylen length of the HMAC key 142 | * \param input buffer holding the data 143 | * \param ilen length of the input data 144 | * \param output HMAC-MD5 result 145 | */ 146 | void md5_hmac( const unsigned char *key, size_t keylen, 147 | const unsigned char *input, size_t ilen, 148 | unsigned char output[16] ); 149 | 150 | /** 151 | * \brief Checkup routine 152 | * 153 | * \return 0 if successful, or 1 if the test failed 154 | */ 155 | int md5_self_test( int verbose ); 156 | 157 | /* Internal use */ 158 | void md5_process( md5_context *ctx, const unsigned char data[64] ); 159 | 160 | #ifdef __cplusplus 161 | } 162 | #endif 163 | 164 | #endif /* md5.h */ 165 | -------------------------------------------------------------------------------- /src/polarssl/polarssl_1.2.7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddontang/libtnet/fe530db174a32800f3f218214a4d223c46f64e4d/src/polarssl/polarssl_1.2.7 -------------------------------------------------------------------------------- /src/polarssl/sha1.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file sha1.h 3 | * 4 | * \brief SHA-1 cryptographic hash function 5 | * 6 | * Copyright (C) 2006-2010, Brainspark B.V. 7 | * 8 | * This file is part of PolarSSL (http://www.polarssl.org) 9 | * Lead Maintainer: Paul Bakker 10 | * 11 | * All rights reserved. 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License along 24 | * with this program; if not, write to the Free Software Foundation, Inc., 25 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 26 | */ 27 | #ifndef POLARSSL_SHA1_H 28 | #define POLARSSL_SHA1_H 29 | 30 | #include 31 | 32 | #ifdef _MSC_VER 33 | #include 34 | typedef UINT32 uint32_t; 35 | #else 36 | #include 37 | #endif 38 | 39 | #define POLARSSL_ERR_SHA1_FILE_IO_ERROR -0x0076 /**< Read/write error in file. */ 40 | 41 | /** 42 | * \brief SHA-1 context structure 43 | */ 44 | typedef struct 45 | { 46 | uint32_t total[2]; /*!< number of bytes processed */ 47 | uint32_t state[5]; /*!< intermediate digest state */ 48 | unsigned char buffer[64]; /*!< data block being processed */ 49 | 50 | unsigned char ipad[64]; /*!< HMAC: inner padding */ 51 | unsigned char opad[64]; /*!< HMAC: outer padding */ 52 | } 53 | sha1_context; 54 | 55 | #ifdef __cplusplus 56 | extern "C" { 57 | #endif 58 | 59 | /** 60 | * \brief SHA-1 context setup 61 | * 62 | * \param ctx context to be initialized 63 | */ 64 | void sha1_starts( sha1_context *ctx ); 65 | 66 | /** 67 | * \brief SHA-1 process buffer 68 | * 69 | * \param ctx SHA-1 context 70 | * \param input buffer holding the data 71 | * \param ilen length of the input data 72 | */ 73 | void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); 74 | 75 | /** 76 | * \brief SHA-1 final digest 77 | * 78 | * \param ctx SHA-1 context 79 | * \param output SHA-1 checksum result 80 | */ 81 | void sha1_finish( sha1_context *ctx, unsigned char output[20] ); 82 | 83 | /** 84 | * \brief Output = SHA-1( input buffer ) 85 | * 86 | * \param input buffer holding the data 87 | * \param ilen length of the input data 88 | * \param output SHA-1 checksum result 89 | */ 90 | void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ); 91 | 92 | /** 93 | * \brief Output = SHA-1( file contents ) 94 | * 95 | * \param path input file name 96 | * \param output SHA-1 checksum result 97 | * 98 | * \return 0 if successful, or POLARSSL_ERR_SHA1_FILE_IO_ERROR 99 | */ 100 | int sha1_file( const char *path, unsigned char output[20] ); 101 | 102 | /** 103 | * \brief SHA-1 HMAC context setup 104 | * 105 | * \param ctx HMAC context to be initialized 106 | * \param key HMAC secret key 107 | * \param keylen length of the HMAC key 108 | */ 109 | void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ); 110 | 111 | /** 112 | * \brief SHA-1 HMAC process buffer 113 | * 114 | * \param ctx HMAC context 115 | * \param input buffer holding the data 116 | * \param ilen length of the input data 117 | */ 118 | void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); 119 | 120 | /** 121 | * \brief SHA-1 HMAC final digest 122 | * 123 | * \param ctx HMAC context 124 | * \param output SHA-1 HMAC checksum result 125 | */ 126 | void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ); 127 | 128 | /** 129 | * \brief SHA-1 HMAC context reset 130 | * 131 | * \param ctx HMAC context to be reset 132 | */ 133 | void sha1_hmac_reset( sha1_context *ctx ); 134 | 135 | /** 136 | * \brief Output = HMAC-SHA-1( hmac key, input buffer ) 137 | * 138 | * \param key HMAC secret key 139 | * \param keylen length of the HMAC key 140 | * \param input buffer holding the data 141 | * \param ilen length of the input data 142 | * \param output HMAC-SHA-1 result 143 | */ 144 | void sha1_hmac( const unsigned char *key, size_t keylen, 145 | const unsigned char *input, size_t ilen, 146 | unsigned char output[20] ); 147 | 148 | /** 149 | * \brief Checkup routine 150 | * 151 | * \return 0 if successful, or 1 if the test failed 152 | */ 153 | int sha1_self_test( int verbose ); 154 | 155 | /* Internal use */ 156 | void sha1_process( sha1_context *ctx, const unsigned char data[64] ); 157 | 158 | #ifdef __cplusplus 159 | } 160 | #endif 161 | 162 | #endif /* sha1.h */ 163 | -------------------------------------------------------------------------------- /src/poller.cpp: -------------------------------------------------------------------------------- 1 | #include "poller.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "log.h" 11 | #include "ioevent.h" 12 | 13 | using namespace std; 14 | 15 | namespace tnet 16 | { 17 | const int DefaultEventSize = 1024; 18 | const int MaxEventSize = 10240; 19 | 20 | Poller::Poller(IOLoop* loop) 21 | : m_loop(loop) 22 | { 23 | m_fd = epoll_create1(EPOLL_CLOEXEC); 24 | if(m_fd < 0) 25 | { 26 | LOG_ERROR("epoll create error %s", errorMsg(errno)); 27 | } 28 | 29 | m_eventSize = DefaultEventSize; 30 | m_events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * m_eventSize); 31 | } 32 | 33 | Poller::~Poller() 34 | { 35 | if(m_fd > 0) 36 | { 37 | close(m_fd); 38 | } 39 | 40 | free(m_events); 41 | } 42 | 43 | int Poller::add(int fd, int events) 44 | { 45 | assert(m_fd > 0); 46 | struct epoll_event event; 47 | 48 | event.data.u64 = 0; 49 | 50 | event.data.fd = fd; 51 | event.events = (events & TNET_READ ? EPOLLIN : 0) 52 | | (events & TNET_WRITE ? EPOLLOUT : 0); 53 | 54 | int ret = epoll_ctl(m_fd, EPOLL_CTL_ADD, fd, &event); 55 | if(ret < 0) 56 | { 57 | LOG_ERROR("epoll_ctl add error %s", errorMsg(errno)); 58 | return -1; 59 | } 60 | 61 | return 0; 62 | } 63 | 64 | int Poller::update(int fd, int events) 65 | { 66 | assert(m_fd > 0); 67 | 68 | struct epoll_event event; 69 | 70 | event.data.u64 = 0; 71 | event.data.fd = fd; 72 | 73 | event.events = (events & TNET_READ ? EPOLLIN : 0) 74 | | (events & TNET_WRITE ? EPOLLOUT : 0); 75 | 76 | int ret = epoll_ctl(m_fd, EPOLL_CTL_MOD, fd, &event); 77 | if(ret < 0) 78 | { 79 | LOG_ERROR("epoll_ctl update error %s", errorMsg(errno)); 80 | return -1; 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | int Poller::remove(int fd) 87 | { 88 | assert(m_fd > 0); 89 | 90 | int ret = epoll_ctl(m_fd ,EPOLL_CTL_DEL, fd, 0); 91 | if(ret < 0) 92 | { 93 | LOG_ERROR("epoll_ctl remove error %s", errorMsg(errno)); 94 | return -1; 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | int Poller::poll(int timeout, const std::vector& events) 101 | { 102 | memset(m_events, 0, sizeof(struct epoll_event) * m_eventSize); 103 | 104 | int ret = epoll_wait(m_fd, m_events, m_eventSize, timeout); 105 | if(ret < 0) 106 | { 107 | LOG_ERROR("epoll_wait error %s", errorMsg(errno)); 108 | return -1; 109 | } 110 | 111 | for(int i = 0; i < ret; ++i) 112 | { 113 | struct epoll_event* ev = m_events + i; 114 | int fd = ev->data.fd; 115 | int got = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? TNET_WRITE : 0) 116 | | (ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLRDHUP) ? TNET_READ : 0); 117 | 118 | IOEvent* io = fd < events.size() ? events[fd] : 0; 119 | 120 | if(!io) 121 | { 122 | //may occur event cache problem, see man 7 epoll 123 | continue; 124 | } 125 | 126 | //int want = io ? io->events : TNET_NONE; 127 | int want = io->events; 128 | 129 | if(got & ~want) 130 | { 131 | ev->events = (want & TNET_READ ? EPOLLIN : 0) 132 | | (want & TNET_WRITE ? EPOLLOUT : 0); 133 | if(epoll_ctl(m_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev) < 0) 134 | { 135 | LOG_ERROR("ctl error %s got:%d, want:%d, fd:%d", errorMsg(errno), got, want, fd); 136 | continue; 137 | } 138 | } 139 | 140 | (io->handler)(m_loop, got); 141 | } 142 | 143 | if(ret == m_eventSize && m_eventSize != MaxEventSize) 144 | { 145 | m_eventSize *= 2; 146 | if(m_eventSize > MaxEventSize) 147 | { 148 | m_eventSize = MaxEventSize; 149 | } 150 | 151 | m_events = (struct epoll_event*)realloc(m_events, sizeof(struct epoll_event) * m_eventSize); 152 | } 153 | 154 | return ret; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/poller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "tnet.h" 6 | 7 | extern "C" 8 | { 9 | struct epoll_event; 10 | } 11 | 12 | namespace tnet 13 | { 14 | class IOLoop; 15 | class IOEvent; 16 | 17 | //A wrapper for epoll 18 | class Poller 19 | { 20 | public: 21 | Poller(IOLoop* loop); 22 | ~Poller(); 23 | 24 | //timeout is milliseconds 25 | int poll(int timeout, const std::vector& events); 26 | 27 | int add(int fd, int events); 28 | int update(int fd, int events); 29 | int remove(int fd); 30 | 31 | private: 32 | IOLoop* m_loop; 33 | int m_fd; 34 | 35 | struct epoll_event* m_events; 36 | size_t m_eventSize; 37 | }; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/process.cpp: -------------------------------------------------------------------------------- 1 | #include "process.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "signaler.h" 10 | #include "log.h" 11 | 12 | using namespace std; 13 | 14 | namespace tnet 15 | { 16 | Process::Process() 17 | : m_running(false) 18 | { 19 | m_main = getpid(); 20 | vector signums{SIGTERM}; 21 | m_fd = Signaler::createSignalFd(signums); 22 | } 23 | 24 | Process::~Process() 25 | { 26 | 27 | } 28 | 29 | pid_t Process::create() 30 | { 31 | pid_t pid = fork(); 32 | if(pid < 0) 33 | { 34 | LOG_ERROR("fork error %s", errorMsg(errno)); 35 | return 0; 36 | } 37 | else if(pid == 0) 38 | { 39 | //child 40 | m_children.clear(); 41 | close(m_fd); 42 | return getpid(); 43 | } 44 | else 45 | { 46 | //parent 47 | m_children.insert(pid); 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | void Process::wait(size_t num, const ProcessCallback_t& callback) 54 | { 55 | m_running = true; 56 | for(size_t i = 0; i < num; ++i) 57 | { 58 | pid_t pid = create(); 59 | if(pid != 0) 60 | { 61 | callback(); 62 | return; 63 | } 64 | } 65 | 66 | while(!m_children.empty()) 67 | { 68 | int status = 0; 69 | pid_t pid; 70 | if((pid = waitpid(-1, &status, WNOHANG)) > 0) 71 | { 72 | m_children.erase(pid); 73 | 74 | if(!m_running) 75 | { 76 | continue; 77 | } 78 | 79 | LOG_INFO("child was dead, restart it"); 80 | 81 | if(create() != 0) 82 | { 83 | callback(); 84 | return; 85 | } 86 | } 87 | else 88 | { 89 | checkStop(); 90 | sleep(1); 91 | continue; 92 | } 93 | } 94 | 95 | return; 96 | } 97 | 98 | void Process::stop() 99 | { 100 | LOG_INFO("stop child process"); 101 | m_running = false; 102 | for_each_all(m_children, std::bind(::kill, _1, SIGTERM)); 103 | } 104 | 105 | void Process::checkStop() 106 | { 107 | struct signalfd_siginfo fdsi; 108 | ssize_t s = read(m_fd, &fdsi, sizeof(fdsi)); 109 | 110 | if(s != sizeof(fdsi)) 111 | { 112 | //no signal, 113 | return; 114 | } 115 | 116 | int signum = fdsi.ssi_signo; 117 | switch(signum) 118 | { 119 | case SIGTERM: 120 | stop(); 121 | break; 122 | default: 123 | LOG_INFO("signum %d", signum); 124 | break; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "tnet.h" 7 | 8 | namespace tnet 9 | { 10 | 11 | class Process 12 | { 13 | public: 14 | Process(); 15 | ~Process(); 16 | 17 | void wait(size_t num, const ProcessCallback_t& callback); 18 | 19 | void stop(); 20 | 21 | bool isMainProc() { return m_main == getpid(); } 22 | 23 | bool hasChild() { return m_children.size() > 0; } 24 | 25 | private: 26 | pid_t create(); 27 | void checkStop(); 28 | 29 | private: 30 | pid_t m_main; 31 | bool m_running; 32 | std::set m_children; 33 | 34 | int m_fd; 35 | }; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/redis/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS}") 2 | 3 | set (SRCS 4 | hiredis.c 5 | redisclient.cpp 6 | redisconnection.cpp 7 | redistrans.cpp 8 | sds.c 9 | ) 10 | 11 | include_directories(../) 12 | 13 | set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) 14 | 15 | add_library(tnet_redis ${SRCS}) 16 | target_link_libraries(tnet_redis tnet) 17 | 18 | install(TARGETS tnet_redis DESTINATION lib) 19 | 20 | set (HEADERS 21 | hiredis.h 22 | redisclient.h 23 | redistrans.h 24 | tnet_redis.h 25 | ) 26 | 27 | install(FILES ${HEADERS} DESTINATION include/tnet/redis) 28 | 29 | -------------------------------------------------------------------------------- /src/redis/redisclient.cpp: -------------------------------------------------------------------------------- 1 | #include "redisclient.h" 2 | 3 | #include 4 | 5 | #include "ioloop.h" 6 | #include "connection.h" 7 | #include "log.h" 8 | #include "address.h" 9 | #include "sockutil.h" 10 | #include "redisconnection.h" 11 | #include "connector.inl" 12 | #include "redistrans.h" 13 | 14 | extern "C" 15 | { 16 | #include "hiredis.h" 17 | } 18 | 19 | using namespace std; 20 | 21 | namespace tnet 22 | { 23 | RedisClient::RedisClient(IOLoop* loop, const Address& addr, const string& password, size_t maxClients) 24 | : m_loop(loop) 25 | , m_address(addr) 26 | , m_password(password) 27 | , m_maxClients(maxClients) 28 | { 29 | m_conns.reserve(maxClients); 30 | } 31 | 32 | RedisClient::~RedisClient() 33 | { 34 | for(size_t i = 0; i < m_conns.size(); ++i) 35 | { 36 | RedisConnectionPtr_t conn = m_conns[i].lock(); 37 | if(conn) 38 | { 39 | conn->shutDown(); 40 | } 41 | } 42 | } 43 | 44 | void RedisClient::pushConn(const RedisConnectionPtr_t& conn) 45 | { 46 | if(!conn) 47 | { 48 | return; 49 | } 50 | 51 | conn->clearCallback(); 52 | 53 | if(m_conns.size() > m_maxClients) 54 | { 55 | conn->shutDown(); 56 | return; 57 | } 58 | 59 | m_conns.push_back(conn); 60 | } 61 | 62 | RedisConnectionPtr_t RedisClient::popConn() 63 | { 64 | if(m_conns.empty()) 65 | { 66 | return RedisConnectionPtr_t(); 67 | } 68 | 69 | while(!m_conns.empty()) 70 | { 71 | RedisConnectionPtr_t conn = m_conns.back().lock(); 72 | m_conns.pop_back(); 73 | if(conn) 74 | { 75 | return conn; 76 | } 77 | } 78 | return RedisConnectionPtr_t(); 79 | } 80 | 81 | void RedisClient::onReply(const RedisConnectionPtr_t& conn, const RedisReply& reply, const ReplyCallback_t& callback) 82 | { 83 | RedisConnectionPtr_t c = conn->shared_from_this(); 84 | auto cb = std::move(callback); 85 | 86 | pushConn(conn); 87 | 88 | cb(reply); 89 | } 90 | 91 | void RedisClient::newTrans(const NewTransCallback_t& callback) 92 | { 93 | RedisConnectionPtr_t conn = popConn(); 94 | if(conn) 95 | { 96 | RedisTransPtr_t trans = std::make_shared(shared_from_this(), conn); 97 | 98 | conn->setCallback(std::bind(&RedisTrans::onReply, trans, _1, _2)); 99 | 100 | callback(trans, 0); 101 | } 102 | else 103 | { 104 | conn = std::make_shared(); 105 | 106 | RedisTransPtr_t trans = std::make_shared(shared_from_this(), conn); 107 | 108 | conn->connect(m_loop, m_address, m_password, std::bind(&RedisTrans::onConnect, trans, _1, _2, callback)); 109 | } 110 | } 111 | 112 | void RedisClient::exec(initializer_list cmd, const ReplyCallback_t& callback) 113 | { 114 | RedisConnectionPtr_t conn = popConn(); 115 | if(conn) 116 | { 117 | conn->setCallback(std::bind(&RedisClient::onReply, 118 | shared_from_this(), _1, _2, callback)); 119 | 120 | conn->exec(cmd); 121 | } 122 | else 123 | { 124 | conn = std::make_shared(); 125 | conn->setCallback(std::bind(&RedisClient::onReply, 126 | shared_from_this(), _1, _2, callback)); 127 | 128 | conn->connect(m_loop, m_address, m_password, std::bind(&RedisClient::onConnect, shared_from_this(), _1, _2, vector(cmd))); 129 | } 130 | } 131 | 132 | void RedisClient::onConnect(const RedisConnectionPtr_t& conn, int status, const vector& cmd) 133 | { 134 | if(status != 0) 135 | { 136 | LOG_ERROR("redis client connect fail %d", status); 137 | return; 138 | } 139 | 140 | conn->exec(cmd); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/redis/redisclient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "address.h" 9 | #include "tnet_redis.h" 10 | 11 | namespace tnet 12 | { 13 | class RedisClient : public nocopyable 14 | , public std::enable_shared_from_this 15 | { 16 | public: 17 | friend class RedisTrans; 18 | 19 | RedisClient(IOLoop* loop, const Address& addr, const std::string& password = std::string(), size_t maxClients = 10); 20 | ~RedisClient(); 21 | 22 | void exec(std::initializer_list cmd, const ReplyCallback_t& callback); 23 | void newTrans(const NewTransCallback_t& callback); 24 | 25 | private: 26 | void request(const std::string& data, const ReplyCallback_t& callback); 27 | 28 | RedisConnectionPtr_t popConn(); 29 | void pushConn(const RedisConnectionPtr_t& conn); 30 | 31 | void onReply(const RedisConnectionPtr_t& conn, const RedisReply& reply, const ReplyCallback_t& callback); 32 | 33 | void onConnect(const RedisConnectionPtr_t& conn, int status, const std::vector& cmd); 34 | 35 | private: 36 | IOLoop* m_loop; 37 | Address m_address; 38 | std::string m_password; 39 | size_t m_maxClients; 40 | 41 | std::vector m_conns; 42 | 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /src/redis/redisconnection.cpp: -------------------------------------------------------------------------------- 1 | #include "redisconnection.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "connection.h" 8 | #include "log.h" 9 | #include "sockutil.h" 10 | #include "address.h" 11 | #include "connector.inl" 12 | 13 | using namespace std; 14 | 15 | namespace tnet 16 | { 17 | static void dummyCallback(const RedisConnectionPtr_t&, const RedisReply&) 18 | { 19 | 20 | } 21 | 22 | RedisConnection::RedisConnection() 23 | : Connector() 24 | { 25 | m_callback = std::bind(&dummyCallback, _1, _2); 26 | 27 | m_context = redisContextInit(); 28 | } 29 | 30 | RedisConnection::~RedisConnection() 31 | { 32 | redisFree(m_context); 33 | LOG_INFO("destroyed"); 34 | } 35 | 36 | void RedisConnection::handleRead(const char* buffer, size_t count) 37 | { 38 | ReplyCallback_t callback = bool(m_authCallback) ? m_authCallback : m_callback; 39 | 40 | struct redisReader* reader = m_context->reader; 41 | if(redisReaderFeed(reader, buffer, count) != REDIS_OK) 42 | { 43 | LOG_ERROR("redis read error %d %s", reader->err, reader->errstr); 44 | 45 | callback(shared_from_this(), RedisReply(reader->err)); 46 | 47 | return; 48 | } 49 | 50 | do 51 | { 52 | struct redisReply* reply = NULL; 53 | if(redisGetReplyFromReader(m_context, (void**)&reply) == REDIS_ERR) 54 | { 55 | LOG_ERROR("redis get reply error %d %s", reader->err, reader->errstr); 56 | callback(shared_from_this(), RedisReply(reader->err)); 57 | return; 58 | } 59 | 60 | if(reply != NULL) 61 | { 62 | callback(shared_from_this(), RedisReply(0, reply)); 63 | freeReplyObject(reply); 64 | } 65 | else 66 | { 67 | return; 68 | } 69 | }while(true); 70 | 71 | return; 72 | } 73 | 74 | void RedisConnection::clearCallback() 75 | { 76 | m_callback = std::bind(&dummyCallback, _1, _2); 77 | } 78 | 79 | string buildRequest(const vector& args) 80 | { 81 | string str; 82 | str.reserve(1024); 83 | 84 | char buf[64]; 85 | 86 | int n = snprintf(buf, sizeof(buf), "*%d\r\n", int(args.size())); 87 | 88 | str.append(buf, n); 89 | 90 | for(auto i = args.begin(); i != args.end(); ++i) 91 | { 92 | n = snprintf(buf, sizeof(buf), "$%d\r\n", int((*i).size())); 93 | str.append(buf); 94 | str.append(*i); 95 | str.append("\r\n"); 96 | } 97 | 98 | return str; 99 | } 100 | 101 | void RedisConnection::exec(initializer_list cmd) 102 | { 103 | return exec(vector(cmd)); 104 | } 105 | 106 | void RedisConnection::exec(const vector& cmd) 107 | { 108 | send(buildRequest(cmd)); 109 | } 110 | 111 | void RedisConnection::connect(IOLoop* loop, const Address& addr, const string& password, const ConnectCallback_t& callback) 112 | { 113 | Base_t::connect(loop, addr, std::bind(&RedisConnection::onConnect, shared_from_this(), _1, _2, password, callback)); 114 | } 115 | 116 | void RedisConnection::onConnect(const RedisConnectionPtr_t& conn, bool connected, const string& password, const ConnectCallback_t& callback) 117 | { 118 | if(!connected) 119 | { 120 | LOG_ERROR("redis connect error"); 121 | callback(shared_from_this(), -1); 122 | return; 123 | } 124 | 125 | if(!password.empty()) 126 | { 127 | string pass = std::move(password); 128 | ConnectCallback_t cb = std::move(callback); 129 | 130 | m_authCallback = std::bind(&RedisConnection::onAuth, shared_from_this(), _1, _2, cb); 131 | 132 | static const string AuthCmd = "AUTH"; 133 | string data = buildRequest({AuthCmd, pass}); 134 | 135 | conn->send(data); 136 | } 137 | else 138 | { 139 | callback(shared_from_this(), 0); 140 | } 141 | } 142 | 143 | void RedisConnection::onAuth(const RedisConnectionPtr_t& conn, const RedisReply& r, const ConnectCallback_t& callback) 144 | { 145 | ConnectCallback_t cb = std::move(callback); 146 | 147 | ReplyCallback_t t; 148 | m_authCallback.swap(t); 149 | 150 | struct redisReply* reply = r.reply; 151 | 152 | if(r.err != 0 || reply->type != REDIS_REPLY_STATUS && strcasecmp(reply->str, "OK") != 0) 153 | { 154 | cb(shared_from_this(), -2); 155 | conn->shutDown(); 156 | } 157 | else 158 | { 159 | cb(shared_from_this(), 0); 160 | } 161 | 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/redis/redisconnection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "tnet_redis.h" 6 | #include "connector.h" 7 | 8 | namespace tnet 9 | { 10 | class Address; 11 | class RedisConnection : public Connector 12 | { 13 | public: 14 | typedef std::function ReplyCallback_t; 15 | 16 | typedef Connector Base_t; 17 | friend class Connector; 18 | 19 | RedisConnection(); 20 | ~RedisConnection(); 21 | 22 | void clearCallback(); 23 | void setCallback(const ReplyCallback_t& callback) { m_callback = callback; } 24 | 25 | void exec(std::initializer_list cmd); 26 | void exec(const std::vector& cmd); 27 | 28 | //ConnectCallback status: 0 for ok, -1 for not connected, -2 for auth fail 29 | typedef std::function ConnectCallback_t; 30 | void connect(IOLoop* loop, const Address& addr, const std::string& password, const ConnectCallback_t& callback); 31 | 32 | private: 33 | void handleRead(const char*, size_t); 34 | 35 | void onConnect(const RedisConnectionPtr_t& conn, bool connected, const std::string& password, const ConnectCallback_t& callback); 36 | void onAuth(const RedisConnectionPtr_t& conn, const RedisReply& reply, const ConnectCallback_t& callback); 37 | 38 | private: 39 | ReplyCallback_t m_callback; 40 | 41 | ReplyCallback_t m_authCallback; 42 | 43 | struct redisContext* m_context; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/redis/redistrans.cpp: -------------------------------------------------------------------------------- 1 | #include "redistrans.h" 2 | 3 | #include 4 | 5 | #include "redisconnection.h" 6 | #include "log.h" 7 | #include "redisclient.h" 8 | 9 | using namespace std; 10 | 11 | namespace tnet 12 | { 13 | static void dummyCallback(const RedisReply&) 14 | { 15 | 16 | } 17 | 18 | RedisTrans::RedisTrans(const RedisClientPtr_t& client, const RedisConnectionPtr_t& conn) 19 | : m_client(client) 20 | , m_conn(conn) 21 | , m_transNum(0) 22 | { 23 | m_callback = std::bind(&dummyCallback, _1); 24 | } 25 | 26 | RedisTrans::~RedisTrans() 27 | { 28 | LOG_INFO("destroyed"); 29 | 30 | RedisClientPtr_t client = m_client.lock(); 31 | if(client) 32 | { 33 | client->pushConn(m_conn.lock()); 34 | } 35 | } 36 | 37 | void RedisTrans::onReply(const RedisConnectionPtr_t& conn, const RedisReply& reply) 38 | { 39 | --m_transNum; 40 | assert(m_transNum >= 0); 41 | 42 | m_callback(reply); 43 | } 44 | 45 | void RedisTrans::onConnect(const RedisConnectionPtr_t& conn, int status, const NewTransCallback_t& callback) 46 | { 47 | if(status != 0) 48 | { 49 | LOG_ERROR("redis trans connect error"); 50 | callback(shared_from_this(), status); 51 | return; 52 | } 53 | 54 | NewTransCallback_t cb = std::move(callback); 55 | conn->setCallback(std::bind(&RedisTrans::onReply, shared_from_this(), _1, _2)); 56 | cb(shared_from_this(), 0); 57 | } 58 | 59 | void RedisTrans::request(initializer_list cmd) 60 | { 61 | RedisConnectionPtr_t conn = m_conn.lock(); 62 | if(!conn) 63 | { 64 | LOG_ERROR("connection was closed"); 65 | return; 66 | } 67 | 68 | ++m_transNum; 69 | 70 | conn->exec(cmd); 71 | } 72 | 73 | void RedisTrans::begin() 74 | { 75 | if(m_transNum > 0) 76 | { 77 | LOG_ERROR("already in trans"); 78 | return; 79 | } 80 | 81 | m_inTrans = true; 82 | static const string MultiCmd = "MULTI"; 83 | request({MultiCmd}); 84 | } 85 | 86 | void RedisTrans::exec(initializer_list cmd) 87 | { 88 | if(m_transNum == 0) 89 | { 90 | begin(); 91 | } 92 | 93 | request(cmd); 94 | } 95 | 96 | void RedisTrans::commit(const ReplyCallback_t& callback) 97 | { 98 | if(m_transNum == 0) 99 | { 100 | LOG_ERROR("not in trans, can't commit"); 101 | return; 102 | } 103 | 104 | static const string ExecCmd = "EXEC"; 105 | 106 | m_callback = std::bind(&RedisTrans::onCommit, shared_from_this(), _1, callback); 107 | 108 | request({ExecCmd}); 109 | } 110 | 111 | void RedisTrans::cancel(const ReplyCallback_t& callback) 112 | { 113 | if(m_transNum == 0) 114 | { 115 | LOG_ERROR("not in trans, can't commit"); 116 | return; 117 | } 118 | 119 | static const string CancelCmd = "DISCARD"; 120 | 121 | m_callback = std::bind(&RedisTrans::onCancel, shared_from_this(), _1, callback); 122 | 123 | request({CancelCmd}); 124 | } 125 | 126 | void RedisTrans::onCommit(const RedisReply& reply, const ReplyCallback_t& callback) 127 | { 128 | if(m_transNum != 0) 129 | { 130 | return; 131 | } 132 | 133 | ReplyCallback_t cb = std::move(callback); 134 | 135 | m_callback = std::bind(&dummyCallback, _1); 136 | 137 | cb(reply); 138 | } 139 | 140 | void RedisTrans::onCancel(const RedisReply& reply, const ReplyCallback_t& callback) 141 | { 142 | if(m_transNum != 0) 143 | { 144 | return; 145 | } 146 | 147 | ReplyCallback_t cb = std::move(callback); 148 | 149 | m_callback = std::bind(&dummyCallback, _1); 150 | 151 | cb(reply); 152 | } 153 | 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/redis/redistrans.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "address.h" 6 | 7 | #include "tnet_redis.h" 8 | 9 | namespace tnet 10 | { 11 | 12 | class RedisTrans : public nocopyable 13 | , public std::enable_shared_from_this 14 | { 15 | public: 16 | friend class RedisClient; 17 | 18 | RedisTrans(const RedisClientPtr_t& client, const RedisConnectionPtr_t& conn); 19 | ~RedisTrans(); 20 | 21 | void begin(); 22 | 23 | void exec(std::initializer_list cmd); 24 | 25 | void commit(const ReplyCallback_t& callback); 26 | void cancel(const ReplyCallback_t& callback); 27 | 28 | private: 29 | void onReply(const RedisConnectionPtr_t& conn, const RedisReply& reply); 30 | void onConnect(const RedisConnectionPtr_t& conn, int status, const NewTransCallback_t& callback); 31 | 32 | void onCommit(const RedisReply& reply, const ReplyCallback_t& callback); 33 | void onCancel(const RedisReply& reply, const ReplyCallback_t& callback); 34 | 35 | void request(std::initializer_list cmd); 36 | 37 | private: 38 | WeakRedisClientPtr_t m_client; 39 | 40 | WeakRedisConnectionPtr_t m_conn; 41 | 42 | ReplyCallback_t m_callback; 43 | 44 | int m_transNum; 45 | 46 | bool m_inTrans; 47 | }; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/redis/sds.h: -------------------------------------------------------------------------------- 1 | /* SDSLib, A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 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, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __SDS_H 32 | #define __SDS_H 33 | 34 | #include 35 | #include 36 | 37 | typedef char *sds; 38 | 39 | struct sdshdr { 40 | int len; 41 | int free; 42 | char buf[]; 43 | }; 44 | 45 | static inline size_t sdslen(const sds s) { 46 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 47 | return sh->len; 48 | } 49 | 50 | static inline size_t sdsavail(const sds s) { 51 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 52 | return sh->free; 53 | } 54 | 55 | sds sdsnewlen(const void *init, size_t initlen); 56 | sds sdsnew(const char *init); 57 | sds sdsempty(void); 58 | size_t sdslen(const sds s); 59 | sds sdsdup(const sds s); 60 | void sdsfree(sds s); 61 | size_t sdsavail(sds s); 62 | sds sdsgrowzero(sds s, size_t len); 63 | sds sdscatlen(sds s, const void *t, size_t len); 64 | sds sdscat(sds s, const char *t); 65 | sds sdscpylen(sds s, char *t, size_t len); 66 | sds sdscpy(sds s, char *t); 67 | 68 | sds sdscatvprintf(sds s, const char *fmt, va_list ap); 69 | #ifdef __GNUC__ 70 | sds sdscatprintf(sds s, const char *fmt, ...) 71 | __attribute__((format(printf, 2, 3))); 72 | #else 73 | sds sdscatprintf(sds s, const char *fmt, ...); 74 | #endif 75 | 76 | sds sdstrim(sds s, const char *cset); 77 | sds sdsrange(sds s, int start, int end); 78 | void sdsupdatelen(sds s); 79 | int sdscmp(sds s1, sds s2); 80 | sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count); 81 | void sdsfreesplitres(sds *tokens, int count); 82 | void sdstolower(sds s); 83 | void sdstoupper(sds s); 84 | sds sdsfromlonglong(long long value); 85 | sds sdscatrepr(sds s, char *p, size_t len); 86 | sds *sdssplitargs(char *line, int *argc); 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/redis/tnet_redis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "tnet.h" 7 | 8 | extern "C" 9 | { 10 | #include "hiredis.h" 11 | } 12 | 13 | namespace tnet 14 | { 15 | class RedisClient; 16 | class RedisReply; 17 | class RedisConnection; 18 | class RedisTrans; 19 | 20 | typedef std::shared_ptr RedisClientPtr_t; 21 | typedef std::weak_ptr WeakRedisClientPtr_t; 22 | typedef std::shared_ptr RedisConnectionPtr_t; 23 | typedef std::weak_ptr WeakRedisConnectionPtr_t; 24 | typedef std::shared_ptr RedisTransPtr_t; 25 | 26 | class RedisReply 27 | { 28 | public: 29 | RedisReply() 30 | { 31 | err = 0; 32 | reply = NULL; 33 | } 34 | 35 | RedisReply(int e, struct redisReply* r = 0) 36 | { 37 | err = e; 38 | reply = r; 39 | } 40 | 41 | int err; //0 for ok 42 | struct redisReply* reply; 43 | }; 44 | 45 | typedef std::function ReplyCallback_t; 46 | 47 | typedef std::function NewTransCallback_t; 48 | } 49 | -------------------------------------------------------------------------------- /src/signaler.cpp: -------------------------------------------------------------------------------- 1 | #include "signaler.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "log.h" 12 | #include "ioloop.h" 13 | 14 | using namespace std; 15 | 16 | namespace tnet 17 | { 18 | Signaler::Signaler(int signum, const SignalHandler_t& handler) 19 | : m_loop(0) 20 | , m_fd(-1) 21 | , m_running(false) 22 | , m_signums{signum} 23 | , m_handler(handler) 24 | { 25 | m_fd = createSignalFd(m_signums); 26 | } 27 | 28 | Signaler::Signaler(const vector& signums, const SignalHandler_t& handler) 29 | : m_loop(0) 30 | , m_fd(-1) 31 | , m_running(false) 32 | , m_signums(signums) 33 | , m_handler(handler) 34 | { 35 | m_fd = createSignalFd(m_signums); 36 | } 37 | 38 | Signaler::~Signaler() 39 | { 40 | LOG_INFO("destroyed %d", m_fd); 41 | if(m_fd > 0) 42 | { 43 | close(m_fd); 44 | } 45 | } 46 | 47 | int Signaler::createSignalFd(const std::vector& signums) 48 | { 49 | sigset_t mask; 50 | 51 | sigemptyset(&mask); 52 | for(size_t i = 0; i < signums.size(); ++i) 53 | { 54 | sigaddset(&mask, signums[i]); 55 | } 56 | 57 | if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0) 58 | { 59 | LOG_ERROR("sigprocmask error"); 60 | return -1; 61 | } 62 | 63 | int fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); 64 | if(fd < 0) 65 | { 66 | LOG_ERROR("signalfd error %s", errorMsg(errno)); 67 | } 68 | 69 | return fd; 70 | } 71 | 72 | void Signaler::start(IOLoop* loop) 73 | { 74 | assert(m_fd > 0); 75 | if(m_running) 76 | { 77 | LOG_WARN("signaler was started"); 78 | return; 79 | } 80 | 81 | LOG_INFO("start signaler %d", m_fd); 82 | 83 | m_running = true; 84 | m_loop = loop; 85 | 86 | m_loop->addHandler(m_fd, TNET_READ, 87 | std::bind(&Signaler::onSignal, shared_from_this(), _1, _2)); 88 | } 89 | 90 | void Signaler::stop() 91 | { 92 | assert(m_fd > 0); 93 | if(!m_running) 94 | { 95 | LOG_WARN("signaler was stopped"); 96 | return; 97 | } 98 | 99 | LOG_INFO("stop signaler %d", m_fd); 100 | 101 | m_running = false; 102 | 103 | m_loop->removeHandler(m_fd); 104 | } 105 | 106 | void Signaler::onSignal(IOLoop* loop, int events) 107 | { 108 | SignalerPtr_t signaler = shared_from_this(); 109 | 110 | struct signalfd_siginfo fdsi; 111 | ssize_t s = read(m_fd, &fdsi, sizeof(fdsi)); 112 | if(s != sizeof(fdsi)) 113 | { 114 | LOG_ERROR("onSignal read error"); 115 | return; 116 | } 117 | 118 | int signum = fdsi.ssi_signo; 119 | if(std::find(m_signums.begin(), m_signums.end(), signum) == m_signums.end()) 120 | { 121 | LOG_ERROR("uexpect signum %d", signum); 122 | return; 123 | } 124 | 125 | m_handler(signaler, signum); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/signaler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "tnet.h" 6 | 7 | namespace tnet 8 | { 9 | class IOLoop; 10 | class Signaler : public nocopyable 11 | , public std::enable_shared_from_this 12 | { 13 | public: 14 | Signaler(int signum, const SignalHandler_t& handler); 15 | Signaler(const std::vector& signums, const SignalHandler_t& handler); 16 | ~Signaler(); 17 | 18 | static int createSignalFd(const std::vector& signums); 19 | 20 | void start(IOLoop* loop); 21 | void stop(); 22 | 23 | int fd() { return m_fd; } 24 | 25 | IOLoop* loop() { return m_loop; } 26 | 27 | private: 28 | void onSignal(IOLoop*, int); 29 | 30 | private: 31 | IOLoop* m_loop; 32 | int m_fd; 33 | bool m_running; 34 | std::vector m_signums; 35 | SignalHandler_t m_handler; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/sockutil.cpp: -------------------------------------------------------------------------------- 1 | #include "sockutil.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "address.h" 17 | #include "log.h" 18 | 19 | using namespace std; 20 | 21 | namespace tnet 22 | { 23 | int SockUtil::create() 24 | { 25 | int fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP); 26 | if(fd < 0) 27 | { 28 | return fd; 29 | } 30 | 31 | return fd; 32 | } 33 | 34 | int SockUtil::bindAndListen(const Address& addr) 35 | { 36 | int err = 0; 37 | 38 | int fd = create(); 39 | if(fd < 0) 40 | { 41 | err = errno; 42 | LOG_ERROR("create socket error %s", errorMsg(err)); 43 | return fd; 44 | } 45 | 46 | SockUtil::setReuseable(fd, true); 47 | 48 | do 49 | { 50 | struct sockaddr_in sockAddr = addr.sockAddr(); 51 | 52 | if(bind(fd, (struct sockaddr*)&sockAddr, sizeof(sockAddr)) < 0) 53 | { 54 | err = errno; 55 | LOG_ERROR("bind address %s:%d error: %s", addr.ipstr().c_str(), addr.port(), errorMsg(err)); 56 | break; 57 | } 58 | 59 | if(listen(fd, SOMAXCONN) < 0) 60 | { 61 | err = errno; 62 | LOG_ERROR("listen address %s:%d error: %s", addr.ipstr().c_str(), addr.port(), errorMsg(err)); 63 | break; 64 | } 65 | 66 | 67 | return fd; 68 | 69 | }while(0); 70 | 71 | close(fd); 72 | return err; 73 | } 74 | 75 | int SockUtil::connect(int sockFd, const Address& addr) 76 | { 77 | struct sockaddr_in sockAddr = addr.sockAddr(); 78 | 79 | int ret = ::connect(sockFd, (struct sockaddr*)&sockAddr, sizeof(sockAddr)); 80 | if(ret < 0) 81 | { 82 | int err = errno; 83 | return err; 84 | } 85 | else 86 | { 87 | SockUtil::setNoDelay(sockFd, true); 88 | return ret; 89 | } 90 | } 91 | 92 | int SockUtil::setNoDelay(int sockFd, bool on) 93 | { 94 | int opt = on ? 1 : 0; 95 | 96 | return setsockopt(sockFd, IPPROTO_TCP, 97 | TCP_NODELAY, &opt, 98 | static_cast(sizeof(opt))); 99 | } 100 | 101 | int SockUtil::setReuseable(int sockFd, bool on) 102 | { 103 | int opt = on ? 1 : 0; 104 | return setsockopt(sockFd, SOL_SOCKET, 105 | SO_REUSEADDR, &opt, 106 | static_cast(sizeof(opt))); 107 | } 108 | 109 | int SockUtil::setKeepAlive(int sockFd, bool on) 110 | { 111 | int opt = on ? 1 : 0; 112 | return setsockopt(sockFd, SOL_SOCKET, 113 | SO_KEEPALIVE, &opt, 114 | static_cast(sizeof(opt))); 115 | } 116 | 117 | int SockUtil::getLocalAddr(int sockFd, Address& addr) 118 | { 119 | struct sockaddr_in sockAddr; 120 | socklen_t sockLen = sizeof(sockAddr); 121 | if(getsockname(sockFd, (struct sockaddr*)&sockAddr, &sockLen) != 0) 122 | { 123 | int err = errno; 124 | return err; 125 | } 126 | 127 | addr = Address(sockAddr); 128 | return 0; 129 | } 130 | 131 | int SockUtil::getRemoteAddr(int sockFd, Address& addr) 132 | { 133 | struct sockaddr_in sockAddr; 134 | socklen_t sockLen = sizeof(sockAddr); 135 | if(getpeername(sockFd, (struct sockaddr*)&sockAddr, &sockLen) != 0) 136 | { 137 | int err = errno; 138 | return err; 139 | } 140 | 141 | addr = Address(sockAddr); 142 | return 0; 143 | } 144 | 145 | int SockUtil::getSockError(int sockFd) 146 | { 147 | int opt; 148 | socklen_t optLen = static_cast(sizeof(opt)); 149 | 150 | if(getsockopt(sockFd, SOL_SOCKET, SO_ERROR, &opt, &optLen) < 0) 151 | { 152 | int err = errno; 153 | return err; 154 | } 155 | else 156 | { 157 | return opt; 158 | } 159 | } 160 | 161 | uint32_t SockUtil::getHostByName(const string& host) 162 | { 163 | struct addrinfo hint; 164 | struct addrinfo *answer; 165 | 166 | memset(&hint, 0, sizeof(hint)); 167 | hint.ai_family = AF_INET; 168 | hint.ai_socktype = SOCK_STREAM; 169 | 170 | int ret = getaddrinfo(host.c_str(), NULL, &hint, &answer); 171 | if(ret != 0) 172 | { 173 | LOG_ERROR("getaddrinfo error %s", errorMsg(errno)); 174 | return uint32_t(-1); 175 | } 176 | 177 | //we only use first find addr 178 | for(struct addrinfo* cur = answer; cur != NULL; cur = cur->ai_next) 179 | { 180 | return ((struct sockaddr_in*)(cur->ai_addr))->sin_addr.s_addr; 181 | } 182 | 183 | LOG_ERROR("getHostByName Error"); 184 | return uint32_t(-1); 185 | } 186 | 187 | int SockUtil::bindDevice(int sockFd, const string& device) 188 | { 189 | 190 | struct ifreq ifr; 191 | memset(&ifr, 0, sizeof(ifr)); 192 | 193 | snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), device.c_str()); 194 | struct sockaddr_in *sin=(struct sockaddr_in*)&ifr.ifr_addr; 195 | 196 | if(ioctl(sockFd, SIOCGIFADDR, &ifr) < 0) 197 | { 198 | LOG_ERROR("ioctl get addr error %s", errorMsg(errno)); 199 | return -1; 200 | } 201 | 202 | sin->sin_family = AF_INET; 203 | sin->sin_port = 0; 204 | 205 | if(bind(sockFd, (struct sockaddr*)sin, sizeof(*sin)) < 0) 206 | { 207 | LOG_ERROR("bind interface error %s", errorMsg(errno)); 208 | return -1; 209 | } 210 | 211 | return 0; 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/sockutil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace tnet 7 | { 8 | class Address; 9 | class SockUtil 10 | { 11 | public: 12 | static int create(); 13 | static int bindAndListen(const Address& addr); 14 | static int connect(int sockFd, const Address& addr); 15 | 16 | static int setNoDelay(int sockFd, bool on); 17 | 18 | static int setReuseable(int sockFd, bool on); 19 | static int setKeepAlive(int sockFd, bool on); 20 | 21 | static int getLocalAddr(int sockFd, Address& addr); 22 | static int getRemoteAddr(int sockFd, Address& addr); 23 | 24 | static int getSockError(int sockFd); 25 | 26 | static uint32_t getHostByName(const std::string& host); 27 | 28 | static int bindDevice(int sockFd, const std::string& device); 29 | }; 30 | 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/spinlock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tnet.h" 4 | 5 | namespace tnet 6 | { 7 | class SpinLock : public nocopyable 8 | { 9 | public: 10 | SpinLock() { 11 | m_lock = 0; 12 | } 13 | ~SpinLock() {} 14 | 15 | void lock() 16 | { 17 | while(__sync_lock_test_and_set(&m_lock, 1)) 18 | { 19 | } 20 | } 21 | 22 | void unlock() 23 | { 24 | __sync_lock_release(&m_lock); 25 | } 26 | 27 | private: 28 | volatile int m_lock; 29 | }; 30 | 31 | class SpinLockGuard : public nocopyable 32 | { 33 | public: 34 | SpinLockGuard(SpinLock& lock) 35 | : m_lock(lock) 36 | { 37 | m_lock.lock(); 38 | } 39 | 40 | ~SpinLockGuard() 41 | { 42 | m_lock.unlock(); 43 | } 44 | private: 45 | SpinLock& m_lock; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/stringutil.cpp: -------------------------------------------------------------------------------- 1 | #include "stringutil.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | extern "C" 8 | { 9 | #include "polarssl/base64.h" 10 | #include "polarssl/md5.h" 11 | #include "polarssl/sha1.h" 12 | } 13 | 14 | using namespace std; 15 | 16 | namespace tnet 17 | { 18 | 19 | vector StringUtil::split(const string& src, const string& delim, size_t maxParts) 20 | { 21 | if(maxParts == 0) 22 | { 23 | maxParts = size_t(-1); 24 | } 25 | size_t lastPos = 0; 26 | size_t pos = 0; 27 | size_t size = src.size(); 28 | 29 | vector tokens; 30 | 31 | while(pos < size) 32 | { 33 | pos = lastPos; 34 | while(pos < size && delim.find_first_of(src[pos]) == string::npos) 35 | { 36 | ++pos; 37 | } 38 | 39 | if(pos - lastPos > 0) 40 | { 41 | if(tokens.size() == maxParts - 1) 42 | { 43 | tokens.push_back(src.substr(lastPos)); 44 | break; 45 | } 46 | else 47 | { 48 | tokens.push_back(src.substr(lastPos, pos - lastPos)); 49 | } 50 | } 51 | 52 | lastPos = pos + 1; 53 | } 54 | 55 | return tokens; 56 | } 57 | 58 | uint32_t StringUtil::hash(const string& str) 59 | { 60 | //use elf hash 61 | uint32_t h = 0; 62 | uint32_t x = 0; 63 | uint32_t i = 0; 64 | uint32_t len = (uint32_t)str.size(); 65 | for(i = 0; i < len; ++i) 66 | { 67 | h = (h << 4) + str[i]; 68 | if((x = h & 0xF0000000L) != 0) 69 | { 70 | h ^= (x >> 24); 71 | h &= ~x; 72 | } 73 | } 74 | 75 | return (h & 0x7FFFFFFF); 76 | } 77 | 78 | string StringUtil::lower(const string& src) 79 | { 80 | string dest(src); 81 | 82 | transform(dest.begin(), dest.end(), dest.begin(), ::tolower); 83 | return dest; 84 | } 85 | 86 | string StringUtil::upper(const string& src) 87 | { 88 | string dest(src); 89 | 90 | transform(dest.begin(), dest.end(), dest.begin(), ::toupper); 91 | return dest; 92 | } 93 | 94 | string StringUtil::lstrip(const string& src) 95 | { 96 | string s(src); 97 | s.erase(s.begin(), find_if(s.begin(), s.end(), not1(ptr_fun(isspace)))); 98 | return s; 99 | } 100 | 101 | string StringUtil::rstrip(const string& src) 102 | { 103 | string s(src); 104 | s.erase(find_if(s.rbegin(), s.rend(), not1(ptr_fun(isspace))).base(), s.end()); 105 | return s; 106 | } 107 | 108 | string StringUtil::strip(const string& src) 109 | { 110 | return lstrip(rstrip(src)); 111 | } 112 | string StringUtil::hex(const uint8_t* src, size_t srcLen) 113 | { 114 | size_t destLen = 2 * srcLen; 115 | string dest(destLen, '\0'); 116 | 117 | static uint8_t h[] = "0123456789abcdef"; 118 | 119 | for(size_t i = 0; i < srcLen; i++) 120 | { 121 | dest[i] = h[src[i] >> 4]; 122 | dest[i + 1] = h[src[i + 1] & 0xf]; 123 | } 124 | 125 | return dest; 126 | } 127 | 128 | string StringUtil::hex(const string& src) 129 | { 130 | return hex((const uint8_t*)src.data(), src.size()); 131 | } 132 | 133 | string StringUtil::base64Encode(const string& src) 134 | { 135 | size_t destLen = src.size() * 4 / 3 + 4; 136 | string str(destLen, '\0'); 137 | 138 | uint8_t* dest = (uint8_t*)&str[0]; 139 | 140 | if(base64_encode(dest, &destLen, (uint8_t*)src.data(), src.size()) != 0) 141 | { 142 | return ""; 143 | } 144 | 145 | str.resize(destLen); 146 | 147 | return str; 148 | } 149 | 150 | string StringUtil::base64Decode(const string& src) 151 | { 152 | size_t destLen = src.size() * 3 / 4 + 4; 153 | string str(destLen, '\0'); 154 | 155 | uint8_t* dest = (uint8_t*)&str[0]; 156 | 157 | if(base64_decode(dest, &destLen, (uint8_t*)src.data(), src.size()) != 0) 158 | { 159 | return ""; 160 | } 161 | 162 | str.resize(destLen); 163 | 164 | return str; 165 | } 166 | 167 | string StringUtil::md5Bin(const string& src) 168 | { 169 | uint8_t output[16]; 170 | md5((const uint8_t*)src.data(), src.size(), output); 171 | 172 | return string((char*)output, 16); 173 | } 174 | 175 | string StringUtil::md5Hex(const string& src) 176 | { 177 | uint8_t output[16]; 178 | md5((const uint8_t*)src.data(), src.size(), output); 179 | 180 | return hex(output, 16); 181 | } 182 | 183 | string StringUtil::sha1Bin(const string& src) 184 | { 185 | uint8_t output[20]; 186 | sha1((const uint8_t*)src.data(), src.size(), output); 187 | return string((char*)output, 20); 188 | } 189 | 190 | string StringUtil::sha1Hex(const string& src) 191 | { 192 | uint8_t output[20]; 193 | sha1((const uint8_t*)src.data(), src.size(), output); 194 | return hex(output, 20); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/stringutil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tnet 9 | { 10 | class StringUtil 11 | { 12 | public: 13 | static std::vector split(const std::string& src, const std::string& delim, size_t maxParts = size_t(-1)); 14 | static uint32_t hash(const std::string& str); 15 | 16 | static std::string lower(const std::string& src); 17 | static std::string upper(const std::string& src); 18 | 19 | static std::string lstrip(const std::string& src); 20 | static std::string rstrip(const std::string& src); 21 | static std::string strip(const std::string& src); 22 | 23 | static std::string hex(const std::string& src); 24 | static std::string hex(const uint8_t* src, size_t srcLen); 25 | 26 | static std::string base64Encode(const std::string& src); 27 | static std::string base64Decode(const std::string& src); 28 | 29 | static std::string md5Bin(const std::string& src); 30 | static std::string md5Hex(const std::string& src); 31 | 32 | static std::string sha1Bin(const std::string& src); 33 | static std::string sha1Hex(const std::string& src); 34 | 35 | template 36 | static std::string toString(const T& in) 37 | { 38 | std::stringstream str; 39 | str << in; 40 | return str.str(); 41 | } 42 | 43 | static std::string toString(const char* in) 44 | { 45 | return std::string(in); 46 | } 47 | 48 | static std::string toString(const std::string& in) 49 | { 50 | return std::string(in); 51 | } 52 | }; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/tcpserver.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpserver.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "address.h" 13 | #include "sockutil.h" 14 | #include "log.h" 15 | #include "acceptor.h" 16 | #include "connection.h" 17 | #include "ioloop.h" 18 | #include "signaler.h" 19 | #include "timer.h" 20 | #include "process.h" 21 | #include "timingwheel.h" 22 | 23 | using namespace std; 24 | 25 | namespace tnet 26 | { 27 | const int defaultIdleTimeout = 120; 28 | 29 | void dummyRunCallback(IOLoop*) 30 | { 31 | } 32 | 33 | TcpServer::TcpServer() 34 | : m_loop(0) 35 | { 36 | m_process = std::make_shared(); 37 | 38 | m_running = false; 39 | 40 | m_maxIdleTimeout = defaultIdleTimeout; 41 | 42 | m_runCallback = std::bind(&dummyRunCallback, _1); 43 | } 44 | 45 | TcpServer::~TcpServer() 46 | { 47 | if(m_loop) 48 | { 49 | delete m_loop; 50 | } 51 | } 52 | 53 | int TcpServer::listen(const Address& addr, const ConnEventCallback_t& callback) 54 | { 55 | LOG_INFO("listen %s:%d", addr.ipstr().c_str(), addr.port()); 56 | NewConnCallback_t cb = std::bind(&TcpServer::onNewConnection, this, _1, _2, callback); 57 | AcceptorPtr_t acceptor = std::make_shared(cb); 58 | if(acceptor->listen(addr) < 0) 59 | { 60 | return -1; 61 | } 62 | 63 | m_acceptors.push_back(acceptor); 64 | return 0; 65 | } 66 | 67 | void TcpServer::run() 68 | { 69 | if(m_running) 70 | { 71 | return; 72 | } 73 | 74 | m_loop = new IOLoop(); 75 | 76 | m_running = true; 77 | 78 | 79 | m_loop->addCallback(std::bind(&TcpServer::onRun, this)); 80 | 81 | m_loop->start(); 82 | } 83 | 84 | void TcpServer::onRun() 85 | { 86 | LOG_INFO("tcp server on run"); 87 | 88 | for_each(m_acceptors.begin(), m_acceptors.end(), 89 | std::bind(&Acceptor::start, _1, m_loop)); 90 | 91 | vector signums{SIGINT, SIGTERM}; 92 | m_signaler = std::make_shared(signums, std::bind(&TcpServer::onSignal, this, _1, _2)); 93 | 94 | m_signaler->start(m_loop); 95 | 96 | m_idleWheel = std::make_shared(1000, 3600); 97 | 98 | m_idleWheel->start(m_loop); 99 | 100 | m_runCallback(m_loop); 101 | } 102 | 103 | void TcpServer::start(size_t maxProcess) 104 | { 105 | if(maxProcess > 1) 106 | { 107 | m_process->wait(maxProcess, std::bind(&TcpServer::run, this)); 108 | } 109 | else 110 | { 111 | run(); 112 | } 113 | } 114 | 115 | void TcpServer::onStop() 116 | { 117 | LOG_INFO("tcp server on stop"); 118 | if(!m_running) 119 | { 120 | return; 121 | } 122 | 123 | m_running = false; 124 | 125 | m_idleWheel->stop(); 126 | 127 | m_signaler->stop(); 128 | 129 | for_each_all(m_acceptors, std::bind(&Acceptor::stop, _1)); 130 | 131 | m_loop->stop(); 132 | } 133 | 134 | void TcpServer::stop() 135 | { 136 | LOG_INFO("stop server"); 137 | m_process->stop(); 138 | 139 | onStop(); 140 | } 141 | 142 | void TcpServer::onNewConnection(IOLoop* loop, int fd, const ConnEventCallback_t& callback) 143 | { 144 | ConnectionPtr_t conn = std::make_shared(loop, fd); 145 | 146 | conn->setEventCallback(callback); 147 | 148 | conn->onEstablished(); 149 | 150 | int afterCheck = m_maxIdleTimeout / 2 + random() % m_maxIdleTimeout; 151 | m_idleWheel->add(std::bind(&TcpServer::onIdleConnCheck, this, _1, WeakConnectionPtr_t(conn)), afterCheck * 1000); 152 | 153 | return; 154 | } 155 | 156 | void TcpServer::onIdleConnCheck(const TimingWheelPtr_t& wheel, const WeakConnectionPtr_t& conn) 157 | { 158 | ConnectionPtr_t c = conn.lock(); 159 | if(!c) 160 | { 161 | return; 162 | } 163 | 164 | struct timespec t; 165 | clock_gettime(CLOCK_MONOTONIC, &t); 166 | uint64_t now = t.tv_sec; 167 | 168 | if(now - c->lastActiveTime() > (uint64_t)m_maxIdleTimeout) 169 | { 170 | LOG_INFO("timeout, force shutdown"); 171 | c->shutDown(); 172 | } 173 | else 174 | { 175 | //check interval is (maxIdleTimeout * 9 / 10) * 1000 176 | m_idleWheel->add(std::bind(&TcpServer::onIdleConnCheck, this, _1, WeakConnectionPtr_t(c)), m_maxIdleTimeout * 900); 177 | } 178 | } 179 | 180 | void TcpServer::onSignal(const SignalerPtr_t& signaler, int signum) 181 | { 182 | switch(signum) 183 | { 184 | case SIGINT: 185 | case SIGTERM: 186 | { 187 | onStop(); 188 | } 189 | break; 190 | default: 191 | LOG_ERROR("invalid signal %d", signum); 192 | break; 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/tcpserver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "tnet.h" 8 | 9 | namespace tnet 10 | { 11 | class Address; 12 | class IOLoop; 13 | class Process; 14 | class ConnChecker; 15 | 16 | class TcpServer : public nocopyable 17 | { 18 | public: 19 | TcpServer(); 20 | ~TcpServer(); 21 | 22 | int listen(const Address& addr, const ConnEventCallback_t& callback); 23 | 24 | void start(size_t maxProcess = 0); 25 | void stop(); 26 | 27 | IOLoop* getLoop() { return m_loop; } 28 | 29 | void setRunCallback(const ServerRunCallback_t& callback) { m_runCallback = callback; } 30 | 31 | //timeout is second 32 | void setMaxIdleTimeout(int timeout) { m_maxIdleTimeout = timeout; } 33 | 34 | private: 35 | void run(); 36 | void onRun(); 37 | void onStop(); 38 | void onSignal(const SignalerPtr_t& signaler, int signum); 39 | void onIdleConnCheck(const TimingWheelPtr_t& wheel, const WeakConnectionPtr_t& conn); 40 | void onNewConnection(IOLoop* loop, int fd, const ConnEventCallback_t& callback); 41 | 42 | private: 43 | IOLoop* m_loop; 44 | 45 | ProcessPtr_t m_process; 46 | 47 | std::vector m_acceptors; 48 | 49 | SignalerPtr_t m_signaler; 50 | 51 | int m_maxIdleTimeout; 52 | TimingWheelPtr_t m_idleWheel; 53 | 54 | std::set m_workers; 55 | size_t m_workerNum; 56 | 57 | bool m_workerProc; 58 | bool m_running; 59 | 60 | ServerRunCallback_t m_runCallback; 61 | }; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ioloop.h" 8 | #include "log.h" 9 | 10 | namespace tnet 11 | { 12 | const uint64_t milliPerSeconds = 1000; 13 | const uint64_t microPerSeconds = 1000000; 14 | const uint64_t nanoPerSeconds = 1000000000; 15 | const uint64_t nanoPerMilli = 1000000; 16 | 17 | Timer::Timer(const TimerHandler_t& handler, int repeat, int after) 18 | : m_loop(0) 19 | , m_running(false) 20 | , m_handler(handler) 21 | , m_repeated(false) 22 | { 23 | m_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); 24 | if(m_fd < 0) 25 | { 26 | LOG_ERROR("create timer error %s", errorMsg(errno)); 27 | return; 28 | } 29 | 30 | reset(repeat, after); 31 | } 32 | 33 | Timer::~Timer() 34 | { 35 | LOG_INFO("destroyed %d", m_fd); 36 | if(m_fd > 0) 37 | { 38 | close(m_fd); 39 | } 40 | } 41 | 42 | void Timer::start(IOLoop* loop) 43 | { 44 | assert(m_fd > 0); 45 | 46 | if(m_running) 47 | { 48 | LOG_WARN("timer was started"); 49 | return; 50 | } 51 | 52 | LOG_INFO("start timer %d", m_fd); 53 | 54 | m_loop = loop; 55 | 56 | m_running = true; 57 | 58 | m_loop->addHandler(m_fd, TNET_READ, 59 | std::bind(&Timer::onTimer, shared_from_this(), _1, _2)); 60 | } 61 | 62 | void Timer::stop() 63 | { 64 | if(!m_running) 65 | { 66 | LOG_WARN("timer was stopped"); 67 | return; 68 | } 69 | 70 | m_running = false; 71 | m_loop->removeHandler(m_fd); 72 | 73 | } 74 | 75 | void Timer::reset(int repeat, int after) 76 | { 77 | if(m_fd <= 0) 78 | { 79 | return; 80 | } 81 | 82 | m_repeated = (repeat > 0); 83 | 84 | struct itimerspec t; 85 | if(repeat > 0) 86 | { 87 | t.it_interval.tv_sec = (uint64_t)repeat / milliPerSeconds; 88 | t.it_interval.tv_nsec = ((uint64_t)repeat % milliPerSeconds) * nanoPerMilli; 89 | } 90 | 91 | struct timespec now; 92 | clock_gettime(CLOCK_MONOTONIC, &now); 93 | t.it_value.tv_sec = now.tv_sec + (uint64_t)after / milliPerSeconds; 94 | t.it_value.tv_nsec = now.tv_nsec + ((uint64_t)after % milliPerSeconds) * nanoPerMilli; 95 | 96 | if(timerfd_settime(m_fd, TFD_TIMER_ABSTIME, &t, NULL) < 0) 97 | { 98 | LOG_ERROR("set timer error"); 99 | } 100 | } 101 | 102 | void Timer::onTimer(IOLoop* loop, int events) 103 | { 104 | TimerPtr_t timer = shared_from_this(); 105 | 106 | uint64_t exp; 107 | ssize_t s = read(m_fd, &exp, sizeof(uint64_t)); 108 | if(s != sizeof(exp)) 109 | { 110 | LOG_ERROR("onTimer read error"); 111 | } 112 | else 113 | { 114 | m_handler(timer); 115 | 116 | if(!isRepeated()) 117 | { 118 | //timer is an once timer, here stop 119 | timer->stop(); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tnet.h" 4 | 5 | namespace tnet 6 | { 7 | class IOLoop; 8 | class Timer : public nocopyable 9 | , public std::enable_shared_from_this 10 | { 11 | public: 12 | //repeat and after are all milliseconds 13 | Timer(const TimerHandler_t& handler, int repeat, int after); 14 | ~Timer(); 15 | 16 | void start(IOLoop* loop); 17 | void stop(); 18 | 19 | void reset(int repeat, int after); 20 | 21 | int fd() { return m_fd; } 22 | 23 | IOLoop* loop() { return m_loop; } 24 | 25 | bool isRepeated() { return m_repeated; } 26 | 27 | private: 28 | void onTimer(IOLoop* loop, int events); 29 | 30 | void initTimer(int repeat, int after); 31 | 32 | private: 33 | IOLoop* m_loop; 34 | int m_fd; 35 | bool m_running; 36 | bool m_repeated; 37 | 38 | TimerHandler_t m_handler; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/timingwheel.cpp: -------------------------------------------------------------------------------- 1 | #include "timingwheel.h" 2 | 3 | #include "log.h" 4 | #include "timer.h" 5 | 6 | using namespace std; 7 | 8 | namespace tnet 9 | { 10 | void dummyHandler(const TimingWheelPtr_t&) 11 | { 12 | 13 | } 14 | 15 | TimingWheel::TimingWheel(int interval, int maxBuckets) 16 | : m_loop(0) 17 | , m_interval(interval) 18 | , m_maxBuckets(maxBuckets) 19 | , m_nextBucket(0) 20 | { 21 | m_maxTimeout = interval * maxBuckets; 22 | 23 | m_timer = std::make_shared(std::bind(&TimingWheel::onTimer, this, _1), interval, 0); 24 | 25 | m_buckets.resize(maxBuckets); 26 | } 27 | 28 | TimingWheel::~TimingWheel() 29 | { 30 | 31 | } 32 | 33 | void TimingWheel::start(IOLoop* loop) 34 | { 35 | if(m_running) 36 | { 37 | LOG_WARN("timing wheel was started"); 38 | return; 39 | } 40 | 41 | m_loop = loop; 42 | m_running = true; 43 | 44 | m_timer->start(loop); 45 | } 46 | 47 | void TimingWheel::stop() 48 | { 49 | if(!m_running) 50 | { 51 | LOG_WARN("timing wheel was stopped"); 52 | return; 53 | } 54 | 55 | m_running = false; 56 | m_timer->stop(); 57 | } 58 | 59 | void TimingWheel::onTimer(const TimerPtr_t& timer) 60 | { 61 | int index = m_nextBucket; 62 | 63 | auto& chans = m_buckets[index]; 64 | for(auto iter = chans.begin(); iter != chans.end(); ++iter) 65 | { 66 | (*iter)(shared_from_this()); 67 | } 68 | 69 | chans.clear(); 70 | 71 | m_nextBucket = (m_nextBucket + 1) % m_maxBuckets; 72 | } 73 | 74 | union Slot 75 | { 76 | uint64_t h; 77 | uint32_t p[2]; 78 | }; 79 | 80 | uint64_t TimingWheel::add(const TimingWheelHandler_t& handler, int timeout) 81 | { 82 | if(timeout >= m_maxTimeout) 83 | { 84 | LOG_ERROR("timeout %d > max %d", timeout, m_maxTimeout); 85 | return (uint64_t)(-1); 86 | } 87 | 88 | uint32_t bucket = (m_nextBucket + timeout / m_interval) % m_maxBuckets; 89 | uint32_t id = uint32_t(m_buckets[bucket].size()); 90 | 91 | m_buckets[bucket].push_back(handler); 92 | 93 | Slot u; 94 | u.p[0] = bucket; 95 | u.p[1] = id; 96 | return u.h; 97 | } 98 | 99 | uint64_t TimingWheel::update(uint64_t slot, int timeout) 100 | { 101 | Slot u; 102 | u.h = slot; 103 | 104 | uint32_t bucket = u.p[0]; 105 | uint32_t id = u.p[1]; 106 | 107 | if(bucket > m_maxBuckets) 108 | { 109 | return (uint64_t)-1; 110 | } 111 | 112 | auto& chans = m_buckets[bucket]; 113 | if(id >= uint32_t(chans.size())) 114 | { 115 | return (uint64_t)-1; 116 | } 117 | 118 | auto&& cb = std::move(chans[id]); 119 | chans[id] = std::bind(&dummyHandler, _1); 120 | 121 | return add(cb, timeout); 122 | } 123 | 124 | void TimingWheel::remove(uint64_t slot) 125 | { 126 | Slot u; 127 | u.h = slot; 128 | 129 | uint32_t bucket = u.p[0]; 130 | uint32_t id = u.p[1]; 131 | 132 | if(bucket > m_maxBuckets) 133 | { 134 | return; 135 | } 136 | 137 | auto& chans = m_buckets[bucket]; 138 | 139 | if(id >= uint32_t(chans.size())) 140 | { 141 | return; 142 | } 143 | 144 | chans[id] = std::bind(&dummyHandler, _1); 145 | 146 | return; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/timingwheel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tnet.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace tnet 9 | { 10 | class TimingWheel : public nocopyable 11 | , public std::enable_shared_from_this 12 | { 13 | public: 14 | //interval is milliseconds 15 | //max timeout is interval * maxBuckets 16 | TimingWheel(int interval, int maxBuckets); 17 | ~TimingWheel(); 18 | 19 | void start(IOLoop* loop); 20 | void stop(); 21 | 22 | IOLoop* loop() { return m_loop; } 23 | 24 | uint64_t add(const TimingWheelHandler_t& handler, int timeout); 25 | uint64_t update(uint64_t slot, int timeout); 26 | void remove(uint64_t slot); 27 | 28 | private: 29 | void onTimer(const TimerPtr_t& timer); 30 | 31 | private: 32 | IOLoop* m_loop; 33 | bool m_running; 34 | TimerPtr_t m_timer; 35 | 36 | int m_interval; 37 | int m_maxBuckets; 38 | 39 | int m_maxTimeout; 40 | 41 | int m_nextBucket; 42 | 43 | typedef std::vector TimingChan_t; 44 | typedef std::vector Buckets_t; 45 | 46 | Buckets_t m_buckets; 47 | }; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/tnet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "nocopyable.h" 8 | 9 | using namespace std::placeholders; 10 | 11 | namespace tnet 12 | { 13 | #define TNET_VERSION "0.1" 14 | 15 | class IOLoop; 16 | class Acceptor; 17 | class Connection; 18 | class Timer; 19 | class Signaler; 20 | class Process; 21 | class Notifier; 22 | class Address; 23 | class TimingWheel; 24 | 25 | enum 26 | { 27 | TNET_NONE = 0x00000000, 28 | TNET_READ = 0x00000001, 29 | TNET_WRITE = 0x00000002, 30 | TNET_ERROR = 0x80000000, 31 | }; 32 | 33 | enum ConnEvent 34 | { 35 | Conn_EstablishedEvent, 36 | Conn_ConnectEvent, 37 | Conn_ConnectingEvent, 38 | Conn_ReadEvent, 39 | Conn_WriteCompleteEvent, 40 | Conn_ErrorEvent, 41 | Conn_CloseEvent, 42 | }; 43 | 44 | template 45 | void for_each_all(T& c, const Func& func) 46 | { 47 | typename T::iterator iter = c.begin(); 48 | while(iter != c.end()) 49 | { 50 | func(*iter); 51 | ++iter; 52 | } 53 | } 54 | 55 | typedef std::shared_ptr ConnectionPtr_t; 56 | typedef std::weak_ptr WeakConnectionPtr_t; 57 | typedef std::shared_ptr AcceptorPtr_t; 58 | typedef std::shared_ptr TimerPtr_t; 59 | typedef std::shared_ptr SignalerPtr_t; 60 | typedef std::shared_ptr ProcessPtr_t; 61 | typedef std::shared_ptr NotifierPtr_t; 62 | typedef std::shared_ptr TimingWheelPtr_t; 63 | 64 | typedef std::function IOHandler_t; 65 | typedef std::function NewConnCallback_t; 66 | 67 | typedef std::function ServerRunCallback_t; 68 | 69 | class StackBuffer 70 | { 71 | public: 72 | StackBuffer(const char* buf, size_t c) : buffer(buf), count(c) {} 73 | 74 | const char* buffer; 75 | size_t count; 76 | }; 77 | 78 | //Conn_ReadEvent, context is StackBuffer, it will be destroied after callback 79 | //you must fetch data and store yourself if you need it later 80 | //other conn event, context is NULL 81 | 82 | typedef std::function ConnEventCallback_t; 83 | 84 | typedef std::function Callback_t; 85 | typedef std::function ProcessCallback_t; 86 | 87 | typedef std::function TimerHandler_t; 88 | 89 | typedef std::function SignalHandler_t; 90 | typedef std::function NotifierHandler_t; 91 | typedef std::function TimingWheelHandler_t; 92 | } 93 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS}") 2 | 3 | include_directories(../src) 4 | include_directories(../src/http) 5 | include_directories(../src/redis) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) 8 | 9 | add_executable(echoserver_test echoserver_test.cpp) 10 | target_link_libraries(echoserver_test tnet) 11 | 12 | add_executable(echoclient_test echoclient_test.cpp) 13 | target_link_libraries(echoclient_test tnet) 14 | 15 | add_executable(httpserver_test httpserver_test.cpp) 16 | target_link_libraries(httpserver_test tnet tnet_http) 17 | 18 | add_executable(httpclient_test httpclient_test.cpp) 19 | target_link_libraries(httpclient_test tnet tnet_http) 20 | 21 | add_executable(wsserver_test wsserver_test.cpp) 22 | target_link_libraries(wsserver_test tnet tnet_http) 23 | 24 | add_executable(wsclient_test wsclient_test.cpp) 25 | target_link_libraries(wsclient_test tnet tnet_http) 26 | 27 | add_executable(timer_test timer_test.cpp) 28 | target_link_libraries(timer_test tnet rt) 29 | 30 | add_executable(timingwheel_test timingwheel_test.cpp) 31 | target_link_libraries(timingwheel_test tnet rt) 32 | 33 | add_executable(signaler_test signaler_test.cpp) 34 | target_link_libraries(signaler_test tnet) 35 | 36 | add_executable(notifier_test notifier_test.cpp) 37 | target_link_libraries(notifier_test tnet) 38 | 39 | add_executable(redisclient_test redisclient_test.cpp) 40 | target_link_libraries(redisclient_test tnet tnet_redis) 41 | 42 | 43 | add_executable(cometserver_test cometserver_test.cpp) 44 | target_link_libraries(cometserver_test tnet tnet_http) 45 | 46 | 47 | add_executable(cometclient_test cometclient_test.cpp) 48 | target_link_libraries(cometclient_test tnet tnet_http) 49 | -------------------------------------------------------------------------------- /test/cometclient_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "log.h" 7 | #include "timingwheel.h" 8 | #include "httpclient.h" 9 | 10 | #include "httprequest.h" 11 | #include "httpresponse.h" 12 | #include "ioloop.h" 13 | 14 | using namespace std; 15 | using namespace tnet; 16 | 17 | string url = "http://10.20.187.120:11181/"; 18 | 19 | void onResponse(const HttpClientPtr_t& client, const HttpResponse& resp) 20 | { 21 | if(resp.statusCode != 200) 22 | { 23 | cout << "request error:" << resp.statusCode << "\t" << resp.body << endl; 24 | return; 25 | } 26 | 27 | client->request(url, std::bind(&onResponse, client, _1)); 28 | } 29 | 30 | void request(const TimingWheelPtr_t& wheel, const HttpClientPtr_t& client, int num) 31 | { 32 | for(int i = 0; i < num; ++i) 33 | { 34 | client->request(url, std::bind(&onResponse, client, _1)); 35 | } 36 | } 37 | 38 | int main(int argc, char* args[]) 39 | { 40 | Log::rootLog().setLevel(Log::ERROR); 41 | 42 | if(argc < 2) 43 | { 44 | cout << "args: num [eth0]" << endl; 45 | } 46 | 47 | int num = num = atoi(args[1]); 48 | 49 | vector clients; 50 | 51 | IOLoop loop; 52 | 53 | if(argc == 2) 54 | { 55 | clients.push_back(std::make_shared(&loop)); 56 | } 57 | else 58 | { 59 | for(int i = 0; i < argc - 2; ++i) 60 | { 61 | HttpClientPtr_t client = std::make_shared(&loop); 62 | client->bindDevice(args[i + 2]); 63 | clients.push_back(client); 64 | } 65 | } 66 | 67 | cout << "request num:" << num << endl; 68 | 69 | 70 | TimingWheelPtr_t wheel = std::make_shared(1000, 3600); 71 | 72 | int c = 60 * clients.size(); 73 | for(int i = 0; i < c; ++i) 74 | { 75 | int reqNum = num / c; 76 | for(auto it = clients.begin(); it != clients.end(); ++it) 77 | { 78 | wheel->add(std::bind(&request, _1, *it, reqNum), i * 1000); 79 | } 80 | } 81 | 82 | wheel->start(&loop); 83 | 84 | loop.start(); 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /test/cometserver_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tnet.h" 2 | 3 | #include 4 | 5 | #include "log.h" 6 | #include "tcpserver.h" 7 | #include "address.h" 8 | #include "httpserver.h" 9 | #include "httprequest.h" 10 | #include "httpresponse.h" 11 | #include "httpconnection.h" 12 | #include "timingwheel.h" 13 | 14 | using namespace std; 15 | using namespace tnet; 16 | 17 | class CometServer 18 | { 19 | public: 20 | TimingWheelPtr_t wheel; 21 | }; 22 | 23 | static CometServer comet; 24 | 25 | void onServerRun(IOLoop* loop) 26 | { 27 | comet.wheel = std::make_shared(1000, 3600); 28 | comet.wheel->start(loop); 29 | } 30 | 31 | void onTimeout(const TimingWheelPtr_t& wheel, const WeakHttpConnectionPtr_t& conn) 32 | { 33 | HttpConnectionPtr_t c = conn.lock(); 34 | if(c) 35 | { 36 | c->send(200); 37 | } 38 | } 39 | 40 | void onHandler(const HttpConnectionPtr_t& conn, const HttpRequest& request) 41 | { 42 | if(request.method == HTTP_GET) 43 | { 44 | int timeout = random() % 60 + 30; 45 | comet.wheel->add(std::bind(&onTimeout, _1, WeakHttpConnectionPtr_t(conn)), timeout * 1000); 46 | //conn->send(200); 47 | } 48 | else 49 | { 50 | conn->send(405); 51 | } 52 | } 53 | 54 | int main() 55 | { 56 | Log::rootLog().setLevel(Log::ERROR); 57 | 58 | TcpServer s; 59 | 60 | s.setRunCallback(std::bind(&onServerRun, _1)); 61 | 62 | HttpServer httpd(&s); 63 | 64 | httpd.setHttpCallback("/", std::bind(&onHandler, _1, _2)); 65 | 66 | httpd.listen(Address(11181)); 67 | 68 | s.start(8); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /test/echoclient_test.cpp: -------------------------------------------------------------------------------- 1 | #include "ioloop.h" 2 | #include "log.h" 3 | #include "connection.h" 4 | #include "tnet.h" 5 | #include "sockutil.h" 6 | #include "address.h" 7 | 8 | using namespace std; 9 | using namespace tnet; 10 | 11 | int i = 0; 12 | 13 | void onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context) 14 | { 15 | switch(event) 16 | { 17 | case Conn_ReadEvent: 18 | { 19 | const StackBuffer* buffer = (const StackBuffer*)(context); 20 | LOG_INFO("echo %s", string(buffer->buffer, buffer->count).c_str()); 21 | 22 | char buf[1024]; 23 | int n = snprintf(buf, sizeof(buf), "hello world %d", i); 24 | conn->send(string(buf, n)); 25 | 26 | if(++i > 10) 27 | { 28 | conn->shutDown(); 29 | } 30 | } 31 | break; 32 | case Conn_CloseEvent: 33 | LOG_INFO("close"); 34 | break; 35 | case Conn_ErrorEvent: 36 | LOG_INFO("error"); 37 | break; 38 | case Conn_ConnectEvent: 39 | LOG_INFO("connect"); 40 | conn->send("hello world"); 41 | break; 42 | default: 43 | break; 44 | } 45 | } 46 | 47 | int main() 48 | { 49 | IOLoop loop; 50 | 51 | int fd = SockUtil::create(); 52 | 53 | ConnectionPtr_t conn = std::make_shared(&loop, fd); 54 | 55 | conn->setEventCallback(std::bind(&onConnEvent, _1, _2, _3)); 56 | 57 | conn->connect(Address(11181)); 58 | 59 | loop.start(); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /test/echoserver_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tnet.h" 4 | #include "tcpserver.h" 5 | #include "log.h" 6 | #include "connection.h" 7 | #include "address.h" 8 | 9 | using namespace std; 10 | using namespace tnet; 11 | 12 | void onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context) 13 | { 14 | switch(event) 15 | { 16 | case Conn_ReadEvent: 17 | { 18 | const StackBuffer* buffer = static_cast(context); 19 | conn->send(string(buffer->buffer, buffer->count)); 20 | } 21 | break; 22 | default: 23 | break; 24 | } 25 | } 26 | 27 | int main() 28 | { 29 | TcpServer s; 30 | s.listen(Address(11181), std::bind(&onConnEvent, _1, _2, _3)); 31 | 32 | s.start(1); 33 | 34 | LOG_INFO("test over"); 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /test/httpclient_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "httpclient.h" 4 | 5 | #include "httprequest.h" 6 | #include "httpresponse.h" 7 | #include "ioloop.h" 8 | 9 | using namespace std; 10 | using namespace tnet; 11 | 12 | void onResponse(IOLoop* loop, const HttpResponse& resp) 13 | { 14 | cout << resp.statusCode << endl; 15 | 16 | Headers_t::const_iterator iter = resp.headers.begin(); 17 | 18 | while(iter != resp.headers.end()) 19 | { 20 | cout << iter->first << ": " << iter->second << endl; 21 | ++iter; 22 | } 23 | 24 | cout << resp.body.size() << endl << endl; 25 | 26 | loop->stop(); 27 | } 28 | 29 | int main() 30 | { 31 | IOLoop loop; 32 | 33 | HttpClientPtr_t client = std::make_shared(&loop); 34 | 35 | client->request("http://127.0.0.1:11181/abc", std::bind(&onResponse, &loop, _1)); 36 | 37 | loop.start(); 38 | 39 | cout << "exit" << endl; 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /test/httpserver_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "log.h" 7 | 8 | #include "address.h" 9 | #include "tcpserver.h" 10 | 11 | #include "connection.h" 12 | 13 | #include "httpserver.h" 14 | #include "httprequest.h" 15 | #include "httpresponse.h" 16 | #include "httpconnection.h" 17 | 18 | using namespace std; 19 | using namespace tnet; 20 | using namespace std::placeholders; 21 | 22 | 23 | void onHandler(const HttpConnectionPtr_t& conn, const HttpRequest& request) 24 | { 25 | HttpResponse resp; 26 | resp.statusCode = 200; 27 | resp.setContentType("text/html"); 28 | resp.setKeepAlive(true); 29 | resp.enableDate(); 30 | 31 | resp.body.append("first"); 32 | //resp.body.append(1600, 'a'); 33 | resp.body.append("Hello World"); 34 | 35 | conn->send(resp); 36 | } 37 | 38 | 39 | int main() 40 | { 41 | Log::rootLog().setLevel(Log::ERROR); 42 | 43 | TcpServer s; 44 | 45 | HttpServer httpd(&s); 46 | 47 | httpd.setHttpCallback("/", std::bind(&onHandler, _1, _2)); 48 | 49 | httpd.listen(Address(11181)); 50 | 51 | LOG_INFO("start tcp server"); 52 | 53 | s.start(4); 54 | 55 | LOG_INFO("stop server"); 56 | 57 | return 0; 58 | } 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/notifier_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "notifier.h" 4 | #include "ioloop.h" 5 | #include "timer.h" 6 | #include "tnet.h" 7 | 8 | using namespace std; 9 | using namespace tnet; 10 | 11 | void onNotify(const NotifierPtr_t& notifier) 12 | { 13 | cout << "on notify" << endl; 14 | 15 | IOLoop* loop = notifier->loop(); 16 | 17 | notifier->stop(); 18 | 19 | loop->stop(); 20 | } 21 | 22 | void onTimer(const TimerPtr_t& timer, const NotifierPtr_t& notifier) 23 | { 24 | cout << "begin to notify" << endl; 25 | 26 | notifier->notify(); 27 | } 28 | 29 | void run(IOLoop* loop) 30 | { 31 | NotifierPtr_t notifier = std::make_shared(std::bind(&onNotify, _1)); 32 | TimerPtr_t timer = std::make_shared(std::bind(&onTimer, _1, notifier), 0, 5000); 33 | 34 | notifier->start(loop); 35 | timer->start(loop); 36 | } 37 | 38 | int main() 39 | { 40 | IOLoop loop; 41 | 42 | run(&loop); 43 | 44 | cout << "start" << endl; 45 | 46 | loop.start(); 47 | 48 | cout << "stop" << endl; 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /test/redisclient_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "redisclient.h" 6 | #include "ioloop.h" 7 | #include "log.h" 8 | #include "address.h" 9 | #include "redistrans.h" 10 | 11 | using namespace std; 12 | using namespace tnet; 13 | 14 | void getCallback(const RedisClientPtr_t& client, const RedisReply& reply) 15 | { 16 | cout << "getCallback" << endl; 17 | if(reply.err != 0) 18 | { 19 | cout << "redis error %d" << reply.err << endl; 20 | } 21 | else 22 | { 23 | cout << reply.reply->str << endl; 24 | } 25 | } 26 | 27 | void setCallback(const RedisClientPtr_t& client, const RedisReply& reply) 28 | { 29 | if(reply.err != 0) 30 | { 31 | cout << "redis error %d" << reply.err << endl; 32 | } 33 | else 34 | { 35 | client->exec({"GET", "key"}, std::bind(&getCallback, client, _1)); 36 | } 37 | } 38 | 39 | void onCommit(const RedisReply& r) 40 | { 41 | if(r.err != 0) 42 | { 43 | cout << "commit error " << r.err << endl; 44 | return; 45 | } 46 | 47 | struct redisReply* reply = r.reply; 48 | if(reply->type != REDIS_REPLY_ARRAY) 49 | { 50 | cout << "multi error" << endl; 51 | } 52 | 53 | cout << reply->elements << endl; 54 | 55 | for(size_t i = 0; i < reply->elements; ++i) 56 | { 57 | struct redisReply* elem = reply->element[i]; 58 | cout << elem->str << endl; 59 | } 60 | } 61 | 62 | void onNewTrans(const RedisTransPtr_t& trans, int status) 63 | { 64 | if(status != 0) 65 | { 66 | cout << "new trans error\t" << status << endl; 67 | return; 68 | } 69 | 70 | trans->begin(); 71 | 72 | trans->exec({"SET", "KEY1", "VALUE1"}); 73 | trans->exec({"SET", "KEY2", "VALUE2"}); 74 | trans->exec({"SET", "KEY3", "VALUE3"}); 75 | trans->exec({"GET", "KEY1"}); 76 | trans->exec({"GET", "KEY2"}); 77 | trans->exec({"GET", "KEY3"}); 78 | 79 | trans->commit(std::bind(&onCommit, _1)); 80 | } 81 | 82 | int main() 83 | { 84 | IOLoop loop; 85 | 86 | Address addr("127.0.0.1", 6379); 87 | RedisClientPtr_t client = std::make_shared(&loop, addr); 88 | 89 | client->exec({"SET", "key", "hello world"}, std::bind(&setCallback, client, _1)); 90 | 91 | client->newTrans(std::bind(&onNewTrans, _1, _2)); 92 | 93 | loop.start(); 94 | 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /test/signaler_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "signaler.h" 7 | #include "ioloop.h" 8 | #include "tnet.h" 9 | 10 | using namespace tnet; 11 | using namespace std; 12 | 13 | void onSignaler(const SignalerPtr_t& signaler, int signum) 14 | { 15 | cout << "signal:" << signum << endl; 16 | IOLoop* loop = signaler->loop(); 17 | switch(signum) 18 | { 19 | case SIGINT: 20 | case SIGTERM: 21 | loop->stop(); 22 | break; 23 | default: 24 | break; 25 | } 26 | } 27 | 28 | int main() 29 | { 30 | IOLoop loop; 31 | 32 | vector signums{SIGINT, SIGTERM}; 33 | SignalerPtr_t signaler = std::make_shared(signums, std::bind(&onSignaler, _1, _2)); 34 | 35 | signaler->start(&loop); 36 | 37 | cout << "start" << endl; 38 | 39 | loop.start(); 40 | 41 | cout << "end" << endl; 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /test/timer_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "timer.h" 5 | #include "ioloop.h" 6 | #include "tnet.h" 7 | 8 | using namespace std; 9 | using namespace tnet; 10 | 11 | int i = 0; 12 | 13 | void onTimer(const TimerPtr_t& timer) 14 | { 15 | cout << "onTimer\t" << time(0) << endl; 16 | if(++i >= 10) 17 | { 18 | IOLoop* loop = timer->loop(); 19 | 20 | timer->stop(); 21 | 22 | loop->stop(); 23 | } 24 | } 25 | 26 | void onOnceTimer(const TimerPtr_t& timer) 27 | { 28 | cout << "onOnceTimer" << endl; 29 | } 30 | 31 | void run(IOLoop* loop) 32 | { 33 | TimerPtr_t timer1 = std::make_shared(std::bind(&onTimer, _1), 1000, 1000); 34 | TimerPtr_t timer2 = std::make_shared(std::bind(&onOnceTimer, _1), 0, 5000); 35 | 36 | timer1->start(loop); 37 | timer2->start(loop); 38 | } 39 | 40 | int main() 41 | { 42 | 43 | IOLoop loop; 44 | 45 | run(&loop); 46 | 47 | cout << "start" << endl; 48 | 49 | loop.start(); 50 | 51 | cout << "end" << endl; 52 | 53 | return 0; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /test/timingwheel_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "timingwheel.h" 4 | #include "ioloop.h" 5 | #include "tnet.h" 6 | 7 | using namespace std; 8 | using namespace tnet; 9 | 10 | int i = 0; 11 | 12 | void onWheel(const TimingWheelPtr_t& wheel, int num) 13 | { 14 | i++; 15 | 16 | cout << i << "\t" << num << "\t" << "onwheel\t" << time(0) << endl; 17 | uint64_t h = wheel->add(std::bind(&onWheel, _1, num), num * 1000); 18 | 19 | if(i == 5) 20 | { 21 | wheel->update(h, num * 1000); 22 | } 23 | 24 | if(i == 6) 25 | { 26 | wheel->remove(h); 27 | wheel->add(std::bind(&onWheel, _1, num), num * 1000); 28 | } 29 | 30 | if(i > 10) 31 | { 32 | wheel->loop()->stop(); 33 | } 34 | } 35 | 36 | int main() 37 | { 38 | IOLoop loop; 39 | 40 | TimingWheelPtr_t t = std::make_shared(1000, 20); 41 | 42 | t->add(std::bind(&onWheel, _1, 1), 1000); 43 | t->add(std::bind(&onWheel, _1, 2), 2000); 44 | 45 | t->start(&loop); 46 | 47 | cout << "start" << endl; 48 | loop.start(); 49 | 50 | t->stop(); 51 | 52 | cout << "end" << endl; 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /test/wsclient_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "wsclient.h" 4 | #include "ioloop.h" 5 | #include "wsconnection.h" 6 | #include "log.h" 7 | 8 | using namespace std; 9 | using namespace tnet; 10 | 11 | int i = 0; 12 | 13 | void onWriteComplete(int j) 14 | { 15 | LOG_INFO("write complete %d", j); 16 | } 17 | 18 | void onWsConnEvent(const WsConnectionPtr_t& conn, WsEvent event, const void* context) 19 | { 20 | switch(event) 21 | { 22 | case Ws_OpenEvent: 23 | LOG_INFO("open"); 24 | conn->send("Hello world"); 25 | break; 26 | case Ws_CloseEvent: 27 | LOG_INFO("close"); 28 | break; 29 | case Ws_MessageEvent: 30 | { 31 | const string& msg = *(const string*)context; 32 | 33 | LOG_INFO("message %s", msg.c_str()); 34 | 35 | char buf[1024]; 36 | int n = snprintf(buf, sizeof(buf), "Hello World %d", i++); 37 | 38 | conn->send(string(buf, n), std::bind(&onWriteComplete, i)); 39 | 40 | if(i > 10) 41 | { 42 | conn->close(); 43 | } 44 | 45 | } 46 | break; 47 | case Ws_ErrorEvent: 48 | LOG_INFO("error"); 49 | break; 50 | default: 51 | break; 52 | } 53 | } 54 | 55 | int main() 56 | { 57 | IOLoop loop; 58 | WsClientPtr_t client = std::make_shared(&loop); 59 | 60 | client->connect("ws://127.0.0.1:11181/push/ws", std::bind(&onWsConnEvent, _1, _2, _3)); 61 | 62 | loop.start(); 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /test/wsserver_test.cpp: -------------------------------------------------------------------------------- 1 | #include "tnet_http.h" 2 | #include "httpserver.h" 3 | #include "tcpserver.h" 4 | 5 | #include "log.h" 6 | #include "address.h" 7 | #include "connection.h" 8 | #include "wsconnection.h" 9 | 10 | using namespace std; 11 | using namespace tnet; 12 | 13 | void onWsCallback(const WsConnectionPtr_t& conn, WsEvent event, const void* context) 14 | { 15 | switch(event) 16 | { 17 | case Ws_OpenEvent: 18 | LOG_INFO("websocket open"); 19 | break; 20 | case Ws_CloseEvent: 21 | LOG_INFO("websocket close"); 22 | break; 23 | case Ws_MessageEvent: 24 | { 25 | const string& str = *(const string*)context; 26 | LOG_INFO("websocket message %s", str.c_str()); 27 | conn->send("hello " + str); 28 | } 29 | break; 30 | case Ws_PongEvent: 31 | LOG_INFO("websocket pong"); 32 | break; 33 | case Ws_ErrorEvent: 34 | LOG_INFO("websocket error"); 35 | break; 36 | } 37 | } 38 | 39 | int main() 40 | { 41 | TcpServer s; 42 | 43 | HttpServer httpd(&s); 44 | 45 | httpd.setWsCallback("/push/ws", std::bind(&onWsCallback, _1, _2, _3)); 46 | 47 | httpd.listen(Address(11181)); 48 | 49 | s.start(); 50 | 51 | return 0; 52 | } 53 | 54 | --------------------------------------------------------------------------------