├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── conf └── tinyco.json ├── dns ├── dns_resolve.h ├── dns_resolve_impl.cc └── dns_resolve_impl.h ├── example ├── dns │ └── main.cc ├── http_client │ └── main.cc ├── http_server │ └── main.cc ├── rpc │ ├── CMakeLists.txt │ ├── README.md │ ├── client.cc │ ├── server.cc │ └── service.proto ├── server │ ├── CMakeLists.txt │ ├── conf │ │ └── tinyco.json │ └── main.cc ├── udp_client │ └── main.cc └── udp_server │ └── main.cc ├── frame.cc ├── frame.h ├── http ├── http_client.cc ├── http_client.h ├── http_op.cc ├── http_op.h ├── http_request.cc ├── http_request.h ├── http_response.cc ├── http_response.h ├── http_server.cc ├── http_server.h ├── http_tool.cc ├── http_tool.h ├── https_client.cc ├── https_client.h ├── tcp_conn.cc └── tcp_conn.h ├── listener.cc ├── listener.h ├── mutex.cc ├── mutex.h ├── rpc ├── channel.cc ├── channel.h ├── controller.cc ├── controller.h ├── rpc_impl.cc └── rpc_impl.h ├── server.cc ├── server.h ├── static └── img │ └── tinyco_perf_8c.jpg ├── thread.cc ├── thread.h ├── util ├── defer.h ├── log.cc ├── log.h ├── network.cc ├── network.h ├── setproctitle.c ├── string.cc ├── string.h └── time.h └── work.h /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third-party/http-parser"] 2 | path = third-party/http-parser 3 | url = https://github.com/nodejs/http-parser.git 4 | [submodule "third-party/libevent"] 5 | path = third-party/libevent 6 | url = https://github.com/libevent/libevent.git 7 | [submodule "third-party/c-ares"] 8 | path = third-party/c-ares 9 | url = https://github.com/c-ares/c-ares.git 10 | [submodule "third-party/jsoncpp"] 11 | path = third-party/jsoncpp 12 | url = https://github.com/open-source-parsers/jsoncpp.git 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: g++ 3 | os: linux 4 | addons: 5 | apt: 6 | sources: 7 | - ubuntu-toolchain-r-test 8 | - george-edison55-precise-backports 9 | packages: 10 | - g++-6 11 | - cmake-data 12 | - cmake 13 | - libcurl4-gnutls-dev 14 | before_install: 15 | - export CXX="g++-6" 16 | - wget https://github.com/google/protobuf/releases/download/v3.3.0/protobuf-cpp-3.3.0.tar.gz 17 | - tar -xzvf protobuf-cpp-3.3.0.tar.gz 18 | - pushd protobuf-3.3.0 && ./configure --prefix=/usr && make && sudo make install && popd 19 | - cd ./third-party/c-ares/; cmake -DCARES_STATIC=ON .; make; cd - 20 | - cd ./third-party/libevent/; cmake .; make; cd - 21 | - cd ./third-party/jsoncpp/; cmake .; make; cd - 22 | install: 23 | script: cmake . && make 24 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.9) 2 | set(CMAKE_VERBOSE_MAKEFILE on) 3 | SET(PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | SET(MYFLAG "-g -D__STDC_FORMAT_MACROS") 6 | add_definitions(${MYFLAG}) 7 | 8 | file(GLOB TINYCO_CORE_CC *.cc ./util/*.cc ./util/*.c) 9 | file(GLOB TINYCO_HTTP_CC 10 | ./http/*.cc 11 | ./dns/*.cc 12 | ./rpc/*.cc 13 | ./third-party/http-parser/http_parser.c) 14 | 15 | SET(COMMON_STATIC_LIB 16 | ${PROJECT_DIR}/third-party/libevent/lib/libevent.a 17 | ${PROJECT_DIR}/third-party/c-ares/lib/libcares_static.a 18 | ${PROJECT_DIR}/third-party/jsoncpp/src/lib_json/libjsoncpp.a) 19 | 20 | SET(COMMON_SHARE_LIB 21 | -lrt z libssl.so libcrypto.so libcurl.so 22 | libprotobuf.a) 23 | 24 | project (tinyco) 25 | 26 | add_library(tinyco SHARED 27 | ${TINYCO_CORE_CC} 28 | ${TINYCO_HTTP_CC} 29 | ${COMMON_STATIC_LIB}) 30 | 31 | add_library(tinyco_static STATIC 32 | ${TINYCO_CORE_CC} 33 | ${TINYCO_HTTP_CC}) 34 | 35 | add_executable(udp_client 36 | ./example/udp_client/main.cc) 37 | 38 | add_executable(udp_server 39 | ./example/udp_server/main.cc) 40 | 41 | add_executable(http_client 42 | ./example/http_client/main.cc) 43 | 44 | add_executable(http_server 45 | ./example/http_server/main.cc) 46 | 47 | add_executable(dns_test 48 | ./example/dns/main.cc) 49 | 50 | add_dependencies(udp_client tinyco_static) 51 | add_dependencies(udp_server tinyco_static) 52 | add_dependencies(http_client tinyco_static) 53 | add_dependencies(http_server tinyco_static) 54 | add_dependencies(dns_test tinyco_static) 55 | 56 | include_directories( 57 | ./ 58 | ./third-party/http-parser/ 59 | ./third-party/libevent/include/ 60 | ./third-party/c-ares/ 61 | ./third-party/jsoncpp/include/) 62 | 63 | target_link_libraries(udp_client 64 | ${PROJECT_DIR}/libtinyco_static.a 65 | ${COMMON_STATIC_LIB} 66 | ${COMMON_SHARE_LIB}) 67 | 68 | target_link_libraries(udp_server 69 | ${PROJECT_DIR}/libtinyco_static.a 70 | ${COMMON_STATIC_LIB} 71 | ${COMMON_SHARE_LIB}) 72 | 73 | target_link_libraries(http_server 74 | ${PROJECT_DIR}/libtinyco_static.a 75 | ${COMMON_STATIC_LIB} 76 | ${COMMON_SHARE_LIB}) 77 | 78 | target_link_libraries(http_client 79 | ${PROJECT_DIR}/libtinyco_static.a 80 | ${COMMON_STATIC_LIB} 81 | ${COMMON_SHARE_LIB}) 82 | 83 | target_link_libraries(dns_test 84 | ${PROJECT_DIR}/libtinyco_static.a 85 | ${COMMON_STATIC_LIB} 86 | ${COMMON_SHARE_LIB}) 87 | 88 | add_custom_target(thirdparty COMMAND cd ./third-party/c-ares/ && cmake -DCARES_STATIC=ON && make && cd - 89 | COMMAND cd ./third-party/libevent/ && cmake . && make && cd - 90 | COMMAND cd ./third-party/jsoncpp/ && cmake . && make && cd -) 91 | 92 | # install 93 | install(TARGETS tinyco tinyco_static 94 | LIBRARY DESTINATION lib/tinyco 95 | ARCHIVE DESTINATION lib/tinyco) 96 | 97 | file(GLOB HEADERS *.h) 98 | file(GLOB UTIL_HEADERS util/*.h) 99 | file(GLOB HTTP_HEADERS http/*.h) 100 | file(GLOB DNS_HEADERS dns/*.h) 101 | file(GLOB RPC_HEADERS rpc/*.h) 102 | install(FILES ${HEADERS} DESTINATION include/tinyco) 103 | install(FILES ${UTIL_HEADERS} DESTINATION include/tinyco/util) 104 | install(FILES ${HTTP_HEADERS} DESTINATION include/tinyco/http) 105 | install(FILES ${DNS_HEADERS} DESTINATION include/tinyco/dns) 106 | install(FILES ${RPC_HEADERS} DESTINATION include/tinyco/rpc) 107 | install(FILES ${COMMON_STATIC_LIB} DESTINATION /usr/local/lib/tinyco) 108 | 109 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, The tinyco Authors 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tinyco 2 | [![Build Status](https://travis-ci.org/daoluan/tinyco.svg?branch=master)](https://travis-ci.org/daoluan/tinyco) 3 | 4 | tinyco is a framework with **high performance and high efficiency** focusing on business with which you can build a server in only couple lines of code. It use coroutine implemented in ucontext syscall. 5 | 6 | # Features 7 | - High performance. Make full use of CPU and process requests concurrently which makes server's throughput greatly improved 8 | - Perfect http/dns components 9 | - Small kernel in size 10 | - Business code can be easy to read and maintained. No more async framework 11 | 12 | ![!](https://github.com/daoluan/tinyco/blob/master/static/img/tinyco_perf_8c.jpg?raw=true) 13 | 14 | # How to start ? 15 | Clone tinyco and build it: 16 | 17 | % git clone https://github.com/daoluan/tinyco.git 18 | % cd tinyco 19 | % git submodule update --init --recursive 20 | 21 | % cmake . && make thirdparty && make && make install 22 | 23 | It's very easy to DIY your tinyco-server. You can refer to [this](https://github.com/daoluan/tinyco/tree/master/example/server). 24 | 25 | For more usages, Read [here](https://github.com/daoluan/tinyco/tree/master/example). 26 | 27 | # refs 28 | 29 | - [how-to-solve-thundering-herd-in-tinyco](https://daoluan.github.io/%E7%BC%96%E7%A8%8B%E5%B0%8F%E8%AE%B0/2017/08/16/how-to-solve-thundering-herd-in-tinyco.html) 30 | - [how-to-DIY-your-tinyco-server](https://daoluan.github.io/%E7%BC%96%E7%A8%8B%E5%B0%8F%E8%AE%B0/2017/09/02/how-to-DIY-your-tinyco-server.html) 31 | -------------------------------------------------------------------------------- /conf/tinyco.json: -------------------------------------------------------------------------------- 1 | { 2 | "udp": [ 3 | { 4 | "listen": "eth1:12345" 5 | } 6 | ], 7 | "tcp": [ 8 | { 9 | "listen": "eth1:8080" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /dns/dns_resolve.h: -------------------------------------------------------------------------------- 1 | #ifndef DNS_RESOLVE_H_ 2 | #define DNS_RESOLVE_H_ 3 | 4 | // abstract class for dns resolving. 5 | // you can implement your Resolve function by inheriting DNSResolver 6 | #include 7 | #include 8 | 9 | #include "util/network.h" 10 | 11 | namespace tinyco { 12 | 13 | namespace dns { 14 | 15 | class DNSResolver { 16 | public: 17 | DNSResolver() {} 18 | virtual ~DNSResolver() {} 19 | 20 | virtual bool Resolve(const std::string &domain, network::IP *ip) = 0; 21 | }; 22 | } 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /dns/dns_resolve_impl.cc: -------------------------------------------------------------------------------- 1 | #include "dns_resolve_impl.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // ares 8 | #include "ares.h" 9 | #include "nameser.h" 10 | // run "cmake ." in c-ares to generate ares_config.h 11 | #include "ares_config.h" 12 | #include "ares_private.h" 13 | 14 | #include "frame.h" 15 | 16 | namespace tinyco { 17 | namespace dns { 18 | std::unordered_map 19 | DNSResolverImpl::res_cache_; 20 | 21 | // not thread-safe 22 | static ares_channel channel; 23 | static bool ares_channel_init = false; 24 | 25 | bool DNSResolverImpl::Resolve(const std::string &domain, network::IP *ip) { 26 | network::IP res; 27 | res.af_inet_ip = 0; 28 | int ret = 0; 29 | 30 | if (!ares_channel_init) { 31 | struct ares_options options; 32 | int optmask = 0; 33 | 34 | ret = ares_library_init(ARES_LIB_INIT_ALL); 35 | if (ret != ARES_SUCCESS) { 36 | return false; 37 | } 38 | 39 | ret = ares_init_options(&channel, &options, optmask); 40 | if (ret != ARES_SUCCESS) { 41 | return false; 42 | } 43 | 44 | ares_channel_init = true; 45 | } 46 | 47 | // check cache 48 | auto c = res_cache_.find(domain); 49 | if (c != res_cache_.end() && c->second.timeout > time::mstime() / 1000llu) { 50 | *ip = c->second.PickOne(); 51 | return true; 52 | } 53 | 54 | // resolve 55 | unsigned char *buf; 56 | std::string rsp; 57 | int buflen = 0; 58 | ret = ares_mkquery(domain.c_str(), ns_c_in, T_A, 1234, 0, &buf, &buflen); 59 | if (ret != 0) { 60 | return false; 61 | } 62 | 63 | if (channel->nservers <= 0) return false; 64 | in_addr dnsip = (channel->servers[0].addr.addr.addr4); 65 | 66 | struct sockaddr_in addr; 67 | addr.sin_addr = dnsip; 68 | addr.sin_port = htons(53); 69 | addr.sin_family = AF_INET; 70 | 71 | std::string req; 72 | req.assign(reinterpret_cast(buf), buflen); 73 | ret = Frame::UdpSendAndRecv(req, addr, &rsp); 74 | if (ret < 0) { 75 | return false; 76 | } 77 | 78 | hostent *ht; 79 | const unsigned char *p = reinterpret_cast(rsp.data()); 80 | ret = ares_parse_a_reply(p, rsp.size(), &ht, NULL, NULL); 81 | if (!ht) { 82 | return false; 83 | } 84 | 85 | auto &n = res_cache_[domain]; 86 | n.timeout = time::mstime() / 1000llu + 5 * 60; 87 | for (auto i = 0; ht->h_addr_list[i]; ++i) { 88 | res.af_inet_ip = *reinterpret_cast(ht->h_addr_list[i]); 89 | n.ip.push_back(res); 90 | } 91 | 92 | *ip = n.PickOne(); 93 | return true; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /dns/dns_resolve_impl.h: -------------------------------------------------------------------------------- 1 | #ifndef DNS_RESOLVE_IMPL_H_ 2 | #define DNS_RESOLVE_IMPL_H_ 3 | 4 | #include "dns_resolve.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "util/network.h" 11 | 12 | namespace tinyco { 13 | namespace dns { 14 | // not thread-safe 15 | class DNSResolverImpl : public DNSResolver { 16 | public: 17 | DNSResolverImpl() {} 18 | virtual ~DNSResolverImpl() {} 19 | virtual bool Resolve(const std::string &domain, network::IP *ip); 20 | 21 | private: 22 | struct ResCache { 23 | std::vector ip; 24 | uint32_t timeout; // timeout unix timestmap 25 | uint32_t idx; // return next time 26 | 27 | network::IP PickOne() { 28 | network::IP res; 29 | res.af_inet_ip = 0; 30 | if (ip.empty()) return res; 31 | 32 | return ip[(idx++) % ip.size()]; 33 | } 34 | }; 35 | 36 | private: 37 | static std::unordered_map res_cache_; 38 | }; 39 | } 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /example/dns/main.cc: -------------------------------------------------------------------------------- 1 | #include "frame.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "dns/dns_resolve_impl.h" 10 | 11 | using namespace tinyco; 12 | 13 | class TestWork : public Work { 14 | public: 15 | TestWork() {} 16 | virtual ~TestWork() {} 17 | 18 | int Run() { 19 | const std::vector domains = {"www.baidu.com", "www.qq.com", 20 | "www.taobao.com", "daoluan.net"}; 21 | dns::DNSResolverImpl dri; 22 | network::IP ip; 23 | for (auto i = 0; i < domains.size(); i++) { 24 | auto bret = dri.Resolve(domains[i], &ip); 25 | if (bret) 26 | LOG("%s->%s", domains[i].c_str(), inet_ntoa(in_addr{ip.af_inet_ip})); 27 | else 28 | LOG("%s reslove error", domains[i].c_str()); 29 | } 30 | 31 | // use cache 32 | LOG("## use dns cache ##"); 33 | for (auto i = 0; i < domains.size(); i++) { 34 | auto bret = dri.Resolve(domains[i], &ip); 35 | if (bret) 36 | LOG("%s->%s", domains[i].c_str(), inet_ntoa(in_addr{ip.af_inet_ip})); 37 | else 38 | LOG("%s reslove error", domains[i].c_str()); 39 | } 40 | return 0; 41 | } 42 | }; 43 | 44 | int main() { 45 | assert(Frame::Init()); 46 | 47 | auto t = Frame::CreateThread(new TestWork); 48 | while (!t.IsDead()) { 49 | Frame::Sleep(1000); 50 | } 51 | 52 | Frame::Fini(); 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /example/http_client/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "server.h" 4 | #include "http/http_client.h" 5 | #include "http/http_request.h" 6 | #include "http/https_client.h" 7 | #include "http/http_op.h" 8 | #include "dns/dns_resolve_impl.h" 9 | 10 | using namespace tinyco; 11 | class TestWork : public Work { 12 | public: 13 | int Run() { 14 | network::IP ip; 15 | dns::DNSResolverImpl dri; 16 | const std::string &domain = "baidu.com"; 17 | auto bret = dri.Resolve(domain, &ip); 18 | if (!bret) { 19 | LOG("dns resolve error, check your dns config"); 20 | return 0; 21 | } 22 | 23 | LOG("test http..."); 24 | 25 | http::HttpClient hc; 26 | sockaddr_in addr; 27 | addr.sin_addr.s_addr = ip.af_inet_ip; 28 | 29 | int ret = hc.Init(addr.sin_addr.s_addr, 80); 30 | if (ret < 0) { 31 | LOG("ret = %d|%s", ret, hc.GetErrorMsg().c_str()); 32 | return 0; 33 | } 34 | 35 | http::HttpRequest hr; 36 | hr.SetMethod(http::HttpRequest::HTTP_REQUEST_METHOD_GET); 37 | hr.SetUri("/"); 38 | hr.SetHeader("Host", domain); 39 | std::string req, rsp; 40 | hr.SerializeToString(&req); 41 | 42 | LOG("http request=%s", req.c_str()); 43 | ret = hc.SendAndReceive(req.c_str(), req.size(), &rsp); 44 | if (ret < 0) { 45 | LOG("ret = %d|%s", ret, hc.GetErrorMsg().c_str()); 46 | return 0; 47 | } 48 | LOG("http response=%s", rsp.c_str()); 49 | 50 | LOG("test https..."); 51 | http::HttpsClient hsc; 52 | addr.sin_addr.s_addr = ip.af_inet_ip; 53 | ret = hsc.Init(addr.sin_addr.s_addr, 443); 54 | if (ret < 0) { 55 | LOG("ret=%d|%s", ret, hsc.GetErrorMsg().c_str()); 56 | return 0; 57 | } 58 | 59 | LOG("https request=%s", req.c_str()); 60 | ret = hsc.SendAndReceive(req.c_str(), req.size(), &rsp); 61 | if (ret < 0) { 62 | LOG("ret=%d|%s", ret, hsc.GetErrorMsg().c_str()); 63 | return 0; 64 | } 65 | LOG("https response = %s", rsp.c_str()); 66 | 67 | LOG("test http op"); 68 | std::map extra_headers; 69 | http::HttpResponse hrsp; 70 | ret = http::HttpOp::HttpGet("https://www.baidu.com", extra_headers, &hrsp); 71 | if (ret < 0) 72 | LOG("HttpGet error: ret=%d", ret); 73 | else 74 | LOG("new intf: ret=%d|%s", ret, hrsp.SerializeToString().c_str()); 75 | return 0; 76 | } 77 | }; 78 | 79 | int main() { 80 | assert(Frame::Init()); 81 | 82 | auto t = Frame::CreateThread(new TestWork); 83 | while (!t.IsDead()) { 84 | Frame::Sleep(1000); 85 | } 86 | 87 | Frame::Fini(); 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /example/http_server/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "http/http_server.h" 4 | 5 | using namespace tinyco; 6 | 7 | class TestWork : public http::HttpSrvWork { 8 | public: 9 | virtual int Serve() { 10 | LOG("ready to reply"); 11 | hrsp_.SetStatus(200); 12 | hrsp_.SetContent("hello world!"); 13 | Reply(); 14 | 15 | return 0; 16 | } 17 | }; 18 | 19 | class MyBuilder : public BusinessWorkBuilder { 20 | public: 21 | virtual TcpReqWork *BuildStreamBusinessWork(uint32_t port) { 22 | return new TestWork(); 23 | } 24 | virtual UdpReqWork *BuildUdpBusinessWork(uint32_t port) { return NULL; } 25 | }; 26 | 27 | int main() { 28 | TcpSrv(0, 8080, new MyBuilder()); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /example/rpc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.9) 2 | # set(CMAKE_VERBOSE_MAKEFILE on) 3 | SET(PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | SET(MYFLAG "-g -D__STDC_FORMAT_MACROS") 6 | add_definitions(${MYFLAG}) 7 | 8 | SET(COMMON_STATIC_LIB 9 | libevent.a 10 | libcares_static.a 11 | libjsoncpp.a) 12 | 13 | SET(COMMON_SHARE_LIB 14 | -lrt z libssl.so libcrypto.so libcurl.so libprotobuf.a) 15 | 16 | project (rpc) 17 | 18 | # you can change include path and library path if necessary 19 | link_directories(/usr/local/lib/tinyco/) 20 | 21 | add_executable(server server.cc service.pb.cc) 22 | add_executable(client client.cc service.pb.cc) 23 | 24 | include_directories(/usr/local/include/tinyco) 25 | target_link_libraries(server 26 | libtinyco_static.a 27 | ${COMMON_STATIC_LIB} 28 | ${COMMON_SHARE_LIB}) 29 | target_link_libraries(client 30 | libtinyco_static.a 31 | ${COMMON_STATIC_LIB} 32 | ${COMMON_SHARE_LIB}) 33 | -------------------------------------------------------------------------------- /example/rpc/README.md: -------------------------------------------------------------------------------- 1 | This is a simple example of building a rpc server. 2 | -------------------------------------------------------------------------------- /example/rpc/client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "server.h" 4 | #include "rpc/controller.h" 5 | #include "rpc/channel.h" 6 | #include "service.pb.h" 7 | #include "util/log.h" 8 | 9 | using namespace tinyco; 10 | 11 | class TestWork : public Work { 12 | public: 13 | int Run() { 14 | Channel channel; 15 | Controller ctrl; 16 | 17 | sockaddr_in addr = {0}; 18 | addr.sin_family = AF_INET; 19 | inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); 20 | 21 | channel.InitServer( 22 | network::EndPoint(network::IP{addr.sin_addr.s_addr}, 8080)); 23 | 24 | ctrl.set_connect_timoeut(1000); 25 | ctrl.set_recv_timoeut(2000); 26 | ctrl.http_requeset_header().SetUri("/v1/queue/stop"); 27 | 28 | MyRequest request; 29 | request.set_content("hello"); 30 | MyResponse response; 31 | 32 | channel.CallMethod(NULL, &ctrl, &request, &response, NULL); 33 | if (ctrl.error_code() == 0) { 34 | LOG("CallMethod ret=%d|response=%s", ctrl.error_code(), 35 | response.DebugString().c_str()); 36 | } else { 37 | LOG("CallMethod error_code=%d", ctrl.error_code()); 38 | } 39 | 40 | return 0; 41 | } 42 | }; 43 | 44 | int main(int argc, char* argv[]) { 45 | assert(Frame::Init()); 46 | Frame::CreateThread(new TestWork); 47 | Frame::Schedule(); 48 | Frame::Fini(); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /example/rpc/server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "server.h" 4 | #include "rpc/rpc_impl.h" 5 | #include "service.pb.h" 6 | 7 | using namespace tinyco; 8 | 9 | // implement your rpc service's business 10 | class HttpServiceImpl : public Service { 11 | public: 12 | virtual void Foo(::google::protobuf::RpcController* controller, 13 | const ::MyRequest* request, ::MyResponse* response, 14 | ::google::protobuf::Closure* done) { 15 | response->set_content("world"); 16 | } 17 | }; 18 | 19 | class MyServer : public ServerImpl { 20 | public: 21 | virtual TcpReqWork* BuildStreamBusinessWork(uint32_t port) { 22 | LOG("my work builder: %d", port); 23 | // when new requet comes to the port, new rpc object with method map 24 | if (8080 == port) return new tinyco::TinycoRpc(method_map_); 25 | return NULL; 26 | } 27 | 28 | virtual UdpReqWork* BuildUdpBusinessWork(uint32_t port) { return NULL; } 29 | }; 30 | 31 | int main(int argc, char* argv[]) { 32 | std::shared_ptr srv(new MyServer); 33 | if (srv->Initialize(argc, argv) < 0) { 34 | LOG_ERROR("fail to initialize server"); 35 | return -1; 36 | } 37 | 38 | // install service 39 | if (!srv->AddRpcService(new HttpServiceImpl(), 40 | "/v1/queue/start > Foo," 41 | "/v1/queue/stop > Foo," 42 | "/v1/queue/stats/ > Foo")) { 43 | LOG_ERROR("fail to add rpc service"); 44 | return -1; 45 | } 46 | 47 | srv->Run(); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /example/rpc/service.proto: -------------------------------------------------------------------------------- 1 | 2 | syntax = "proto2"; 3 | option cc_generic_services = true; 4 | message MyRequest { optional bytes content = 1; } 5 | 6 | message MyResponse { optional bytes content = 1; } 7 | service Service { rpc Foo(MyRequest) returns(MyResponse); } 8 | -------------------------------------------------------------------------------- /example/server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.9) 2 | set(CMAKE_VERBOSE_MAKEFILE on) 3 | SET(PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | SET(MYFLAG "-g -D__STDC_FORMAT_MACROS") 6 | add_definitions(${MYFLAG}) 7 | 8 | SET(COMMON_STATIC_LIB 9 | libevent.a 10 | libcares_static.a 11 | libjsoncpp.a 12 | libprotobuf.a) 13 | 14 | SET(COMMON_SHARE_LIB 15 | -lrt z libssl.so libcrypto.so libcurl.so) 16 | 17 | project (server) 18 | 19 | # you can change include path and library path if necessary 20 | link_directories(/usr/local/lib/tinyco/) 21 | 22 | add_executable(server main.cc) 23 | include_directories(/usr/local/include/tinyco) 24 | target_link_libraries(server 25 | libtinyco_static.a 26 | ${COMMON_STATIC_LIB} 27 | ${COMMON_SHARE_LIB}) 28 | -------------------------------------------------------------------------------- /example/server/conf/tinyco.json: -------------------------------------------------------------------------------- 1 | { 2 | "udp": [ 3 | { 4 | "listen": "eth1:12345" 5 | } 6 | ], 7 | "tcp": [ 8 | { 9 | "listen": "eth1:8080" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /example/server/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "server.h" 4 | #include "http/http_server.h" 5 | 6 | #include 7 | 8 | using namespace tinyco; 9 | 10 | class MyUdpReqWork : public UdpReqWork { 11 | public: 12 | int Run() { 13 | LOG("new udp req: %s|response after 10s", req_.reqpkg.c_str()); 14 | 15 | Frame::Sleep(10000); 16 | 17 | LOG("rsp to client"); 18 | Reply(req_.reqpkg); 19 | } 20 | }; 21 | 22 | class MyHttpReqWork : public http::HttpSrvWork { 23 | public: 24 | virtual int Serve() { 25 | LOG("ready to reply"); 26 | hrsp_.SetStatus(200); 27 | hrsp_.SetContent("hello world!"); 28 | Reply(); 29 | 30 | return 0; 31 | } 32 | }; 33 | 34 | class MyServer : public ServerImpl { 35 | public: 36 | virtual TcpReqWork *BuildStreamBusinessWork(uint32_t port) { 37 | LOG("my work builder: %d", port); 38 | if (8080 == port) return new MyHttpReqWork(); 39 | return NULL; 40 | } 41 | 42 | virtual UdpReqWork *BuildUdpBusinessWork(uint32_t port) { 43 | LOG("my work builder: %d", port); 44 | if (123456 == port) return new MyUdpReqWork(); 45 | return NULL; 46 | } 47 | }; 48 | 49 | int main(int argc, char *argv[]) { 50 | std::shared_ptr srv(new MyServer); 51 | if (srv->Initialize(argc, argv) < 0) { 52 | LOG_ERROR("fail to initialize server"); 53 | return -1; 54 | } 55 | 56 | srv->Run(); 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /example/udp_client/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "server.h" 5 | 6 | using namespace tinyco; 7 | class TestWork : public Work { 8 | public: 9 | TestWork() {} 10 | virtual ~TestWork() {} 11 | 12 | int Run() { 13 | sockaddr_in addr = {0}; 14 | addr.sin_family = AF_INET; 15 | inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); 16 | addr.sin_port = htons(32000); 17 | 18 | std::string sendbuf = "hello"; 19 | 20 | LOG("sleep 1 second"); 21 | Frame::Sleep(1000); 22 | LOG("sleep 2 second"); 23 | Frame::Sleep(2000); 24 | 25 | LOG("client send pkg to udp server and receive the same content"); 26 | LOG("in order to test, udp server will wait for 1 second after receiving " 27 | "pkg from client"); 28 | LOG("send content: %s", sendbuf.c_str()); 29 | std::string recvbuf; 30 | int ret = Frame::UdpSendAndRecv(sendbuf, addr, &recvbuf); 31 | if (ret < 0) { 32 | LOG("UdpSendAndRecv error: ret=%d", ret); 33 | return -1; 34 | } 35 | 36 | LOG("recv content: %s", recvbuf.c_str()); 37 | 38 | return 0; 39 | } 40 | }; 41 | 42 | int main() { 43 | assert(Frame::Init()); 44 | 45 | auto t = Frame::CreateThread(new TestWork); 46 | while (!t.IsDead()) { 47 | Frame::Sleep(1000); 48 | } 49 | 50 | Frame::Fini(); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /example/udp_server/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "server.h" 4 | 5 | using namespace tinyco; 6 | 7 | class TestWork : public UdpReqWork { 8 | public: 9 | TestWork() {} 10 | virtual ~TestWork() {} 11 | 12 | int Run() { 13 | LOG("new udp req: %s|response after 10s", req_.reqpkg.c_str()); 14 | 15 | Frame::Sleep(10000); 16 | 17 | LOG("rsp to client"); 18 | Reply(req_.reqpkg); 19 | 20 | return 0; 21 | } 22 | }; 23 | 24 | class MyBuilder : public BusinessWorkBuilder { 25 | public: 26 | virtual UdpReqWork *BuildUdpBusinessWork(uint32_t port) { 27 | return new TestWork(); 28 | } 29 | virtual TcpReqWork *BuildStreamBusinessWork(uint32_t port) { return NULL; } 30 | }; 31 | 32 | int main() { 33 | UdpSrv(0, 32000, new MyBuilder()); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /frame.cc: -------------------------------------------------------------------------------- 1 | #include "frame.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "event2/event.h" 15 | #include "util/time.h" 16 | 17 | namespace tinyco { 18 | 19 | std::unordered_map Frame::io_wait_map_; // wait on io 20 | std::list Frame::thread_runnable_; 21 | std::list Frame::thread_free_; // like memory pool 22 | std::vector Frame::thread_pending_; // sleeping thread 23 | Thread *Frame::main_thread_ = NULL; 24 | Thread *Frame::running_thread_ = NULL; 25 | struct event_base *Frame::base_ = NULL; 26 | uint64_t Frame::last_loop_ts_ = 0; 27 | 28 | struct ThreadPendingTimeComp { 29 | bool operator()(Thread *&a, Thread *&b) const { 30 | return a->GetWakeupTime() > b->GetWakeupTime(); 31 | } 32 | }; 33 | 34 | bool Frame::Init() { 35 | // avoid duplicate initialization 36 | if (main_thread_ || base_) return false; 37 | 38 | auto m = new Thread; 39 | m->Init(); 40 | m->SetContext(MainThreadLoop, NULL); 41 | main_thread_ = m; 42 | 43 | // initialize own thread 44 | running_thread_ = new Thread(); 45 | if (!running_thread_->Init()) { 46 | return false; 47 | } 48 | 49 | for (auto i = 0; i < 1000; i++) { 50 | thread_free_.push_back(new Thread()); 51 | } 52 | 53 | running_thread_->Schedule(); 54 | 55 | base_ = NULL; 56 | return true; 57 | } 58 | 59 | bool Frame::Fini() { 60 | delete running_thread_; 61 | running_thread_ = NULL; 62 | 63 | delete main_thread_; 64 | main_thread_ = NULL; 65 | 66 | for (auto it = thread_runnable_.begin(); it != thread_runnable_.end(); it++) { 67 | delete (*it); 68 | } 69 | 70 | for (auto it = thread_free_.begin(); it != thread_free_.end(); it++) { 71 | delete (*it); 72 | } 73 | 74 | for (auto it = io_wait_map_.begin(); it != io_wait_map_.end();) { 75 | delete (((it++)->second)); 76 | } 77 | 78 | if (base_) event_base_free(base_); 79 | return true; 80 | } 81 | 82 | int Frame::MainThreadLoop(void *arg) { 83 | struct timeval timeout; 84 | while (true) { 85 | UpdateLoopTimestamp(); 86 | auto timeout = GetEventLoopTimeout(); 87 | EventLoop(timeout); 88 | WakeupPendingThread(); 89 | Schedule(); 90 | } 91 | } 92 | 93 | Thread *Frame::PopPendingTop() { 94 | std::pop_heap(thread_pending_.begin(), thread_pending_.end(), 95 | ThreadPendingTimeComp()); 96 | auto t = *(thread_pending_.end() - 1); 97 | thread_pending_.pop_back(); 98 | return t; 99 | } 100 | 101 | void Frame::WakeupPendingThread() { 102 | auto nowms = GetLastLoopTimestamp(); 103 | if (!thread_pending_.empty()) { 104 | auto n = *thread_pending_.begin(); 105 | auto sz = thread_pending_.size(); 106 | while (sz-- && n->GetWakeupTime() <= nowms) { 107 | n = PopPendingTop(); 108 | n->Pending(0); 109 | n->SetState(Thread::TS_RUNNABLE); 110 | thread_runnable_.push_back(n); 111 | 112 | n = *thread_pending_.begin(); 113 | } 114 | } 115 | } 116 | 117 | timeval Frame::GetEventLoopTimeout() { 118 | auto nowms = GetLastLoopTimestamp(); 119 | struct timeval timeout = {0, 100000}; 120 | if (!thread_pending_.empty()) { 121 | auto mint = *thread_pending_.begin(); 122 | 123 | // thread need to be wake up 124 | if (mint->GetWakeupTime() < nowms) { 125 | timeout.tv_usec = 0; 126 | return timeout; 127 | } 128 | 129 | int delta = mint->GetWakeupTime() - nowms; 130 | timeout.tv_sec = delta / 1000llu; 131 | timeout.tv_usec = (delta - timeout.tv_sec * 1000llu) * 1000llu; 132 | } 133 | return timeout; 134 | } 135 | 136 | int Frame::EventLoop(const timeval &tv) { 137 | event_base_loopexit(GetEventBase(), &tv); 138 | return event_base_loop(GetEventBase(), EVLOOP_ONCE); 139 | } 140 | 141 | int Frame::UdpSendAndRecv(const std::string &sendbuf, 142 | struct sockaddr_in &dest_addr, std::string *recvbuf) { 143 | int ret = 0; 144 | char recvbuf_[65536] = {0}; 145 | auto recvlen = sizeof(recvbuf_); 146 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 147 | 148 | ret = network::SetNonBlock(fd); 149 | if (ret < 0) { 150 | return -1; 151 | } 152 | 153 | if ((ret = sendto(fd, sendbuf.c_str(), sendbuf.size(), 0, 154 | (struct sockaddr *)&dest_addr, sizeof(dest_addr))) < 0) { 155 | return -1; 156 | } 157 | 158 | socklen_t sockaddr_len = sizeof(dest_addr); 159 | if ((ret = recvfrom(fd, recvbuf_, sizeof(recvbuf_), 0, 160 | (struct sockaddr *)&dest_addr, &sockaddr_len)) < 0) { 161 | if (errno == EAGAIN || errno == EWOULDBLOCK) 162 | return -2; 163 | else 164 | return -2; 165 | } 166 | 167 | if (ret > 0) { 168 | recvbuf->assign(recvbuf_, ret); 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | int Frame::TcpSendAndRecv(const void *sendbuf, size_t sendlen, void *recvbuf, 175 | size_t *recvlen, IsCompleteBase *is_complete) { 176 | int nsend = 0; 177 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 178 | if (network::SetNonBlock(fd) < 0) { 179 | return -1; 180 | } 181 | 182 | while (nsend == sendlen) { 183 | int ret = send(fd, static_cast(sendbuf) + nsend, 184 | sendlen - nsend, 0); 185 | if (ret > 0) { 186 | nsend += ret; 187 | } 188 | if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { 189 | continue; 190 | } else if (ret < 0) { 191 | return -1; 192 | } else if (0 == ret) { 193 | return -1; 194 | } 195 | } 196 | 197 | size_t recvd = 0; 198 | while (true) { 199 | int ret = recv(fd, recvbuf, *recvlen, 0); 200 | if (ret > 0) { 201 | recvd += ret; 202 | // ok 203 | if (is_complete->CheckPkg(static_cast(recvbuf), recvd) == 0) { 204 | *recvlen = recvd; 205 | return 0; 206 | } 207 | 208 | continue; 209 | } else if (0 == ret) { // reset by peer 210 | return -2; 211 | } else if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { 212 | continue; 213 | } else { 214 | return -2; 215 | } 216 | } 217 | return 0; 218 | } 219 | 220 | int Frame::accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { 221 | int fd = -1; 222 | struct event ev; 223 | while (fd < 0) { 224 | fd = ::accept(sockfd, addr, addrlen); 225 | if (fd < 0) { 226 | if (errno == EINTR) continue; 227 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 228 | 229 | event_assign(&ev, GetEventBase(), sockfd, EV_READ, SocketReadOrWrite, 230 | &ev); 231 | event_add(&ev, NULL); 232 | io_wait_map_[sockfd] = running_thread_; 233 | running_thread_->Schedule(); 234 | running_thread_->SetState(Thread::TS_STOP); 235 | Schedule(); 236 | } else { 237 | return -1; 238 | } 239 | } 240 | } 241 | 242 | return fd; 243 | } 244 | 245 | int Frame::connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { 246 | struct event ev; 247 | int ret = 0; 248 | while ((ret = ::connect(sockfd, addr, addrlen)) < 0) { 249 | if (errno == EINTR) continue; 250 | if (EINPROGRESS == errno || EADDRINUSE == errno) { 251 | event_assign(&ev, GetEventBase(), sockfd, EV_WRITE | EV_READ, 252 | SocketReadOrWrite, &ev); 253 | event_add(&ev, NULL); // add timeout 254 | io_wait_map_[sockfd] = running_thread_; 255 | running_thread_->Schedule(); 256 | running_thread_->SetState(Thread::TS_STOP); 257 | Schedule(); 258 | 259 | if (ev.ev_res & EV_TIMEOUT) { 260 | return -1; 261 | } else if (ev.ev_res != EV_WRITE) { 262 | return -1; 263 | } 264 | 265 | int err = 0, n = 0; 266 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&err, 267 | (socklen_t *)&n) < 0) { 268 | return -1; 269 | } 270 | 271 | if (err) { 272 | errno = err; 273 | return -1; 274 | } 275 | 276 | break; 277 | } else { 278 | return -1; 279 | } 280 | } 281 | 282 | return 0; 283 | } 284 | 285 | int Frame::send(int sockfd, const void *buf, size_t len, int flags) { 286 | struct event ev; 287 | size_t nsend = 0; 288 | while (nsend < len) { 289 | int ret = 290 | ::send(sockfd, static_cast(buf) + nsend, len - nsend, 0); 291 | if (ret > 0) { 292 | nsend += ret; 293 | } 294 | if (ret < 0 && errno == EINTR) { 295 | continue; 296 | } else if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { 297 | event_assign(&ev, GetEventBase(), sockfd, EV_WRITE, SocketReadOrWrite, 298 | &ev); 299 | event_add(&ev, NULL); 300 | io_wait_map_[sockfd] = running_thread_; 301 | running_thread_->Schedule(); 302 | running_thread_->SetState(Thread::TS_STOP); 303 | Schedule(); 304 | 305 | if (ev.ev_res & EV_TIMEOUT) { 306 | return -2; 307 | break; 308 | } else if (ev.ev_res & EV_WRITE) { 309 | continue; 310 | } 311 | return -1; 312 | } else if (ret < 0) { 313 | return -1; 314 | } else if (0 == ret) { 315 | return 0; // close by peer 316 | } 317 | } 318 | 319 | return nsend; 320 | } 321 | 322 | int Frame::recv(int sockfd, void *buf, size_t len, int flags) { 323 | size_t recvd = 0; 324 | struct event ev; 325 | int ret = 0; 326 | while (true) { 327 | ret = ::recv(sockfd, buf, len, flags); 328 | if (ret > 0) { 329 | recvd += ret; 330 | return recvd; 331 | } else if (0 == ret) { // reset by peer 332 | return 0; 333 | } else if (ret < 0) { 334 | if (errno == EINTR) continue; 335 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 336 | event_assign(&ev, GetEventBase(), sockfd, EV_READ | EV_CLOSED, 337 | SocketReadOrWrite, &ev); 338 | event_add(&ev, NULL); 339 | io_wait_map_[sockfd] = running_thread_; 340 | running_thread_->Schedule(); 341 | running_thread_->SetState(Thread::TS_STOP); 342 | Schedule(); 343 | 344 | if (ev.ev_res & EV_TIMEOUT) { 345 | return -2; // return timeout errcode 346 | } else if (ev.ev_res & EV_CLOSED) { 347 | return 0; 348 | } else if (ev.ev_res & EV_READ) { 349 | continue; 350 | } 351 | } 352 | 353 | break; 354 | } 355 | } 356 | 357 | return ret; 358 | } 359 | 360 | int Frame::sendto(int sockfd, const void *buf, size_t len, int flags, 361 | const struct sockaddr *dest_addr, socklen_t addrlen) { 362 | return ::sendto(sockfd, buf, len, flags, dest_addr, addrlen); 363 | } 364 | 365 | ssize_t Frame::recvfrom(int sockfd, void *buf, size_t len, int flags, 366 | struct sockaddr *src_addr, socklen_t *addrlen) { 367 | struct event ev; 368 | int ret = 0; 369 | while (true) { 370 | int ret = 371 | ::recvfrom(sockfd, buf, len, 0, (struct sockaddr *)src_addr, addrlen); 372 | if (ret > 0) { 373 | return ret; 374 | } else if (0 == ret) { 375 | return 0; 376 | } else if (ret < 0) { 377 | if (errno == EINTR) continue; 378 | if (errno == EAGAIN || errno == EWOULDBLOCK) { 379 | event_assign(&ev, GetEventBase(), sockfd, EV_READ | EV_CLOSED, 380 | SocketReadOrWrite, &ev); 381 | event_add(&ev, NULL); 382 | io_wait_map_[sockfd] = running_thread_; 383 | running_thread_->Schedule(); 384 | running_thread_->SetState(Thread::TS_STOP); 385 | Schedule(); 386 | 387 | if (ev.ev_res & EV_TIMEOUT) { 388 | return -2; // return timeout errcode 389 | } else if (ev.ev_res & EV_CLOSED) { 390 | return 0; 391 | } else if (ev.ev_res & EV_READ) { 392 | continue; 393 | } 394 | } 395 | 396 | break; 397 | } 398 | } 399 | 400 | return ret; 401 | } 402 | 403 | void Frame::SocketReadOrWrite(int fd, short events, void *arg) { 404 | int ret = event_del(static_cast(arg)); 405 | 406 | thread_runnable_.push_back(io_wait_map_[fd]); 407 | io_wait_map_.erase(fd); 408 | event_base_loopbreak(Frame::base_); 409 | } 410 | 411 | int Frame::HandleProcess(void *arg) { 412 | auto *w = static_cast(arg); 413 | LOG_DEBUG("run ret=%d", w->Run()); 414 | delete w; 415 | 416 | // recycle the thread 417 | Frame::RecycleRunningThread(); 418 | Frame::Schedule(); 419 | return 0; 420 | } 421 | 422 | void Frame::RecycleRunningThread() { 423 | running_thread_->SetState(Thread::TS_DEAD); 424 | thread_free_.push_back(running_thread_); 425 | running_thread_ = NULL; 426 | } 427 | 428 | Thread *Frame::AllocNewThread() { 429 | if (thread_free_.empty()) return new Thread; 430 | Thread *t = *(--thread_free_.end()); 431 | thread_free_.pop_back(); 432 | 433 | t->SetState(Thread::TS_STOP); 434 | return t; 435 | } 436 | 437 | ThreadWrapper Frame::CreateThread(Work *w) { 438 | // work will be deleted by HandleProcess() 439 | // thread should not be deleted before deleting work 440 | auto *t = AllocNewThread(); 441 | if (!t) return ThreadWrapper(NULL); 442 | 443 | t->Init(); 444 | t->SetContext(HandleProcess, w); 445 | thread_runnable_.push_back(t); 446 | 447 | return ThreadWrapper(t); 448 | } 449 | 450 | int Frame::Schedule() { 451 | if (thread_runnable_.empty()) { 452 | main_thread_->RestoreContext(); 453 | } else { 454 | running_thread_ = *(thread_runnable_.begin()); 455 | thread_runnable_.pop_front(); 456 | running_thread_->RestoreContext(); 457 | } 458 | return 0; 459 | } 460 | 461 | void Frame::Sleep(uint32_t ms) { 462 | if (running_thread_) { 463 | running_thread_->Pending(time::mstime() + ms); 464 | running_thread_->SetState(Thread::TS_STOP); 465 | PendThread(running_thread_); 466 | running_thread_->Schedule(); 467 | running_thread_ = NULL; 468 | } 469 | 470 | Schedule(); 471 | } 472 | 473 | void Frame::PendThread(Thread *t) { 474 | thread_pending_.push_back(t); 475 | std::push_heap(thread_pending_.begin(), thread_pending_.end(), 476 | ThreadPendingTimeComp()); 477 | } 478 | 479 | void Frame::UpdateLoopTimestamp() { last_loop_ts_ = time::mstime(); } 480 | } 481 | -------------------------------------------------------------------------------- /frame.h: -------------------------------------------------------------------------------- 1 | #ifndef FRAME_H_ 2 | #define FRAME_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "thread.h" 13 | #include "work.h" 14 | #include "mutex.h" 15 | #include "util/defer.h" 16 | #include "util/time.h" 17 | #include "util/log.h" 18 | #include "listener.h" 19 | 20 | struct event_base; 21 | namespace tinyco { 22 | 23 | class IsCompleteBase { 24 | public: 25 | IsCompleteBase() {} 26 | virtual ~IsCompleteBase() {} 27 | 28 | virtual int CheckPkg(const char *buffer, uint32_t buffer_len) { return 0; } 29 | }; 30 | 31 | class Frame { 32 | public: 33 | static bool Init(); 34 | static bool Fini(); 35 | 36 | public: 37 | static int UdpSendAndRecv(const std::string &sendbuf, 38 | struct sockaddr_in &dest_addr, 39 | std::string *recvbuf); 40 | static int TcpSendAndRecv(const void *sendbuf, size_t sendlen, void *recvbuf, 41 | size_t *recvlen, IsCompleteBase *is_complete); 42 | static int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 43 | static int connect(int sockfd, const struct sockaddr *addr, 44 | socklen_t addrlen); 45 | static int send(int sockfd, const void *buf, size_t len, int flags); 46 | static int recv(int sockfd, void *buf, size_t len, int flags); 47 | static int sendto(int sockfd, const void *buf, size_t len, int flags, 48 | const struct sockaddr *dest_addr, socklen_t addrlen); 49 | static ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 50 | struct sockaddr *src_addr, socklen_t *addrlen); 51 | 52 | static ThreadWrapper CreateThread(Work *w); 53 | static void Sleep(uint32_t ms); 54 | static void RecycleRunningThread(); 55 | 56 | // it's very useful before mainloop of your program 57 | static Thread *InitHereAsNewThread() { 58 | auto t = new Thread(); 59 | t->Init(); 60 | t->Schedule(); 61 | running_thread_ = t; 62 | } 63 | 64 | private: 65 | static int Schedule(); 66 | static Thread *AllocNewThread(); 67 | static void SocketReadOrWrite(int fd, short events, void *arg); 68 | static int MainThreadLoop(void *arg); 69 | static void PendThread(Thread *t); 70 | static Thread *PopPendingTop(); 71 | static void WakeupPendingThread(); 72 | static timeval GetEventLoopTimeout(); 73 | static int EventLoop(const timeval &tv); 74 | static void UpdateLoopTimestamp(); 75 | static uint64_t GetLastLoopTimestamp() { return last_loop_ts_; } 76 | static event_base *GetEventBase() { 77 | if (base_) return base_; 78 | return (base_ = event_base_new()); 79 | } 80 | 81 | static std::unordered_map io_wait_map_; 82 | static std::list thread_runnable_; 83 | static std::list thread_free_; 84 | static std::vector thread_pending_; 85 | static Thread *main_thread_; 86 | static Thread *running_thread_; 87 | static struct event_base *base_; 88 | static int HandleProcess(void *arg); 89 | 90 | private: 91 | Frame() {} 92 | virtual ~Frame() {} 93 | 94 | static uint64_t last_loop_ts_; 95 | }; 96 | } 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /http/http_client.cc: -------------------------------------------------------------------------------- 1 | #include "http_client.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "http_parser.h" 7 | #include "http_tool.h" 8 | 9 | namespace tinyco { 10 | namespace http { 11 | 12 | HttpClient::HttpClient() 13 | : ms_connect_timeout_(kMsConnectTimeout), 14 | ms_read_timeout_(kMsReadTimeout), 15 | ms_write_timeout_(kMsWriteTimeout) {} 16 | 17 | HttpClient::~HttpClient() { Close(); } 18 | 19 | int HttpClient::Init(uint32_t ip, uint16_t port) { 20 | if (0 == ip || 0 == port) { 21 | return -1; 22 | } 23 | 24 | int ret = 0; 25 | if ((ret = this->TcpConn::Init(ip, port, ms_connect_timeout_, 26 | ms_read_timeout_, ms_write_timeout_)) < 0) { 27 | return -2; 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | int HttpClient::SendAndReceive(const char *sendbuf, size_t sendlen, 34 | char *recvbuf, size_t *recvlen) { 35 | if (NULL == sendbuf || 0 == sendlen || NULL == recvbuf || NULL == recvlen) { 36 | return -1; 37 | } 38 | 39 | std::string str_recvbuf; 40 | int ret = this->TcpConn::SendAndReceive(sendbuf, sendlen, &str_recvbuf, 41 | HttpCheckComplete); 42 | if (ret < 0) { 43 | return -2; 44 | } 45 | if (*recvlen < str_recvbuf.size()) return -3; 46 | 47 | memcpy(recvbuf, str_recvbuf.c_str(), str_recvbuf.size()); 48 | *recvlen = str_recvbuf.size(); 49 | return ret; 50 | } 51 | 52 | int HttpClient::SendAndReceive(const char *sendbuf, size_t sendlen, 53 | std::string *recvbuf) { 54 | if (NULL == recvbuf) return -1; 55 | return this->TcpConn::SendAndReceive(sendbuf, sendlen, recvbuf, 56 | HttpCheckComplete); 57 | } 58 | 59 | int HttpClient::HttpCheckComplete(void *data, size_t size) { 60 | tinyco::http::HttpRequest http_request; 61 | std::string httpbuf = std::string(static_cast(data), size); 62 | tinyco::http::HttpParserImpl parser; 63 | int ret = parser.CheckHttp(httpbuf); 64 | if (ret == tinyco::http::HttpParserImpl::HPC_OK) 65 | return size; 66 | else if (ret == 67 | http::HttpParserImpl::HPC_NOT_HTTP) // not http, clear recvbuf_ 68 | return 0; 69 | else if (ret == http::HttpParserImpl::HPC_NOT_COMPLETE_HTTP) 70 | return CHECK_INCOMPLETE; 71 | else 72 | return CHECK_ERROR; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /http/http_client.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_CLIENT_H_ 2 | #define HTTP_CLIENT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tcp_conn.h" 10 | 11 | namespace tinyco { 12 | namespace http { 13 | 14 | // HttpClient hc; 15 | // hc.Init(ip, port); 16 | // hc.SendAndReceive(...); 17 | 18 | class HttpClient : public TcpConn { 19 | public: 20 | HttpClient(); 21 | virtual ~HttpClient(); 22 | 23 | std::string GetErrorMsg() { 24 | std::string _e_ = strerr_; 25 | strerr_.clear(); 26 | return _e_; 27 | } 28 | 29 | int Init(uint32_t ip, uint16_t port); 30 | int SendAndReceive(const char *sendbuf, size_t sendlen, uint32_t *status, 31 | char *recvbuf, size_t *recvlen); 32 | int SendAndReceive(const char *sendbuf, size_t sendlen, std::string *recvbuf); 33 | int SendAndReceive(const char *sendbuf, size_t sendlen, char *recvbuf, 34 | size_t *recvlen); 35 | 36 | void SetConnectMsTimeout(int timeout) { 37 | if (timeout < 0) 38 | ms_connect_timeout_ = kMsConnectTimeout; 39 | else 40 | ms_connect_timeout_ = timeout; 41 | } 42 | 43 | void SetReadMsTimeout(int timeout) { 44 | if (timeout < 0) 45 | ms_read_timeout_ = kMsReadTimeout; 46 | else 47 | ms_read_timeout_ = timeout; 48 | } 49 | 50 | void SetWriteMsTimeout(int timeout) { 51 | if (timeout < 0) 52 | ms_write_timeout_ = kMsWriteTimeout; 53 | else 54 | ms_write_timeout_ = timeout; 55 | } 56 | 57 | private: 58 | static int HttpCheckComplete(void *data, size_t size); 59 | 60 | public: 61 | static const int kMsConnectTimeout = 1000; 62 | static const int kMsReadTimeout = 5000; 63 | static const int kMsWriteTimeout = 500; 64 | 65 | private: 66 | std::string strerr_; 67 | 68 | int ms_connect_timeout_; 69 | int ms_read_timeout_; 70 | int ms_write_timeout_; 71 | }; 72 | } 73 | } 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /http/http_op.cc: -------------------------------------------------------------------------------- 1 | #include "http_op.h" 2 | 3 | #include 4 | 5 | #include "dns/dns_resolve_impl.h" 6 | 7 | namespace tinyco { 8 | namespace http { 9 | 10 | int HttpOp::HttpGet(const std::string &url, 11 | const std::map &extra_headers, 12 | tinyco::http::HttpResponse *hr) { 13 | return HttpGet(new DNSResolverImpl(), url, extra_headers, hr); 14 | } 15 | 16 | int HttpOp::HttpPost(const std::string &url, 17 | const std::map &extra_headers, 18 | const std::string &content, 19 | tinyco::http::HttpResponse *hr) { 20 | 21 | return HttpPost(new DNSResolverImpl(), url, extra_headers, content, hr); 22 | } 23 | 24 | int HttpOp::__HttpRequest(uint32_t ip, uint16_t port, 25 | const tinyco::http::HttpRequest &http_request, 26 | tinyco::http::HttpResponse *http_response) { 27 | std::string sendbuf, recvbuf; 28 | http_request.SerializeToString(&sendbuf); 29 | 30 | int ret = EC_NONE; 31 | if ((ret = __HttpSendAndReveive(ip, port, sendbuf, &recvbuf)) < 0) { 32 | return ret; 33 | } 34 | 35 | tinyco::http::HttpParserImpl parser; 36 | if ((ret = parser.ParseHttpResponse(recvbuf, http_response)) < 0) { 37 | return EC_RESPONSE_ERROR; 38 | } 39 | 40 | return ret; 41 | } 42 | 43 | int HttpOp::__HttpsRequest(uint32_t ip, uint16_t port, 44 | const tinyco::http::HttpRequest &http_request, 45 | tinyco::http::HttpResponse *http_response) { 46 | std::string sendbuf, recvbuf; 47 | http_request.SerializeToString(&sendbuf); 48 | 49 | int ret = EC_NONE; 50 | if ((ret = __HttpsSendAndReveive(ip, port, sendbuf, &recvbuf)) < 0) { 51 | return ret; 52 | } 53 | 54 | tinyco::http::HttpParserImpl parser; 55 | if ((ret = parser.ParseHttpResponse(recvbuf, http_response)) < 0) { 56 | return EC_RESPONSE_ERROR; 57 | } 58 | 59 | return ret; 60 | } 61 | 62 | int HttpOp::__HttpSendAndReveive(uint32_t ip, uint32_t port, 63 | const std::string &sendbuf, 64 | std::string *recvbuf) { 65 | HttpClient hc; 66 | 67 | int ret = EC_NONE; 68 | if ((ret = hc.Init(ip, port)) < 0) { 69 | return EC_INIT_ERROR; 70 | } 71 | 72 | if ((ret = hc.SendAndReceive(sendbuf.c_str(), sendbuf.size(), recvbuf)) < 0) { 73 | return EC_SEND_AND_RECEIVE_ERROR; 74 | } 75 | 76 | return ret; 77 | } 78 | 79 | int HttpOp::__HttpsSendAndReveive(uint32_t ip, uint32_t port, 80 | const std::string &sendbuf, 81 | std::string *recvbuf) { 82 | HttpsClient hc; 83 | int ret = EC_NONE; 84 | if ((ret = hc.Init(ip, port)) < 0) { 85 | return EC_INIT_ERROR; 86 | } 87 | 88 | if ((ret = hc.SendAndReceive(sendbuf.c_str(), sendbuf.size(), recvbuf)) < 0) { 89 | return EC_SEND_AND_RECEIVE_ERROR; 90 | } 91 | 92 | return ret; 93 | } 94 | 95 | int HttpOp::HttpGet(DNSResolver *dns_resolver, const std::string &url, 96 | const std::map &extra_headers, 97 | tinyco::http::HttpResponse *hr) { 98 | int ret = 0; 99 | tinyco::http::URL urlobj; 100 | HttpParserImpl hpi; 101 | 102 | std::string fixed_url; 103 | if (url.compare(0, sizeof("http://") - 1, "http://") != 0 && 104 | url.compare(0, sizeof("https://") - 1, "https://") != 0) 105 | fixed_url = "http://" + url; 106 | else 107 | fixed_url = std::move(url); 108 | 109 | if (hpi.ParseUrl(fixed_url, &urlobj) < 0) { 110 | return EC_UNKNOWN; 111 | } 112 | 113 | network::IP ip; 114 | if (!dns_resolver->Resolve(urlobj.host.c_str(), &ip)) { 115 | return EC_DNS_RESOLVE_ERROR; 116 | } 117 | 118 | tinyco::http::HttpRequest http_request; 119 | http_request.SetHeader("Host", urlobj.host); 120 | http_request.SetMethod(tinyco::http::HttpRequest::HTTP_REQUEST_METHOD_GET); 121 | http_request.SetUri(urlobj.uri); 122 | 123 | for (auto &ite : extra_headers) { 124 | http_request.SetHeader(ite.first, ite.second); 125 | } 126 | 127 | return (!urlobj.IsHttps()) 128 | ? __HttpRequest(ip.af_inet_ip, urlobj.port, http_request, hr) 129 | : __HttpsRequest(ip.af_inet_ip, urlobj.port, http_request, hr); 130 | } 131 | 132 | int HttpOp::HttpPost(DNSResolver *dns_resolver, const std::string &url, 133 | const std::map &extra_headers, 134 | const std::string &content, 135 | tinyco::http::HttpResponse *hr) { 136 | int ret = 0; 137 | tinyco::http::URL urlobj; 138 | HttpParserImpl hpi; 139 | 140 | std::string fixed_url; 141 | if (url.compare(0, sizeof("http://") - 1, "http://") != 0 && 142 | url.compare(0, sizeof("https://") - 1, "https://") != 0) 143 | fixed_url = "http://" + url; 144 | else 145 | fixed_url = std::move(url); 146 | 147 | if (hpi.ParseUrl(fixed_url, &urlobj) < 0) { 148 | return EC_UNKNOWN; 149 | } 150 | 151 | network::IP ip; 152 | if (!dns_resolver->Resolve(urlobj.host.c_str(), &ip)) { 153 | return EC_DNS_RESOLVE_ERROR; 154 | } 155 | 156 | tinyco::http::HttpRequest http_request; 157 | http_request.SetMethod(tinyco::http::HttpRequest::HTTP_REQUEST_METHOD_POST); 158 | http_request.SetUri(urlobj.uri); 159 | http_request.SetHeader("Host", urlobj.host); 160 | http_request.SetContent(content); 161 | 162 | for (auto &ite : extra_headers) { 163 | http_request.SetHeader(ite.first, ite.second); 164 | } 165 | 166 | return (!urlobj.IsHttps()) 167 | ? __HttpRequest(ip.af_inet_ip, urlobj.port, http_request, hr) 168 | : __HttpsRequest(ip.af_inet_ip, urlobj.port, http_request, hr); 169 | } 170 | 171 | int HttpOp::HttpRequest(uint32_t net_ip, uint16_t port, 172 | const tinyco::http::HttpRequest &http_request, 173 | tinyco::http::HttpResponse *hr) { 174 | return __HttpRequest(net_ip, port, http_request, hr); 175 | } 176 | 177 | int HttpOp::HttpsRequest(uint32_t net_ip, uint16_t port, 178 | const tinyco::http::HttpRequest &http_request, 179 | tinyco::http::HttpResponse *hr) { 180 | return __HttpsRequest(net_ip, port, http_request, hr); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /http/http_op.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_OP_H_ 2 | #define HTTP_OP_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "http_tool.h" 8 | #include "http_client.h" 9 | #include "https_client.h" 10 | #include "http_request.h" 11 | #include "http_response.h" 12 | #include "dns/dns_resolve.h" 13 | 14 | using namespace tinyco::dns; 15 | 16 | namespace tinyco { 17 | namespace http { 18 | 19 | class HttpRequest; 20 | class HttpResponse; 21 | 22 | class HttpOp { 23 | public: 24 | enum ErrorCode { 25 | EC_NONE = 0, 26 | EC_DNS_RESOLVE_ERROR = -1, 27 | EC_INIT_ERROR = -2, 28 | EC_SEND_AND_RECEIVE_ERROR = -3, 29 | EC_RESPONSE_ERROR = -4, 30 | EC_UNKNOWN = -5, 31 | }; 32 | 33 | // interface without details 34 | static int HttpGet(const std::string &url, 35 | const std::map &extra_headers, 36 | tinyco::http::HttpResponse *hr); 37 | 38 | static int HttpPost(const std::string &url, 39 | const std::map &extra_headers, 40 | const std::string &content, 41 | tinyco::http::HttpResponse *hr); 42 | //// 43 | 44 | static int HttpGet(DNSResolver *dns_resolver, const std::string &url, 45 | const std::map &extra_headers, 46 | tinyco::http::HttpResponse *hr); 47 | 48 | static int HttpPost(DNSResolver *dns_resolver, const std::string &url, 49 | const std::map &extra_headers, 50 | const std::string &content, 51 | tinyco::http::HttpResponse *hr); 52 | 53 | static int HttpRequest(uint32_t net_ip, uint16_t port, 54 | const tinyco::http::HttpRequest &http_request, 55 | tinyco::http::HttpResponse *hr); 56 | 57 | static int HttpsRequest(uint32_t net_ip, uint16_t port, 58 | const tinyco::http::HttpRequest &http_request, 59 | tinyco::http::HttpResponse *hr); 60 | 61 | private: 62 | static int __HttpRequest(uint32_t ip, uint16_t port, 63 | const tinyco::http::HttpRequest &http_request, 64 | tinyco::http::HttpResponse *http_response); 65 | 66 | static int __HttpSendAndReveive(uint32_t ip, uint32_t port, 67 | const std::string &sendbuf, 68 | std::string *recvbuf); 69 | 70 | static int __HttpsRequest(uint32_t ip, uint16_t port, 71 | const tinyco::http::HttpRequest &http_request, 72 | tinyco::http::HttpResponse *http_response); 73 | 74 | static int __HttpsSendAndReveive(uint32_t ip, uint32_t port, 75 | const std::string &sendbuf, 76 | std::string *recvbuf); 77 | }; 78 | } 79 | } 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /http/http_request.cc: -------------------------------------------------------------------------------- 1 | #include "http_request.h" 2 | 3 | #include 4 | 5 | #define crlf "\r\n" 6 | 7 | namespace tinyco { 8 | namespace http { 9 | 10 | HttpRequest::HttpRequest() 11 | : method_(HTTP_REQUEST_METHOD_UNKNOWN), keepalive_(false) {} 12 | 13 | HttpRequest::~HttpRequest() {} 14 | 15 | void HttpRequest::SetUri(const std::string &uri) { this->struri_ = uri; } 16 | 17 | void HttpRequest::SetHeader(const std::string &field, 18 | const std::string &value) { 19 | headers_[field] = value; 20 | } 21 | 22 | void HttpRequest::UnsetHeader(const std::string &field) { 23 | headers_.erase(field); 24 | } 25 | 26 | void HttpRequest::SetContent(const std::string &content) { 27 | this->content_ = content; 28 | std::stringstream ss; 29 | ss << content.size(); 30 | headers_["Content-Length"] = ss.str(); 31 | } 32 | 33 | bool HttpRequest::SerializeToString(std::string *output) const { 34 | std::stringstream ss; 35 | 36 | // 暂时不支持 GET/POST 以外的其他方法 37 | switch (method_) { 38 | case HTTP_REQUEST_METHOD_GET: 39 | ss << "GET "; 40 | break; 41 | case HTTP_REQUEST_METHOD_POST: 42 | ss << "POST "; 43 | break; 44 | default: 45 | return false; 46 | } 47 | 48 | ss << struri_ << " "; 49 | ss << "HTTP/1.1" << crlf; 50 | 51 | std::unordered_map::const_iterator it, 52 | end = headers_.end(); 53 | 54 | for (it = headers_.begin(); it != end; ++it) { 55 | if (!it->second.empty()) ss << it->first << ": " << it->second << crlf; 56 | } 57 | 58 | ss << crlf; 59 | ss << content_; 60 | 61 | *output = ss.str(); 62 | return true; 63 | } 64 | 65 | std::string HttpRequest::SerializeToString() const { 66 | std::string str; 67 | SerializeToString(&str); 68 | return str; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /http/http_request.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_REQUEST_H_ 2 | #define HTTP_REQUEST_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tinyco { 9 | namespace http { 10 | 11 | struct URL { 12 | std::string schema; 13 | std::string host; 14 | uint32_t port; 15 | std::string uri; 16 | std::string path; 17 | std::string query; 18 | std::string fragment; 19 | std::string userinfo; 20 | std::unordered_map query_params; 21 | 22 | bool IsHttps() const { return 443 == port; } 23 | 24 | bool IsSet(const std::string ¶m) { 25 | return query_params.find(param) != query_params.end(); 26 | } 27 | }; 28 | 29 | class HttpRequest { 30 | public: 31 | HttpRequest(); 32 | ~HttpRequest(); 33 | void SetMethod(uint8_t method) { method_ = method; } 34 | void SetUri(const std::string &url); 35 | void SetHeader(const std::string &field, const std::string &value); 36 | int GetMethod() const { return method_; } 37 | std::string GetUri() const { return struri_; } 38 | void SetKeepAlive(bool b) { keepalive_ = b; } 39 | bool KeepAlive() const { return keepalive_; } 40 | URL &GetUriObj() { return uri_; } 41 | 42 | std::string GetHeader(const std::string &header) const { 43 | std::unordered_map::const_iterator cit = 44 | headers_.find(header); 45 | 46 | return cit != headers_.end() ? cit->second : std::string(""); 47 | } 48 | 49 | std::string GetContent() const { return content_; } 50 | void UnsetHeader(const std::string &field); 51 | void SetContent(const std::string &content); 52 | 53 | bool IsHeader(const std::string &header) const { 54 | return headers_.find(header) != headers_.end(); 55 | } 56 | 57 | bool SerializeToString(std::string *output) const; 58 | std::string SerializeToString() const; 59 | 60 | void Clear() { 61 | method_ = HTTP_REQUEST_METHOD_UNKNOWN; 62 | struri_.clear(); 63 | content_.clear(); 64 | headers_.clear(); 65 | } 66 | 67 | enum { 68 | HTTP_REQUEST_METHOD_UNKNOWN = -1, 69 | HTTP_REQUEST_METHOD_GET = 0, 70 | HTTP_REQUEST_METHOD_POST, 71 | }; 72 | 73 | private: 74 | uint8_t method_; 75 | std::string struri_; 76 | URL uri_; 77 | std::string content_; 78 | std::unordered_map headers_; 79 | bool keepalive_; 80 | }; 81 | } 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /http/http_response.cc: -------------------------------------------------------------------------------- 1 | #include "http_response.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace tinyco { 7 | namespace http { 8 | 9 | #define crlf "\r\n" 10 | 11 | HttpResponse::HttpResponse() { status = 200; } 12 | 13 | HttpResponse::HttpResponse(const std::string &content, 14 | const std::string &content_type, int status) { 15 | headers_["content_type"] = content_type; 16 | this->content = content; 17 | this->status = status; 18 | } 19 | 20 | HttpResponse::~HttpResponse() {} 21 | 22 | void HttpResponse::SetHeader(const std::string &field, 23 | const std::string &value) { 24 | headers_[field] = value; 25 | } 26 | 27 | void HttpResponse::UnsetHeader(const std::string &field) { 28 | headers_.erase(field); 29 | } 30 | 31 | void HttpResponse::SetStatus(int status) { this->status = status; } 32 | 33 | void HttpResponse::SetContent(const std::string &content) { 34 | this->content = content; 35 | std::stringstream ss; 36 | ss << content.size(); 37 | 38 | headers_["Content-Length"] = ss.str(); 39 | } 40 | 41 | bool HttpResponse::SerializeToString(std::string *output) const { 42 | bool restult = true; 43 | std::stringstream stream; 44 | 45 | stream << "HTTP/1.1 " << status << " "; 46 | switch (status) { 47 | case 200: 48 | stream << "OK"; 49 | break; 50 | case 400: 51 | stream << "Bad Request"; 52 | break; 53 | case 404: 54 | stream << "Not Found"; 55 | break; 56 | case 500: 57 | stream << "Internal Server Error"; 58 | break; 59 | default: 60 | break; 61 | } 62 | stream << crlf; 63 | 64 | std::unordered_map::const_iterator it, 65 | end = headers_.end(); 66 | for (it = headers_.begin(); it != end; ++it) { 67 | if (!it->second.empty()) stream << it->first << ": " << it->second << crlf; 68 | } 69 | 70 | stream << crlf; 71 | stream << content; 72 | 73 | *output = stream.str(); 74 | return restult; 75 | } 76 | 77 | std::string HttpResponse::SerializeToString() const { 78 | std::string str; 79 | SerializeToString(&str); 80 | return str; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /http/http_response.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_RESPONSE_ 2 | #define HTTP_RESPONSE_ 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyco { 8 | namespace http { 9 | 10 | class HttpResponse { 11 | public: 12 | HttpResponse(); 13 | HttpResponse(const std::string &content, const std::string &content_type, 14 | int status); 15 | ~HttpResponse(); 16 | void SetHeader(const std::string &field, const std::string &value); 17 | void UnsetHeader(const std::string &field); 18 | void SetStatus(int status); 19 | void SetContent(const std::string &content); 20 | int GetStatus() const { return status; } 21 | std::string GetContent() const { return content; } 22 | 23 | std::string GetHeader(const std::string &header) const { 24 | std::unordered_map::const_iterator cit = 25 | headers_.find(header); 26 | return cit != headers_.end() ? cit->second : std::string(""); 27 | } 28 | 29 | const std::unordered_map &GetHeaders() const { 30 | return headers_; 31 | } 32 | 33 | bool SerializeToString(std::string *output) const; 34 | std::string SerializeToString() const; 35 | 36 | void Clear() { 37 | status = 200; 38 | content.clear(); 39 | headers_.clear(); 40 | } 41 | 42 | private: 43 | int status; 44 | std::string content; 45 | std::unordered_map headers_; 46 | }; 47 | } 48 | } 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /http/http_server.cc: -------------------------------------------------------------------------------- 1 | #include "http_server.h" 2 | 3 | #include 4 | 5 | #include "frame.h" 6 | #include "http_tool.h" 7 | 8 | namespace tinyco { 9 | namespace http { 10 | 11 | class HttpCheckPkg : public IsCompleteBase { 12 | public: 13 | virtual int CheckPkg(const char* buffer, uint32_t buffer_len) { 14 | HttpParserImpl hpi; 15 | size_t real_len = 0; 16 | int ret = hpi.CheckHttp(buffer, buffer_len, &real_len); 17 | if (HttpParserImpl::HPC_OK == ret) 18 | return real_len; 19 | else if (HttpParserImpl::HPC_NOT_COMPLETE_HTTP == ret) 20 | return 0; 21 | else if (HttpParserImpl::HPC_NOT_HTTP == ret) 22 | return -1; 23 | return -1; 24 | } 25 | }; 26 | 27 | int HttpSrvWork::Run() { 28 | std::string req; 29 | req.resize(512); 30 | size_t recvd = 0; 31 | 32 | HttpCheckPkg hcp; 33 | 34 | while (true) { 35 | // grow up buffer size 36 | if (recvd == req.size()) { 37 | req.resize(req.size() * 2); 38 | } 39 | 40 | int ret = Frame::recv(sockfd_, const_cast(req.data()) + recvd, 41 | req.size() - recvd, 0); 42 | if (ret > 0) { 43 | recvd += ret; 44 | 45 | // check pkg 46 | int nparsed = hcp.CheckPkg(req.data(), recvd); 47 | if (nparsed > 0) { 48 | // process pkg 49 | HttpParserImpl hpi; 50 | ret = hpi.ParseHttpRequest(req.substr(0, nparsed), &hreq_); 51 | 52 | if (0 == ret) { 53 | Serve(); 54 | 55 | // move left request to front 56 | if (recvd > nparsed) { 57 | req = req.substr(nparsed); 58 | } else if (recvd == nparsed) { 59 | recvd = 0; 60 | } 61 | 62 | if (hreq_.KeepAlive()) { 63 | continue; 64 | } 65 | break; 66 | 67 | } else { // parse error 68 | LOG("fail to parse request: %d", ret); 69 | return -__LINE__; 70 | } 71 | } else if (0 == nparsed) { // continue to recv 72 | continue; 73 | } else if (nparsed < 0) { // not http 74 | return -__LINE__; 75 | } 76 | } else if (0 == ret) // close by peer 77 | return -__LINE__; 78 | else if (ret < 0) { // sys error! 79 | return -__LINE__; 80 | } 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | int HttpSrvWork::Reply() { 87 | std::string rsp; 88 | if (hreq_.IsHeader("Connection")) 89 | hrsp_.SetHeader("Connection", hreq_.GetHeader("Connection")); 90 | hrsp_.SerializeToString(&rsp); 91 | 92 | return Frame::send(sockfd_, rsp.data(), rsp.size(), 0); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /http/http_server.h: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | #include "http_request.h" 3 | #include "http_response.h" 4 | 5 | #include 6 | 7 | namespace tinyco { 8 | namespace http { 9 | 10 | class HttpSrvWork : public TcpReqWork { 11 | public: 12 | virtual int Run(); 13 | // implement you function 14 | virtual int Serve() = 0; 15 | virtual int Reply(); 16 | 17 | protected: 18 | HttpRequest hreq_; 19 | HttpResponse hrsp_; 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /http/http_tool.cc: -------------------------------------------------------------------------------- 1 | #include "http_tool.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "http_parser.h" 12 | #include "util/string.h" 13 | 14 | namespace tinyco { 15 | namespace http { 16 | 17 | template 18 | struct HttpParserParam { 19 | T *http_struct; 20 | std::vector headers; 21 | }; 22 | 23 | // Use template here inorder to reuse function definition. 24 | template 25 | static int header_field_cb(http_parser *parser, const char *at, size_t length) { 26 | if (NULL == parser->data) return 0; 27 | 28 | T *t = static_cast(parser->data); 29 | t->headers.push_back(std::string(at, length)); 30 | return 0; 31 | } 32 | 33 | template 34 | static int header_value_cb(http_parser *parser, const char *at, size_t length) { 35 | if (NULL == parser->data) return 0; 36 | 37 | T *t = static_cast(parser->data); 38 | t->headers.push_back(std::string(at, length)); 39 | return 0; 40 | } 41 | 42 | template 43 | static int request_url_cb(http_parser *parser, const char *at, size_t length) { 44 | if (NULL == parser->data) return 0; 45 | 46 | T *t = static_cast(parser->data); 47 | t->http_struct->SetUri(std::string(at, length)); 48 | HttpParserImpl hpi; 49 | hpi.ParseUrl(t->http_struct->GetUri(), &(t->http_struct->GetUriObj())); 50 | return 0; 51 | } 52 | 53 | template 54 | static int request_body_cb(http_parser *parser, const char *at, size_t length) { 55 | if (NULL == parser->data) return 0; 56 | 57 | T *t = static_cast(parser->data); 58 | t->http_struct->SetContent(std::string(at, length)); 59 | return 0; 60 | } 61 | 62 | template 63 | static int header_complete_cb(http_parser *parser) { 64 | return 2; 65 | } 66 | 67 | int HttpParserImpl::ParseHttpRequest(const std::string &httpbuf, 68 | HttpRequest *http_request) { 69 | if (!http_request) return 0; 70 | 71 | http_parser_settings settings = {0, 0, 0, 0, 0, 0, 0, 0}; 72 | http_parser parser; 73 | 74 | memset(&parser, 0, sizeof(parser)); 75 | http_parser_init(&parser, HTTP_REQUEST); 76 | HttpParserParam hpp; 77 | hpp.http_struct = http_request; 78 | parser.data = &hpp; 79 | settings.on_header_field = &header_field_cb >; 80 | settings.on_header_value = &header_value_cb >; 81 | settings.on_url = request_url_cb >; 82 | settings.on_body = request_body_cb >; 83 | size_t nparsed = 84 | http_parser_execute(&parser, &settings, httpbuf.data(), httpbuf.size()); 85 | if (nparsed != httpbuf.size()) return -1; 86 | 87 | uint32_t size = hpp.headers.size(); 88 | if ((size & 0x01) != 0) return -2; 89 | 90 | for (uint32_t i = 0; i < size; i += 2) { 91 | http_request->SetHeader(hpp.headers[i], hpp.headers[i + 1]); 92 | } 93 | 94 | switch (parser.method) { 95 | case HTTP_GET: 96 | http_request->SetMethod(HttpRequest::HTTP_REQUEST_METHOD_GET); 97 | break; 98 | case HTTP_POST: 99 | http_request->SetMethod(HttpRequest::HTTP_REQUEST_METHOD_POST); 100 | break; 101 | default: 102 | http_request->SetMethod(HttpRequest::HTTP_REQUEST_METHOD_UNKNOWN); 103 | break; 104 | } 105 | 106 | if (http_request->IsHeader("Connection")) { 107 | std::string keepalive = http_request->GetHeader("Connection"); 108 | std::transform(keepalive.begin(), keepalive.end(), keepalive.begin(), 109 | ::tolower); 110 | if (keepalive == "keep-alive") http_request->SetKeepAlive(true); 111 | } else 112 | // keep alive in default 113 | http_request->SetKeepAlive(true); 114 | 115 | return 0; 116 | } 117 | 118 | int HttpParserImpl::ParseHttpRequestHeadOnly(const std::string &httpbuf, 119 | HttpRequest *http_request, 120 | uint32_t *nparsed) { 121 | if (!http_request) return 0; 122 | 123 | http_parser_settings settings = {0, 0, 0, 0, 0, 0, 0, 0}; 124 | http_parser parser; 125 | 126 | memset(&parser, 0, sizeof(parser)); 127 | http_parser_init(&parser, HTTP_REQUEST); 128 | HttpParserParam hpp; 129 | hpp.http_struct = http_request; 130 | parser.data = &hpp; 131 | settings.on_header_field = &header_field_cb >; 132 | settings.on_header_value = &header_value_cb >; 133 | settings.on_url = request_url_cb >; 134 | settings.on_headers_complete = 135 | &header_complete_cb >; 136 | 137 | size_t n = 138 | http_parser_execute(&parser, &settings, httpbuf.data(), httpbuf.size()); 139 | if (0 == n) return -1; 140 | 141 | uint32_t size = hpp.headers.size(); 142 | if ((size & 0x01) != 0) return -2; 143 | 144 | for (uint32_t i = 0; i < size; i += 2) { 145 | http_request->SetHeader(hpp.headers[i], hpp.headers[i + 1]); 146 | } 147 | 148 | if (nparsed) *nparsed = n; 149 | return 0; 150 | } 151 | 152 | int HttpParserImpl::ParseHttpResponse(const std::string &httpbuf, 153 | HttpResponse *http_response) { 154 | if (!http_response) return 0; 155 | 156 | http_parser_settings settings = {0, 0, 0, 0, 0, 0, 0, 0}; 157 | http_parser parser; 158 | 159 | memset(&parser, 0, sizeof(parser)); 160 | http_parser_init(&parser, HTTP_RESPONSE); 161 | HttpParserParam hpp; 162 | hpp.http_struct = http_response; 163 | parser.data = &hpp; 164 | settings.on_header_field = header_field_cb >; 165 | settings.on_header_value = header_value_cb >; 166 | settings.on_body = request_body_cb >; 167 | size_t nparsed = 168 | http_parser_execute(&parser, &settings, httpbuf.data(), httpbuf.size()); 169 | if (nparsed != httpbuf.size()) return -1; 170 | 171 | assert(hpp.headers.size() % 2 == 0); 172 | 173 | uint32_t size = hpp.headers.size(); 174 | for (uint32_t i = 0; i < size; i += 2) { 175 | http_response->SetHeader(hpp.headers[i], hpp.headers[i + 1]); 176 | } 177 | 178 | http_response->SetStatus(parser.status_code); 179 | return 0; 180 | } 181 | 182 | // http_parser 辅助函数 183 | static int http_complete(http_parser *parser) { 184 | *(int *)(parser->data) = 1; 185 | return -1; // 返回非零,终止解析 186 | } 187 | 188 | int HttpParserImpl::CheckHttp(const std::string &httpbuf) { 189 | return CheckHttp(httpbuf.c_str(), httpbuf.size(), NULL); 190 | } 191 | 192 | int HttpParserImpl::CheckHttp(const void *httpbuf, size_t size) { 193 | return CheckHttp(httpbuf, size, NULL); 194 | } 195 | 196 | int HttpParserImpl::CheckHttp(const std::string &httpbuf, size_t *szparsed) { 197 | return CheckHttp(httpbuf.c_str(), httpbuf.size(), szparsed); 198 | } 199 | 200 | int HttpParserImpl::CheckHttp(const void *httpbuf, size_t size, 201 | size_t *szparsed) { 202 | int complete = 0; 203 | http_parser_settings settings = {0, 0, 0, 0, 0, 0, 0, 0}; 204 | http_parser parser; 205 | http_parser_init(&parser, HTTP_BOTH); 206 | parser.data = &complete; 207 | settings.on_message_complete = http_complete; 208 | 209 | int nparsed = http_parser_execute(&parser, &settings, 210 | static_cast(httpbuf), size); 211 | if ((static_cast(nparsed) <= size) && (1 == complete)) { 212 | if (szparsed) *szparsed = nparsed; 213 | return HPC_OK; 214 | } else if (nparsed == 0) // not http, shutdown 215 | { 216 | return HPC_NOT_HTTP; 217 | } else if ((static_cast(nparsed) <= size) && (0 == complete)) { 218 | return HPC_NOT_COMPLETE_HTTP; 219 | } else { 220 | return HPC_UNKNOWN; 221 | } 222 | } 223 | 224 | int HttpParserImpl::ParseUrl(const std::string &s, URL *url) { 225 | return ParseUrl(s.c_str(), s.size(), url); 226 | } 227 | 228 | int HttpParserImpl::ParseUrl(const char *s, size_t slen, URL *url) { 229 | struct http_parser_url hpurl; 230 | int ret = http_parser_parse_url(s, slen, 0, &hpurl); 231 | if (ret != 0) return -1; 232 | 233 | #define has_and_set(field, member) \ 234 | if (hpurl.field_set & (1 << UF_##field)) \ 235 | url->member.assign(&s[hpurl.field_data[UF_##field].off], \ 236 | hpurl.field_data[UF_##field].len); 237 | 238 | has_and_set(SCHEMA, schema); 239 | has_and_set(HOST, host); 240 | 241 | if (hpurl.field_set & (1 << UF_PORT)) 242 | url->port = std::stoul(std::string(&s[hpurl.field_data[UF_PORT].off], 243 | hpurl.field_data[UF_PORT].len).c_str()); 244 | else { 245 | if (hpurl.field_set & (1 << UF_SCHEMA)) { 246 | url->port = url->schema == "http" ? 80 : 443; 247 | } else { 248 | url->port = 80; 249 | } 250 | } 251 | 252 | has_and_set(PATH, path); 253 | 254 | if (hpurl.field_set & (1 << UF_PATH)) { 255 | url->uri = std::string(&s[hpurl.field_data[UF_PATH].off], 256 | slen - hpurl.field_data[UF_PATH].off - 1); 257 | } 258 | 259 | if (url->uri.empty()) { 260 | url->uri = "/"; 261 | } 262 | 263 | has_and_set(QUERY, query); 264 | has_and_set(FRAGMENT, fragment); 265 | has_and_set(USERINFO, userinfo); 266 | 267 | // TODO lazy parse 268 | auto query_kv = tinyco::string::Split(url->query, '&'); 269 | for (auto &ite : query_kv) { 270 | auto kv_vec = tinyco::string::Split(ite, '='); 271 | // ?foo -> size = 1 272 | // ?foo=bar -> size = 2 273 | if (kv_vec.size() < 2) continue; 274 | url->query_params 275 | [curl_unescape(kv_vec[0].c_str(), static_cast(kv_vec[0].size()))] = 276 | curl_unescape(kv_vec[1].c_str(), static_cast(kv_vec[1].size())); 277 | } 278 | 279 | #undef has_and_set 280 | return 0; 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /http/http_tool.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_TOOL_ 2 | #define HTTP_TOOL_ 3 | 4 | #include "http_request.h" 5 | #include "http_response.h" 6 | 7 | #include 8 | 9 | namespace tinyco { 10 | namespace http { 11 | 12 | class HttpParser { 13 | public: 14 | enum HttpParseCode { 15 | HPC_OK = 0, 16 | HPC_NOT_HTTP = -1, 17 | HPC_NOT_COMPLETE_HTTP = -2, 18 | HPC_UNKNOWN, 19 | }; 20 | 21 | HttpParser() {} 22 | 23 | virtual ~HttpParser() {} 24 | 25 | // 解析 http 请求 26 | // 成功返回 0;失败返回 -1 27 | virtual int ParseHttpRequest(const std::string &httpbuf, 28 | HttpRequest *http_request) = 0; 29 | 30 | virtual int ParseHttpRequestHeadOnly(const std::string &httpbuf, 31 | HttpRequest *http_request, 32 | uint32_t *nparsed) = 0; 33 | 34 | virtual int ParseUrl(const std::string &s, URL *url) = 0; 35 | virtual int ParseUrl(const char *s, size_t slen, URL *url) = 0; 36 | 37 | // 解析 http 响应 38 | // 成功返回 0;失败返回 -1 39 | virtual int ParseHttpResponse(const std::string &httpbuf, 40 | HttpResponse *http_response) = 0; 41 | 42 | virtual int CheckHttp(const std::string &httpbuf) = 0; 43 | virtual int CheckHttp(const std::string &httpbuf, size_t *szparsed) = 0; 44 | virtual int CheckHttp(const void *httpbuf, size_t size) = 0; 45 | virtual int CheckHttp(const void *httpbuf, size_t size, size_t *szparsed) = 0; 46 | 47 | private: 48 | HttpParser(const HttpParser &); 49 | void operator=(const HttpParser &); 50 | }; 51 | 52 | class HttpParserImpl : public HttpParser { 53 | public: 54 | HttpParserImpl() {} 55 | virtual ~HttpParserImpl() {} 56 | 57 | // 解析 http 请求 58 | // 成功返回 0;失败返回 -1 59 | virtual int ParseHttpRequest(const std::string &httpbuf, 60 | HttpRequest *http_request); 61 | 62 | virtual int ParseHttpRequestHeadOnly(const std::string &httpbuf, 63 | HttpRequest *http_request, 64 | uint32_t *nparsed); 65 | 66 | // 解析 http 响应 67 | // 成功返回 0;失败返回 -1 68 | virtual int ParseHttpResponse(const std::string &httpbuf, 69 | HttpResponse *http_response); 70 | 71 | virtual int ParseUrl(const std::string &s, URL *url); 72 | virtual int ParseUrl(const char *s, size_t slen, URL *url); 73 | 74 | virtual int CheckHttp(const std::string &httpbuf); 75 | virtual int CheckHttp(const std::string &httpbuf, size_t *szparsed); 76 | virtual int CheckHttp(const void *httpbuf, size_t size); 77 | virtual int CheckHttp(const void *httpbuf, size_t size, size_t *szparsed); 78 | }; 79 | } 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /http/https_client.cc: -------------------------------------------------------------------------------- 1 | #include "https_client.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "frame.h" 14 | #include "http_tool.h" 15 | #include "util/network.h" 16 | 17 | namespace tinyco { 18 | #define SSTR(x) \ 19 | static_cast((std::ostringstream() << std::dec << x)) \ 20 | .str() 21 | namespace http { 22 | static int mt_write(BIO *h, const char *buf, int num); 23 | static int mt_read(BIO *h, char *buf, int size); 24 | static long mt_ctrl(BIO *h, int cmd, long arg1, void *arg2); 25 | static int mt_new(BIO *h); 26 | static int mt_free(BIO *data); 27 | 28 | static BIO_METHOD methods_mt = {BIO_TYPE_SOCKET, "socket_mt", mt_write, mt_read, 29 | NULL, NULL, mt_ctrl, mt_new, 30 | mt_free, NULL}; 31 | 32 | BIO_METHOD *BIO_s_socket_mt(void) { return (&methods_mt); } 33 | 34 | BIO *BIO_new_socket_mt(int fd, int close_flag, BIOUserData &bud) { 35 | BIO *ret; 36 | 37 | ret = BIO_new(BIO_s_socket_mt()); 38 | if (ret == NULL) return (NULL); 39 | BIO_set_fd(ret, fd, close_flag); 40 | ret->ptr = &bud; 41 | return (ret); 42 | } 43 | 44 | static int mt_new(BIO *bi) { 45 | bi->init = 0; 46 | bi->num = 0; 47 | bi->ptr = NULL; 48 | bi->flags = 0; 49 | return (1); 50 | } 51 | 52 | static int mt_free(BIO *a) { 53 | if (a == NULL) return (0); 54 | if (a->shutdown) { 55 | if (a->init) { 56 | shutdown(a->num, SHUT_RDWR); 57 | close(a->num); 58 | } 59 | a->init = 0; 60 | a->flags = 0; 61 | } 62 | return (1); 63 | } 64 | 65 | static int mt_read(BIO *b, char *out, int outl) { 66 | int ret = 0; 67 | 68 | BIOUserData *bud = static_cast(b->ptr); 69 | if (out != NULL) { 70 | errno = 0; 71 | ret = Frame::recv(b->num, out, outl, 0 /*, bud->ms_read_timeout */); 72 | } 73 | return (ret); 74 | } 75 | 76 | static int mt_write(BIO *b, const char *in, int inl) { 77 | int ret = 0; 78 | 79 | BIOUserData *bud = static_cast(b->ptr); 80 | if (in != NULL) { 81 | errno = 0; 82 | ret = Frame::send(b->num, in, inl, 0 /*, bud->ms_write_timeout */); 83 | } 84 | return (ret); 85 | } 86 | 87 | static long mt_ctrl(BIO *b, int cmd, long num, void *ptr) { 88 | long ret = 1; 89 | int *ip; 90 | 91 | switch (cmd) { 92 | case BIO_C_SET_FD: 93 | mt_free(b); 94 | b->num = *((int *)ptr); 95 | b->shutdown = (int)num; 96 | b->init = 1; 97 | break; 98 | case BIO_C_GET_FD: 99 | if (b->init) { 100 | ip = (int *)ptr; 101 | if (ip != NULL) *ip = b->num; 102 | ret = b->num; 103 | } else 104 | ret = -1; 105 | break; 106 | case BIO_CTRL_GET_CLOSE: 107 | ret = b->shutdown; 108 | break; 109 | case BIO_CTRL_SET_CLOSE: 110 | b->shutdown = (int)num; 111 | break; 112 | case BIO_CTRL_DUP: 113 | case BIO_CTRL_FLUSH: 114 | ret = 1; 115 | break; 116 | default: 117 | ret = 0; 118 | break; 119 | } 120 | return (ret); 121 | } 122 | 123 | SSL_CTX *HttpsClient::ctx_ = NULL; 124 | 125 | HttpsClient::HttpsClient() : fd_(-1), bio_(NULL), ssl_(NULL) {} 126 | 127 | HttpsClient::~HttpsClient() { Close(); } 128 | 129 | int HttpsClient::CreateSocket() { 130 | int keepalive = 1; 131 | int keepidle = 20; 132 | int keepinterval = 3; 133 | int keepcount = 3; 134 | int val = 0; 135 | 136 | if ((fd_ = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 137 | strerr_.append("socket error"); 138 | return -1; 139 | } 140 | 141 | if (network::SetNonBlock(fd_) < 0) { 142 | strerr_.append("fail to set nonblock"); 143 | return -2; 144 | } 145 | 146 | if (setsockopt(fd_, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, 147 | sizeof(keepalive)) != 0) { 148 | strerr_.append("setsockopt SO_KEEPALIVE error"); 149 | close(fd_); 150 | return -4; 151 | } 152 | 153 | if (setsockopt(fd_, SOL_TCP, TCP_KEEPIDLE, (void *)&keepidle, 154 | sizeof(keepidle)) != 0) { 155 | strerr_.append("setsockopt TCP_KEEPIDLE error"); 156 | close(fd_); 157 | return -5; 158 | } 159 | 160 | if (setsockopt(fd_, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval, 161 | sizeof(keepinterval)) != 0) { 162 | strerr_.append("setsockopt TCP_KEEPINTVL error"); 163 | close(fd_); 164 | return -6; 165 | } 166 | 167 | if (setsockopt(fd_, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount, 168 | sizeof(keepcount)) != 0) { 169 | strerr_.append("setsockopt TCP_KEEPCNT error"); 170 | close(fd_); 171 | return -7; 172 | } 173 | 174 | return fd_; 175 | } 176 | 177 | int HttpsClient::Init(uint32_t ip, uint16_t port, int connect_timeout, 178 | int read_timeout, int write_timeout) { 179 | ClearLog(); 180 | 181 | bud_.ms_connect_timeout = connect_timeout; 182 | bud_.ms_write_timeout = write_timeout; 183 | bud_.ms_read_timeout = read_timeout; 184 | 185 | struct sockaddr_in addr = {0}; 186 | fd_ = CreateSocket(); 187 | int ret = 0; 188 | 189 | if (fd_ < 0) { 190 | strerr_.append("CreateSocket error"); 191 | return -1; 192 | } 193 | 194 | if ((bio_ = BIO_new_socket_mt(fd_, 1, bud_)) == NULL) goto end; 195 | if (ctx_ == NULL) { 196 | SSL_library_init(); 197 | if ((ctx_ = SSL_CTX_new(TLSv1_client_method())) == NULL) { 198 | strerr_.append("SSL_CTX_new error"); 199 | goto end; 200 | } 201 | } 202 | 203 | addr.sin_family = AF_INET; 204 | addr.sin_port = htons(port); 205 | addr.sin_addr.s_addr = ip; 206 | 207 | strlog_.append("HttpsClient connect to ") 208 | .append(inet_ntoa(addr.sin_addr)) 209 | .append(":") 210 | .append(SSTR(port)); 211 | 212 | if (Frame::connect(fd_, (struct sockaddr *)&addr, sizeof(addr) 213 | /*, bud_.ms_connect_timeout */) < 0) { 214 | strerr_.append("tinyco::connect error: ").append("strerror(errno)"); 215 | goto end; 216 | } 217 | 218 | if ((ssl_ = SSL_new(ctx_)) == NULL) { 219 | strerr_.append("SSL_new error"); 220 | goto end; 221 | } 222 | 223 | SSL_set_bio(ssl_, bio_, bio_); 224 | 225 | if ((ret = SSL_connect(ssl_)) <= 0) { 226 | strerr_.append("SSL_connect error, error code = ") 227 | .append(SSTR(SSL_get_error(ssl_, ret))); 228 | goto end; 229 | } 230 | 231 | return 0; 232 | 233 | end: 234 | Close(); 235 | return -2; 236 | } 237 | 238 | int HttpsClient::SendAndReceive(const char *sendbuf, size_t sendlen, 239 | std::string *recvbuf) { 240 | ClearLog(); 241 | 242 | if (fd_ < 0 || NULL == ssl_ || NULL == bio_) { 243 | strerr_.append("invalid parameters"); 244 | return -1; 245 | } 246 | 247 | if (NULL == sendbuf || 0 == sendlen || NULL == recvbuf) { 248 | strerr_.append("invalid parameters"); 249 | return -1; 250 | } 251 | 252 | return SslSendAndReceive(sendbuf, sendlen, recvbuf); 253 | } 254 | 255 | int HttpsClient::SslSendAndReceive(const char *sendbuf, size_t sendlen, 256 | std::string *recvbuf) { 257 | char recvbuf_[max_recvlen] = {0}; 258 | std::vector real_buf; 259 | 260 | size_t recvpos = 0; 261 | bool http_done = false; 262 | int ret = 0; 263 | 264 | size_t left = sendlen; 265 | while (left) { 266 | // <=0 包含两种情况: 267 | // 1. <0 表示发送错误 268 | // 2. =0 表示服务器重启或宕机了 269 | if ((ret = SSL_write(ssl_, sendbuf + sendlen - left, left)) <= 0) { 270 | strerr_.append("SSL_write error, error code = ") 271 | .append(SSTR(SSL_get_error(ssl_, ret))) 272 | .append("|ret = ") 273 | .append(SSTR(ret)) 274 | .append("|errno = ") 275 | .append(SSTR(errno)) 276 | .append("|strerror = ") 277 | .append(strerror(errno)); 278 | return -1; 279 | } 280 | 281 | if ((int)left < ret) { 282 | strerr_.append("SSL_write error, left = ") 283 | .append(SSTR(left)) 284 | .append("|ret = ") 285 | .append(SSTR(ret)); 286 | return -1; 287 | } 288 | 289 | left -= ret; 290 | } 291 | 292 | while (true) { 293 | int recvlen_ = 0; 294 | 295 | // <=0 包含两种情况: 296 | // 1. <0 表示发送错误 297 | // 2. =0 表示服务器重启或宕机了 298 | recvlen_ = SSL_read(ssl_, recvbuf_, (int)max_recvlen); 299 | if (recvlen_ <= 0) { 300 | strerr_.append("SSL_read error, error code = ") 301 | .append(SSTR(SSL_get_error(ssl_, recvlen_))) 302 | .append("|ret = ") 303 | .append(SSTR(ret)) 304 | .append("|errno = ") 305 | .append(SSTR(errno)) 306 | .append("|strerror = ") 307 | .append(strerror(errno)); 308 | break; 309 | } 310 | 311 | recvpos += recvlen_; 312 | real_buf.insert(real_buf.end(), recvbuf_, recvbuf_ + recvlen_); 313 | 314 | // pay attention to oom 315 | http::HttpParserImpl parser; 316 | int ret = parser.CheckHttp(&real_buf[0], real_buf.size()); 317 | if (http::HttpParserImpl::HPC_OK == ret) { 318 | http_done = true; 319 | // if (status) *status = parser.status_code; 320 | break; 321 | } else if (http::HttpParserImpl::HPC_NOT_COMPLETE_HTTP == ret) { 322 | continue; 323 | } else if (http::HttpParserImpl::HPC_NOT_HTTP == ret) { 324 | recvpos = 0; 325 | break; 326 | } else { 327 | recvpos = 0; 328 | break; 329 | } 330 | } 331 | 332 | if (http_done) { 333 | recvbuf->assign(&real_buf[0], real_buf.size()); 334 | return 0; 335 | } 336 | 337 | return -1; 338 | } 339 | 340 | void HttpsClient::Close() { 341 | if (ssl_) { 342 | SSL_shutdown(ssl_); 343 | SSL_free(ssl_); 344 | bio_ = NULL; 345 | } 346 | 347 | if (bio_) { 348 | BIO_free(bio_); 349 | } 350 | 351 | if (fd_ != -1) close(fd_); 352 | 353 | fd_ = -1; 354 | ssl_ = NULL; 355 | bio_ = NULL; 356 | } 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /http/https_client.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPS_CLIENT_H_ 2 | #define HTTPS_CLIENT_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "openssl/bio.h" 8 | #include "openssl/ssl.h" 9 | 10 | // HttpsClient hc; 11 | // hc.Init(myip); 12 | // hc.SendAndRecv(...); 13 | 14 | namespace tinyco { 15 | namespace http { 16 | 17 | struct BIOUserData { 18 | int ms_connect_timeout; 19 | int ms_read_timeout; 20 | int ms_write_timeout; 21 | }; 22 | 23 | class HttpsClient { 24 | public: 25 | HttpsClient(); 26 | 27 | ~HttpsClient(); 28 | 29 | int Init(uint32_t ip, uint16_t port = 443, 30 | int connect_timeout = kMsBIOConnectTimeout, 31 | int read_timeout = kMsBIOReadTimeout, 32 | int write_timeout = kMsBIOWriteTimeout); 33 | int SendAndReceive(const char *sendbuf, size_t sendlen, std::string *recvbuf); 34 | void Close(); 35 | 36 | std::string GetErrorMsg() { 37 | std::string _e_ = strerr_; 38 | strerr_.clear(); 39 | return _e_; 40 | } 41 | 42 | const static int kMsBIOConnectTimeout = 1000; 43 | const static int kMsBIOReadTimeout = 5000; 44 | const static int kMsBIOWriteTimeout = 500; 45 | 46 | private: 47 | int CreateSocket(); 48 | int SslSendAndReceive(const char *sendbuf, size_t sendlen, 49 | std::string *recvbuf); 50 | 51 | void ClearLog() { 52 | strlog_.clear(); 53 | strerr_.clear(); 54 | } 55 | 56 | private: 57 | int fd_; 58 | static SSL_CTX *ctx_; 59 | BIO *bio_; 60 | SSL *ssl_; 61 | 62 | std::string strlog_; 63 | std::string strerr_; 64 | BIOUserData bud_; 65 | 66 | static const size_t max_recvlen = 5120; 67 | }; 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /http/tcp_conn.cc: -------------------------------------------------------------------------------- 1 | #include "tcp_conn.h" 2 | 3 | #include "frame.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace tinyco; 15 | 16 | namespace tinyco { 17 | #define SSTR(x) \ 18 | static_cast((std::ostringstream() << std::dec << x)) \ 19 | .str() 20 | 21 | TcpConn::TcpConn() : fd_(kInvalidFd) {} 22 | 23 | TcpConn::~TcpConn() { Close(); } 24 | 25 | int TcpConn::Init(uint32_t ip, uint16_t port, int connect_timeout, 26 | int read_timeout, int write_timeout) { 27 | if (0 == ip || 0 == port) return -1; 28 | 29 | ip_ = ip; 30 | port_ = port; 31 | connect_timeout_ = connect_timeout; 32 | read_timeout_ = read_timeout; 33 | write_timeout_ = write_timeout; 34 | 35 | fd_ = socket(AF_INET, SOCK_STREAM, 0); 36 | if (-1 == fd_) { 37 | strlog_.append("|socket error: ").append(strerror(errno)); 38 | return -1; 39 | } 40 | 41 | if (SetBlocking(false) < 0) { 42 | Close(); 43 | strlog_.append("|SetBlocking error"); 44 | return -1; 45 | } 46 | 47 | sockaddr_in addr; 48 | addr.sin_family = AF_INET; 49 | addr.sin_port = htons(port); 50 | addr.sin_addr.s_addr = ip; 51 | 52 | int ret = 0; 53 | if ((ret = Frame::connect(fd_, (struct sockaddr *)&addr, sizeof(addr))) < 0) { 54 | Close(); 55 | strlog_.append("|tinyco::connect error: ").append(strerror(errno)); 56 | return -1; 57 | } 58 | 59 | return 0; 60 | } 61 | 62 | int TcpConn::SendAndReceive(const char *buf, size_t size, std::string *output, 63 | check_recv_func func) { 64 | if (NULL == output || NULL == buf || 0 == size) return -1; 65 | 66 | int ret = 0; 67 | if (kInvalidFd == fd_) { 68 | strlog_.append("|try to reconnect server"); 69 | if ((ret = TryReConnect()) < 0) { 70 | strlog_.append("|reconnect server error"); 71 | return -1; 72 | } 73 | } 74 | 75 | int left = size; 76 | while (left > 0) { 77 | if ((ret = Frame::send(fd_, buf + size - left, left, 0)) < 0) { 78 | strlog_.append("|tinyco::send error: ").append(strerror(errno)); 79 | return -1; 80 | } 81 | 82 | if (ret == 0) { 83 | strlog_.append("|tinyco::send error: server close the connection: ") 84 | .append(strerror(errno)); 85 | return -1; 86 | } 87 | 88 | if (ret > left) { 89 | strlog_.append("|tinyco::send werid error: ").append(strerror(errno)); 90 | return -1; 91 | } 92 | 93 | left -= ret; 94 | } 95 | 96 | std::vector real_buf; 97 | char recvbuf[kMaxRecvSize] = {0}; 98 | int recvd = 0; 99 | int eof = 0; 100 | while (true) { 101 | if ((ret = Frame::recv(fd_, recvbuf, kMaxRecvSize, 0)) < 0) { 102 | strlog_.append("|tinyco::recv error: ").append(strerror(errno)); 103 | return -1; 104 | } 105 | 106 | if (ret == 0) { 107 | strlog_.append("|tinyco::recv error: server close the connection: ") 108 | .append(strerror(errno)) 109 | .append("|recvd=") 110 | .append(SSTR(recvd)); 111 | return -1; 112 | } 113 | 114 | recvd += ret; 115 | 116 | real_buf.insert(real_buf.end(), recvbuf, recvbuf + ret); // ret > 0 117 | 118 | eof = func(&real_buf[0], real_buf.size()); 119 | if (eof > 0) { 120 | break; 121 | } else if (eof < 0 && eof != CHECK_INCOMPLETE) { 122 | strlog_.append("|recv error data: ret_code=").append(SSTR(eof)); 123 | break; 124 | } 125 | } 126 | 127 | if (eof < 0) { 128 | strlog_.append("|func eof error."); 129 | return -1; 130 | } 131 | 132 | output->assign(&real_buf[0], eof); 133 | return 0; 134 | } 135 | 136 | int TcpConn::SendAndReceive(const std::string &buf, std::string *output, 137 | check_recv_func func) { 138 | return SendAndReceive(buf.data(), buf.size(), output, func); 139 | } 140 | 141 | int TcpConn::Send(const char *buf, size_t size) { 142 | int ret = 0; 143 | if (kInvalidFd == fd_) { 144 | strlog_.append("|try to reconnect server"); 145 | if ((ret = TryReConnect()) < 0) { 146 | strlog_.append("|reconnect server error"); 147 | return -1; 148 | } 149 | } 150 | 151 | if (NULL == buf || 0 == size) return -1; 152 | 153 | int left = size; 154 | while (left > 0) { 155 | if ((ret = Frame::send(fd_, buf + size - left, left, 0)) < 0) { 156 | strlog_.append("|tinyco::send error: ").append(strerror(errno)); 157 | return -1; 158 | } 159 | 160 | if (ret == 0) { 161 | strlog_.append("|tinyco::send error: server close the connection: ") 162 | .append(strerror(errno)); 163 | return -1; 164 | } 165 | 166 | if (ret > left) { 167 | strlog_.append("|tinyco::send werid error: ").append(strerror(errno)); 168 | return -1; 169 | } 170 | 171 | left -= ret; 172 | } 173 | 174 | return 0; 175 | } 176 | 177 | int TcpConn::Send(const std::string &buf) { 178 | return Send(buf.data(), buf.size()); 179 | } 180 | 181 | void TcpConn::Close() { 182 | if (kInvalidFd != fd_) close(fd_); 183 | fd_ = kInvalidFd; 184 | strlog_.clear(); 185 | } 186 | 187 | int TcpConn::TryReConnect() { return Init(ip_, port_); } 188 | 189 | int TcpConn::SetBlocking(bool blocking) { 190 | int flags; 191 | 192 | if ((flags = fcntl(fd_, F_GETFL)) == -1) { 193 | strlog_.append("|fcntl GET error"); 194 | return -1; 195 | } 196 | 197 | if (blocking) 198 | flags &= ~O_NONBLOCK; 199 | else 200 | flags |= O_NONBLOCK; 201 | 202 | if (fcntl(fd_, F_SETFL, flags) == -1) { 203 | strlog_.append("|fcntl SET error"); 204 | return -1; 205 | } 206 | 207 | return 0; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /http/tcp_conn.h: -------------------------------------------------------------------------------- 1 | #ifndef TCP_CONN_H_ 2 | #define TCP_CONN_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyco { 8 | 9 | enum { 10 | CHECK_ERROR = -1, 11 | CHECK_INCOMPLETE = -2, 12 | }; 13 | 14 | class TcpConn { 15 | public: 16 | TcpConn(); 17 | virtual ~TcpConn(); 18 | int Init(uint32_t ip, uint16_t port, int connect_timeout = 200, 19 | int read_timeout = 200, int write_timeout = 200); 20 | typedef int (*check_recv_func)(void *data, size_t size); 21 | int SendAndReceive(const char *buf, size_t size, std::string *output, 22 | check_recv_func func); 23 | int SendAndReceive(const std::string &buf, std::string *output, 24 | check_recv_func func); 25 | void Close(); 26 | 27 | std::string GetErrorMsg() { 28 | std::string _e_ = strlog_; 29 | strlog_.clear(); 30 | return _e_; 31 | } 32 | 33 | int Send(const char *buf, size_t size); 34 | int Send(const std::string &buf); 35 | 36 | static const int kInvalidFd = -1; 37 | static const int kMaxRecvSize = 5120; 38 | 39 | private: 40 | int TryReConnect(); 41 | int SetBlocking(bool blocking); 42 | 43 | protected: 44 | uint32_t ip_; 45 | uint16_t port_; 46 | int fd_; 47 | int connect_timeout_; 48 | int read_timeout_; 49 | int write_timeout_; 50 | 51 | std::string strlog_; 52 | }; 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /listener.cc: -------------------------------------------------------------------------------- 1 | #include "listener.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "util/log.h" 10 | #include "util/network.h" 11 | 12 | namespace tinyco { 13 | Listener::Listener() : listenfd_(-1) {} 14 | 15 | Listener::~Listener() { 16 | if (listenfd_ >= 0) close(listenfd_); 17 | if (mtx_) delete mtx_; 18 | } 19 | 20 | int TcpListener::Listen(const network::IP &ip, uint16_t port) { 21 | if (mtx_ && mtx_->InitMtx(NULL) < 0) return -__LINE__; 22 | 23 | ip_ = ip; 24 | port_ = port; 25 | listenfd_ = socket(AF_INET, SOCK_STREAM, 0); 26 | if (listenfd_ < 0) { 27 | LOG_ERROR("socket error"); 28 | return -__LINE__; 29 | } 30 | 31 | LOG_DEBUG("listenfd=%d|port=%u", listenfd_, port); 32 | 33 | sockaddr_in server; 34 | server.sin_family = AF_INET; 35 | server.sin_addr.s_addr = ip_.af_inet_ip; 36 | server.sin_port = htons(port_); 37 | 38 | if (network::SetNonBlock(listenfd_) < 0) { 39 | LOG_ERROR("set nonblock error"); 40 | return -__LINE__; 41 | } 42 | 43 | if (network::SetReuseAddr(listenfd_)) { 44 | LOG("fail to setsockopt(SO_REUSEADDR)"); 45 | return -__LINE__; 46 | } 47 | 48 | if (bind(listenfd_, (struct sockaddr *)&server, sizeof(server)) < 0) { 49 | LOG_ERROR("bind error"); 50 | return -__LINE__; 51 | } 52 | 53 | if (listen(listenfd_, 5) < 0) { 54 | LOG_ERROR("listen error"); 55 | return -__LINE__; 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | int UdpListener::Listen(const network::IP &ip, uint16_t port) { 62 | if (mtx_ && mtx_->InitMtx(NULL) < 0) return -__LINE__; 63 | 64 | ip_ = ip; 65 | port_ = port; 66 | 67 | listenfd_ = socket(AF_INET, SOCK_DGRAM, 0); 68 | if (listenfd_ < 0) { 69 | LOG("socket error"); 70 | return -__LINE__; 71 | } 72 | 73 | struct sockaddr_in server, client; 74 | server.sin_family = AF_INET; 75 | server.sin_addr.s_addr = ip_.af_inet_ip; 76 | server.sin_port = htons(port_); 77 | 78 | if (network::SetNonBlock(listenfd_) < 0) { 79 | LOG_ERROR("set nonblock error"); 80 | return -__LINE__; 81 | } 82 | 83 | if (bind(listenfd_, (struct sockaddr *)&server, sizeof(server)) < 0) { 84 | LOG("bind error"); 85 | return -__LINE__; 86 | } 87 | 88 | return 0; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /listener.h: -------------------------------------------------------------------------------- 1 | #ifndef LISTENER_H_ 2 | #define LISTENER_H_ 3 | 4 | #include "./dns/dns_resolve.h" 5 | #include "util/network.h" 6 | #include "mutex.h" 7 | 8 | #include 9 | 10 | namespace tinyco { 11 | class Listener { 12 | public: 13 | Listener(); 14 | virtual ~Listener(); 15 | virtual int Listen(const network::IP &ip, uint16_t port) = 0; 16 | virtual Mutex *GetMtx() { return mtx_; } 17 | int GetSocketFd() const { return listenfd_; } 18 | void Destroy() { 19 | if (listenfd_ >= 0) close(listenfd_); 20 | } 21 | network::IP GetIP() const { return ip_; } 22 | uint16_t GetPort() const { return port_; } 23 | std::string GetProto() const { return proto_; } 24 | 25 | protected: 26 | int listenfd_; 27 | network::IP ip_; 28 | uint16_t port_; 29 | Mutex *mtx_; 30 | std::string proto_; 31 | }; 32 | 33 | class TcpListener : public Listener { 34 | public: 35 | TcpListener() { 36 | proto_ = "tcp"; 37 | mtx_ = new AtomicMtx(); 38 | } 39 | virtual int Listen(const network::IP &ip, uint16_t port); 40 | }; 41 | 42 | class UdpListener : public Listener { 43 | public: 44 | UdpListener() { 45 | proto_ = "tcp"; 46 | mtx_ = new AtomicMtx(); 47 | } 48 | virtual int Listen(const network::IP &ip, uint16_t port); 49 | }; 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /mutex.cc: -------------------------------------------------------------------------------- 1 | #include "mutex.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace tinyco { 7 | 8 | int FileMtx::InitMtx(void *arg) { 9 | if (!arg) return -1; 10 | 11 | std::string lf = static_cast(arg); 12 | fd_ = 13 | open(lf.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 14 | if (fd_ != -1) { 15 | const char *byte_to_write = "*"; 16 | for (auto byte_count = 0; byte_count < 10; byte_count++) { 17 | write(fd_, byte_to_write, 1); 18 | } 19 | 20 | // trick: no need to remove it 21 | unlink(lf.c_str()); 22 | return 0; 23 | } 24 | 25 | return -1; 26 | } 27 | 28 | int FileMtx::TryLock() { 29 | struct flock fl = {0}; 30 | fl.l_type = F_WRLCK; 31 | fl.l_whence = SEEK_SET; 32 | 33 | if (fcntl(fd_, F_SETLK, &fl) == -1) { 34 | return -1; 35 | } 36 | 37 | return 0; 38 | } 39 | 40 | int FileMtx::Unlock() { 41 | struct flock fl = {0}; 42 | 43 | fl.l_type = F_UNLCK; 44 | fl.l_whence = SEEK_SET; 45 | 46 | if (fcntl(fd_, F_SETLK, &fl) == -1) { 47 | return -1; 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | #if (__amd64__ || __amd64) 54 | 55 | inline uint64_t AtomicCompareAndSet(uint64_t *a, uint64_t old, uint64_t n) { 56 | u_char res; 57 | 58 | __asm__ volatile( 59 | " cmpxchgq %3, %1; " 60 | " sete %0; " 61 | : "=a"(res) 62 | : "m"(*a), "a"(old), "r"(n) 63 | : "cc", "memory"); 64 | return res; 65 | } 66 | 67 | #else 68 | 69 | inline uint64_t AtomicCompareAndSet(uint64_t *a, uint64_t old, uint64_t n) { 70 | *a = n; 71 | return n; 72 | } 73 | 74 | #endif 75 | 76 | AtomicMtx::~AtomicMtx() { 77 | if (ptr_) munmap(ptr_, sizeof(*ptr_)); 78 | } 79 | 80 | int AtomicMtx::InitMtx(void *arg) { 81 | ptr_ = (uint64_t *)mmap(NULL, sizeof(uint64_t), PROT_READ | PROT_WRITE, 82 | MAP_ANON | MAP_SHARED, -1, 0); 83 | 84 | *ptr_ = 0; 85 | return 0; 86 | } 87 | 88 | int AtomicMtx::TryLock() { 89 | if (!ptr_) return -1; 90 | return (*ptr_ == 0 && AtomicCompareAndSet(ptr_, 0, getpid())) ? 0 : -1; 91 | } 92 | 93 | int AtomicMtx::Unlock() { 94 | if (!ptr_) return -1; 95 | return AtomicCompareAndSet(ptr_, getpid(), 0) ? 0 : -1; 96 | } 97 | 98 | int AtomicMtx::ForcedUnlockIfNeed(void *check_data) { 99 | if (*ptr_ == *reinterpret_cast(check_data)) { 100 | *ptr_ = 0; 101 | return 0; 102 | } 103 | 104 | return 0; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /mutex.h: -------------------------------------------------------------------------------- 1 | #ifndef MUTEX_H_ 2 | #define MUTEX_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tinyco { 9 | 10 | class Mutex { 11 | public: 12 | virtual int InitMtx(void *arg) = 0; 13 | virtual int TryLock() = 0; 14 | virtual int Unlock() = 0; 15 | virtual int ForcedUnlockIfNeed(void *check_data) { return 0; } 16 | }; 17 | 18 | class DummyMtx : public Mutex { 19 | public: 20 | int InitMtx(void *arg) { return 0; } 21 | virtual int TryLock() { return 0; } 22 | virtual int Unlock() { return 0; } 23 | }; 24 | 25 | class FileMtx : public Mutex { 26 | public: 27 | int InitMtx(void *arg); 28 | virtual int TryLock(); 29 | virtual int Unlock(); 30 | 31 | private: 32 | int fd_; 33 | }; 34 | 35 | class AtomicMtx : public Mutex { 36 | public: 37 | int InitMtx(void *arg); 38 | virtual ~AtomicMtx(); 39 | 40 | virtual int TryLock(); 41 | virtual int Unlock(); 42 | virtual int ForcedUnlockIfNeed(void *check_data); 43 | 44 | private: 45 | uint64_t *ptr_; 46 | }; 47 | } 48 | #endif 49 | -------------------------------------------------------------------------------- /rpc/channel.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "channel.h" 3 | 4 | #include 5 | 6 | #include "controller.h" 7 | #include "http/http_client.h" 8 | #include "http/http_op.h" 9 | #include "util/log.h" 10 | 11 | namespace tinyco { 12 | void Channel::CallMethod(const google::protobuf::MethodDescriptor* method, 13 | google::protobuf::RpcController* controller, 14 | const google::protobuf::Message* request, 15 | google::protobuf::Message* response, 16 | google::protobuf::Closure* done) { 17 | 18 | Controller* cntl = reinterpret_cast(controller); 19 | 20 | http::HttpRequest hreq = cntl->http_requeset_header(); 21 | http::HttpResponse hrsp; 22 | std::string req_content; 23 | request->SerializeToString(&req_content); 24 | 25 | hreq.SetContent(req_content); 26 | 27 | LOG("hreq=%s", hreq.SerializeToString().c_str()); 28 | 29 | if (hrsp.GetStatus() != 200) { 30 | // TODO copy header only 31 | return; 32 | } 33 | 34 | int ret = http::HttpOp::HttpRequest(server_.ip.af_inet_ip, server_.port, hreq, 35 | &hrsp); 36 | if (ret < 0) { 37 | cntl->set_error_code(-1); 38 | return; 39 | } 40 | 41 | if (!response->ParseFromString(hrsp.GetContent())) { 42 | LOG_ERROR("ParseFromString error"); 43 | return; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rpc/channel.h: -------------------------------------------------------------------------------- 1 | #ifndef CHANNEL_H_ 2 | #define CHANNEL_H_ 3 | 4 | #include 5 | 6 | #include "util/network.h" 7 | 8 | namespace tinyco { 9 | class Channel : public google::protobuf::RpcChannel { 10 | public: 11 | bool InitServer(const network::EndPoint& server) { server_ = server; } 12 | void CallMethod(const google::protobuf::MethodDescriptor* method, 13 | google::protobuf::RpcController* controller, 14 | const google::protobuf::Message* request, 15 | google::protobuf::Message* response, 16 | google::protobuf::Closure* done); 17 | 18 | private: 19 | network::EndPoint server_; 20 | }; 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /rpc/controller.cc: -------------------------------------------------------------------------------- 1 | #include "controller.h" 2 | 3 | #include 4 | 5 | namespace tinyco {} 6 | -------------------------------------------------------------------------------- /rpc/controller.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTROLLER_H_ 2 | #define CONTROLLER_H_ 3 | 4 | #include 5 | 6 | #include "util/network.h" 7 | #include "http/http_response.h" 8 | #include "http/http_request.h" 9 | 10 | namespace tinyco { 11 | class Controller : public google::protobuf::RpcController { 12 | public: 13 | Controller() : error_code_(0) { 14 | hreq_header_.SetMethod(http::HttpRequest::HTTP_REQUEST_METHOD_POST); 15 | } 16 | int connect_timeout() const { return connect_timeout_; } 17 | void set_connect_timoeut(int timeout) { connect_timeout_ = timeout; } 18 | 19 | int recv_timeout() const { return recv_timeout_; } 20 | void set_recv_timoeut(int timeout) { recv_timeout_ = timeout; } 21 | 22 | int error_code() const { return error_code_; } 23 | void set_error_code(int ec) { error_code_ = ec; } 24 | 25 | http::HttpResponse& http_response_header() { return hrsp_header_; } 26 | http::HttpRequest& http_requeset_header() { return hreq_header_; } 27 | 28 | virtual void Reset() {} 29 | 30 | // After a call has finished, returns true if the call failed. The possible 31 | // reasons for failure depend on the RPC implementation. Failed() must not 32 | // be called before a call has finished. If Failed() returns true, the 33 | // contents of the response message are undefined. 34 | virtual bool Failed() const { return false; } 35 | 36 | // If Failed() is true, returns a human-readable description of the error. 37 | virtual std::string ErrorText() const { return ""; } 38 | 39 | // Advises the RPC system that the caller desires that the RPC call be 40 | // canceled. The RPC system may cancel it immediately, may wait awhile and 41 | // then cancel it, or may not even cancel the call at all. If the call is 42 | // canceled, the "done" callback will still be called and the RpcController 43 | // will indicate that the call failed at that time. 44 | virtual void StartCancel() {} 45 | 46 | // Server-side methods --------------------------------------------- 47 | // These calls may be made from the server side only. Their results 48 | // are undefined on the client side (may crash). 49 | 50 | // Causes Failed() to return true on the client side. "reason" will be 51 | // incorporated into the message returned by ErrorText(). If you find 52 | // you need to return machine-readable information about failures, you 53 | // should incorporate it into your response protocol buffer and should 54 | // NOT call SetFailed(). 55 | virtual void SetFailed(const std::string& reason) {} 56 | 57 | // If true, indicates that the client canceled the RPC, so the server may 58 | // as well give up on replying to it. The server should still call the 59 | // final "done" callback. 60 | virtual bool IsCanceled() const { return false; } 61 | 62 | // Asks that the given callback be called when the RPC is canceled. The 63 | // callback will always be called exactly once. If the RPC completes without 64 | // being canceled, the callback will be called after completion. If the RPC 65 | // has already been canceled when NotifyOnCancel() is called, the callback 66 | // will be called immediately. 67 | // 68 | // NotifyOnCancel() must be called no more than once per request. 69 | virtual void NotifyOnCancel(google::protobuf::Closure* callback) {} 70 | 71 | private: 72 | int connect_timeout_; 73 | int recv_timeout_; 74 | int error_code_; 75 | 76 | http::HttpRequest hreq_header_; 77 | http::HttpResponse hrsp_header_; 78 | }; 79 | } 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /rpc/rpc_impl.cc: -------------------------------------------------------------------------------- 1 | #include "rpc_impl.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace tinyco { 7 | int TinycoRpc::Serve() { 8 | auto path = hreq_.GetUriObj().path; 9 | if (method_map_->find(path) == method_map_->end()) { 10 | goto reply404; 11 | } 12 | 13 | // avoid crosses initialization 14 | { 15 | const ServerImpl::MethodDescriptor& rpc_md = (*method_map_)[path]; 16 | auto service = rpc_md.service; 17 | 18 | const google::protobuf::ServiceDescriptor* sd = service->GetDescriptor(); 19 | 20 | if (sd->method_count() == 0) { 21 | goto reply404; 22 | } 23 | 24 | auto method = sd->FindMethodByName(rpc_md.method); 25 | if (!method) { 26 | goto reply404; 27 | } 28 | 29 | LOG_DEBUG("sd fullname=%s|md fullname=%s|path=%s", sd->full_name().c_str(), 30 | method->full_name().c_str(), hreq_.GetUriObj().path.c_str()); 31 | 32 | google::protobuf::Message* request = 33 | service->GetRequestPrototype(method).New(); 34 | google::protobuf::Message* response = 35 | service->GetResponsePrototype(method).New(); 36 | 37 | LOG_DEBUG("request content size=%d", hreq_.GetContent().size()); 38 | if (!request->ParseFromString(hreq_.GetContent())) { 39 | return -1; 40 | } 41 | 42 | service->CallMethod(method, NULL, request, response, NULL); 43 | 44 | std::string strrsp; 45 | response->SerializeToString(&strrsp); 46 | hrsp_.SetContent(std::move(strrsp)); 47 | Reply(); 48 | 49 | return 0; 50 | } 51 | 52 | reply404: 53 | hrsp_.SetStatus(404); 54 | Reply(); 55 | return 0; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /rpc/rpc_impl.h: -------------------------------------------------------------------------------- 1 | #ifndef RPC_IMPL_H_ 2 | #define RPC_IMPL_H_ 3 | 4 | #include "http/http_server.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace tinyco { 10 | class TinycoRpc : public http::HttpSrvWork { 11 | public: 12 | TinycoRpc() = delete; 13 | TinycoRpc(std::unordered_map & 14 | method_map) { 15 | method_map_ = &method_map; 16 | } 17 | 18 | int Serve(); 19 | 20 | private: 21 | std::unordered_map *method_map_; 22 | }; 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /server.cc: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "util/string.h" 12 | #include "http/http_server.h" 13 | #include "json/json.h" 14 | 15 | namespace tinyco { 16 | 17 | class SignalHelper : public Work { 18 | public: 19 | static void SetServerInstance(Server *srv) { srv_ = srv; } 20 | static void AllInOneCallback(int sig, siginfo_t *sig_info, void *unused) { 21 | LOG_DEBUG("write signal notify fd=%d|sig=%d", srv_->GetSigWriteFd(), sig); 22 | int ret = Frame::send(srv_->GetSigWriteFd(), &sig, sizeof(sig), 0); 23 | LOG_DEBUG("ret = %d", ret); 24 | } 25 | 26 | int Run() { 27 | while (true) { 28 | int signo = 0; 29 | LOG_DEBUG("ready to recv signal notify fd=%d", srv_->GetSigReadFd()); 30 | int ret = Frame::recv(srv_->GetSigReadFd(), &signo, sizeof(signo), 0); 31 | if (ret < 0) { 32 | LOG_ERROR("read notifyfd_ error: ret=%d|socket=%d|error=%d", ret, 33 | srv_->GetSigReadFd(), errno); 34 | return 0; 35 | } 36 | 37 | if (srv_) srv_->SignalCallback(signo); 38 | } 39 | 40 | return 0; 41 | } 42 | 43 | private: 44 | static Server *srv_; 45 | }; 46 | 47 | Server *SignalHelper::srv_; 48 | 49 | ServerImpl::ServerImpl() : mode_(WM_UNKNOWN) {} 50 | 51 | ServerImpl::~ServerImpl() { Frame::Fini(); } 52 | 53 | int ServerImpl::Daemonize() { 54 | int fd; 55 | 56 | if (fork() != 0) exit(0); // parent exits 57 | setsid(); // create a new session 58 | 59 | if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { 60 | dup2(fd, STDIN_FILENO); 61 | dup2(fd, STDOUT_FILENO); 62 | dup2(fd, STDERR_FILENO); 63 | if (fd > STDERR_FILENO) close(fd); // if not, hold them 64 | } 65 | 66 | return 0; 67 | } 68 | 69 | int ServerImpl::Initialize(int argc, char *argv[]) { 70 | config_.reset(new Json::Value); 71 | int ret = 0; 72 | 73 | // set proc signal mask 74 | sigset_t newset, oldset; 75 | sigemptyset(&newset); 76 | sigaddset(&newset, SIGCHLD); 77 | sigaddset(&newset, SIGHUP); 78 | sigaddset(&newset, SIGUSR1); 79 | sigprocmask(SIG_BLOCK, &newset, NULL); 80 | 81 | if (LocalLog::Instance()->Initialize("tinyco") < 0) { 82 | fprintf(stderr, "fail init log util\n"); 83 | return -__LINE__; 84 | } 85 | 86 | if (!Frame::Init()) { 87 | fprintf(stderr, "fail to init frame"); 88 | return -__LINE__; 89 | } 90 | 91 | if (!ParseConfig()) { 92 | fprintf(stderr, "fail to parse config"); 93 | return -__LINE__; 94 | } 95 | 96 | if (InitSigAction() < 0) { 97 | fprintf(stderr, "fail to init sigaction"); 98 | return -__LINE__; 99 | } 100 | 101 | if ((ret = InitSrv()) < 0) { 102 | fprintf(stderr, "fail to InitSrv: ret=%d", ret); 103 | return -__LINE__; 104 | } 105 | 106 | spt_init(argc, argv); 107 | 108 | int worker_num = get_nprocs() > 0 ? get_nprocs() : 1; 109 | int childpid = 0; 110 | const std::string &worker_title = "tinyco: worker"; 111 | for (auto i = 0; i < worker_num; i++) { 112 | if ((childpid = fork()) == 0) break; 113 | if (childpid < 0) { 114 | fprintf(stderr, "fork error"); 115 | return -__LINE__; 116 | } 117 | 118 | Worker w = {worker_title, childpid}; 119 | worker_processes_.push_back(w); 120 | } 121 | 122 | // child init 123 | if (0 == childpid) { 124 | 125 | mode_ = WM_WORKER; 126 | sigemptyset(&newset); 127 | sigprocmask(SIG_SETMASK, &newset, NULL); 128 | 129 | SetProcTitle(worker_title.c_str()); 130 | 131 | return 0; 132 | } 133 | 134 | // master init 135 | mode_ = WM_MASTER; 136 | sigemptyset(&newset); 137 | sigprocmask(SIG_SETMASK, &newset, NULL); 138 | 139 | SetProcTitle("tinyco: master"); 140 | 141 | return 0; 142 | } 143 | 144 | bool ServerImpl::AddRpcService(google::protobuf::Service *service, 145 | const std::string &restful_mappings) { 146 | // contruct service map 147 | auto methods = string::Split(restful_mappings, ','); 148 | for (auto &method : methods) { 149 | auto path_to_method = string::Split(method, '>'); 150 | 151 | if (path_to_method.size() != 2) return false; 152 | 153 | string::trim(path_to_method[0]); 154 | string::trim(path_to_method[1]); 155 | 156 | MethodDescriptor md; 157 | md.service = service; 158 | md.method = path_to_method[1]; 159 | 160 | // check whether method is in service 161 | auto pmd = service->GetDescriptor()->FindMethodByName(md.method); 162 | if (!pmd) { 163 | LOG_ERROR("no corresponding method: %s", md.method.c_str()); 164 | return false; 165 | } 166 | 167 | md.full_name = pmd->full_name(); 168 | 169 | // path to method descriptor 170 | method_map_[path_to_method[0]] = md; 171 | } 172 | 173 | for (auto &ite : method_map_) { 174 | LOG_INFO("%s => %s.%s added", ite.first.c_str(), 175 | ite.second.service->GetDescriptor()->full_name().c_str(), 176 | ite.second.method.c_str()); 177 | } 178 | 179 | return true; 180 | } 181 | 182 | bool ServerImpl::ParseConfig() { 183 | Json::CharReaderBuilder b; 184 | std::shared_ptr reader(b.newCharReader()); 185 | JSONCPP_STRING errs; 186 | 187 | std::ifstream t("./conf/tinyco.json"); 188 | std::string config_data((std::istreambuf_iterator(t)), 189 | std::istreambuf_iterator()); 190 | 191 | LOG_INFO("config = %s", config_data.c_str()); 192 | if (!reader->parse(config_data.c_str(), 193 | config_data.c_str() + config_data.size(), config_.get(), 194 | &errs)) { 195 | LOG_ERROR("fail to parse config, please check config"); 196 | return false; 197 | } 198 | 199 | if (!config_->isMember("udp") && config_->isMember("tcp") && 200 | !config_->isMember("http")) { 201 | LOG_ERROR("no server item: udp, tcp or http"); 202 | return false; 203 | } 204 | 205 | return true; 206 | } 207 | 208 | int ServerImpl::InitSigAction() { 209 | SignalHelper::SetServerInstance(this); 210 | struct sigaction sa; 211 | sa.sa_sigaction = SignalHelper::AllInOneCallback; 212 | sa.sa_flags = 0; 213 | sigemptyset(&sa.sa_mask); 214 | sigaction(SIGUSR1, &sa, NULL); 215 | sigaction(SIGCHLD, &sa, NULL); 216 | sigaction(SIGHUP, &sa, NULL); 217 | 218 | int sockpair[2] = {0}; 219 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) == -1) { 220 | fprintf(stderr, "create unnamed socket pair failed:%s\n", strerror(errno)); 221 | exit(-1); 222 | } 223 | 224 | // 0 read 225 | // 1 write 226 | sig_read_fd_ = sockpair[0]; 227 | sig_write_fd_ = sockpair[1]; 228 | network::SetNonBlock(sig_read_fd_); 229 | network::SetNonBlock(sig_write_fd_); 230 | LOG_INFO("signal notify socket: read=%d|write=%d", sig_read_fd_, 231 | sig_write_fd_); 232 | 233 | Frame::CreateThread(new SignalHelper()); 234 | return 0; 235 | } 236 | 237 | struct ListenItem { 238 | std::string proto; 239 | network::IP ip; 240 | uint16_t port; 241 | 242 | bool Parse(const std::string &proto, const std::string &listen_config) { 243 | std::vector items = string::Split(listen_config, ':'); 244 | if (items.size() != 2) { 245 | return false; 246 | } 247 | 248 | this->proto = proto; 249 | network::IP ip; 250 | if (!network::GetEthAddr(items[0].c_str(), &ip)) { 251 | return false; 252 | } 253 | 254 | this->ip = ip; 255 | 256 | port = std::atoi(items[1].c_str()); 257 | return true; 258 | } 259 | }; 260 | 261 | int ServerImpl::InitListener(const std::string &proto) { 262 | ListenItem li; 263 | const Json::Value &config = (*config_); 264 | if (config_->isMember(proto)) { 265 | for (Json::ArrayIndex i = 0; i < config[proto].size(); i++) { 266 | LOG_DEBUG("listen %s", config[proto][i]["listen"].asString().c_str()); 267 | if (li.Parse(proto, config[proto][i]["listen"].asString())) { 268 | Listener *l = NULL; 269 | if ("tcp" == proto) 270 | l = new TcpListener(); 271 | else if ("udp" == proto) 272 | l = new UdpListener(); 273 | else 274 | continue; 275 | 276 | if (l->Listen(li.ip, li.port) < 0) { 277 | return -__LINE__; 278 | } 279 | LOG_INFO("proto=%s", proto.c_str()); 280 | listeners_.insert(l); 281 | } else { 282 | return -__LINE__; 283 | } 284 | } 285 | } 286 | 287 | return 0; 288 | } 289 | 290 | int ServerImpl::InitSrv() { 291 | int ret = 0; 292 | if ((ret = InitListener("tcp")) < 0) { 293 | return -__LINE__; 294 | } 295 | 296 | if ((ret = InitListener("udp")) < 0) { 297 | return -__LINE__; 298 | } 299 | 300 | Daemonize(); 301 | 302 | return 0; 303 | } 304 | 305 | int ServerImpl::Run() { 306 | std::shared_ptr me(Frame::InitHereAsNewThread()); 307 | 308 | if (WM_MASTER == mode_) { 309 | MasterRun(); 310 | } else if (WM_WORKER == mode_) { 311 | WorkerRun(); 312 | } else { 313 | fprintf(stderr, "unknown work mode"); 314 | return -__LINE__; 315 | } 316 | 317 | return 0; 318 | } 319 | 320 | int ServerImpl::ServerLoop() { return 0; } 321 | 322 | void ServerImpl::SignalCallback(int signo) { 323 | LOG_DEBUG("recv signo = %d", signo); 324 | if (WM_MASTER == mode_) { 325 | switch (signo) { 326 | case SIGCHLD: 327 | GetWorkerStatus(); 328 | break; 329 | } 330 | } else if (WM_WORKER == mode_) { 331 | switch (signo) { 332 | case SIGHUP: 333 | graceful_shutdown_ = true; 334 | break; 335 | } 336 | } 337 | } 338 | 339 | void ServerImpl::FreeAllListener() { 340 | for (auto i = listeners_.begin(); i != listeners_.end(); i++) { 341 | (*i)->Destroy(); 342 | } 343 | } 344 | 345 | void ServerImpl::SetProcTitle(const char *title) { setproctitle("%s", title); } 346 | 347 | void ServerImpl::MasterRun() { 348 | LOG_DEBUG("master run"); 349 | 350 | while (true) { 351 | Frame::Sleep(1000); 352 | LOG_DEBUG("in master main loop"); 353 | 354 | for (auto &pid : restart_worker_pid_) { 355 | LOG_INFO("pid=%d", pid); 356 | Worker *w = NULL; 357 | for (auto &ite : worker_processes_) { 358 | if (ite.pid == pid) { 359 | w = &ite; 360 | } 361 | } 362 | 363 | uint64_t check_data = pid; 364 | for (auto &ite : listeners_) { 365 | ite->GetMtx()->ForcedUnlockIfNeed(&check_data); 366 | } 367 | 368 | int new_worker_pid = fork(); 369 | if (0 == new_worker_pid) { 370 | SetProcTitle("tinyco: worker"); 371 | WorkerRun(); 372 | break; 373 | } else if (new_worker_pid > 0) { 374 | w->pid = new_worker_pid; 375 | } else { 376 | LOG_ERROR("WARNING: fail to restart"); 377 | continue; 378 | } 379 | } 380 | 381 | restart_worker_pid_.clear(); 382 | } 383 | } 384 | 385 | void ServerImpl::WorkerRun() { 386 | LOG_DEBUG("worker run"); 387 | 388 | for (auto ite : listeners_) { 389 | if (ite->GetProto() == "tcp") { 390 | Frame::CreateThread(new TcpSrvWork(ite, this, this)); 391 | } else if (ite->GetProto() == "udp") { 392 | Frame::CreateThread(new UdpSrvWork(ite, this)); 393 | } else 394 | continue; 395 | } 396 | 397 | while (true) { 398 | LOG_DEBUG("in worker main loop"); 399 | Frame::Sleep(1000); 400 | ServerLoop(); 401 | 402 | if (GracefulShutdown()) { 403 | if (GetConnSize() == 0) { 404 | LOG_DEBUG("graceful shutdown"); 405 | break; 406 | } 407 | } 408 | } 409 | } 410 | void ServerImpl::GetWorkerStatus() { 411 | int pid; 412 | int status; 413 | for (;;) { 414 | pid = waitpid(-1, &status, WNOHANG); 415 | 416 | if (0 == pid) { 417 | return; 418 | } 419 | 420 | if (-1 == pid) { 421 | if (EINTR == errno) { 422 | continue; 423 | } 424 | 425 | return; 426 | } 427 | 428 | int restart_pid = 0; 429 | for (auto &w : worker_processes_) { 430 | if (w.pid == pid) { 431 | restart_pid = pid; 432 | LOG_INFO("worker %d exit and restart", pid); 433 | break; 434 | } 435 | } 436 | 437 | if (-1 == restart_pid) { 438 | LOG_ERROR("WARNING: unknow pid: %d", pid); 439 | } 440 | 441 | restart_worker_pid_.push_back(restart_pid); 442 | return; 443 | } 444 | } 445 | } 446 | -------------------------------------------------------------------------------- /server.h: -------------------------------------------------------------------------------- 1 | #ifndef SERVER_H_ 2 | #define SERVER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "frame.h" 11 | #include "util/network.h" 12 | 13 | namespace Json { 14 | class Value; 15 | }; 16 | 17 | namespace tinyco { 18 | 19 | class Server; 20 | class TcpReqWork; 21 | class UdpReqWork; 22 | 23 | class BusinessWorkBuilder { 24 | public: 25 | virtual TcpReqWork *BuildStreamBusinessWork(uint32_t port) = 0; 26 | virtual UdpReqWork *BuildUdpBusinessWork(uint32_t port) = 0; 27 | }; 28 | 29 | class ConnTracker { 30 | public: 31 | ConnTracker() : graceful_shutdown_(false) {} 32 | struct Conn {}; 33 | 34 | void NewConn(int fd, const Conn &conn) { 35 | conns_.empty(); 36 | conns_[fd] = conn; 37 | } 38 | void RemoveConn(int fd) { conns_.erase(fd); } 39 | uint32_t GetConnSize() { return conns_.size(); } 40 | bool GracefulShutdown() const { return graceful_shutdown_; } 41 | 42 | protected: 43 | bool graceful_shutdown_; 44 | 45 | private: 46 | std::map conns_; 47 | }; 48 | 49 | class TcpReqWork : public Work { 50 | public: 51 | TcpReqWork() : sockfd_(-1), ct_(NULL) {} 52 | void SetFd(int sockfd) { sockfd_ = sockfd; } 53 | void SetConnTracker(ConnTracker *ct) { ct_ = ct; } 54 | virtual ~TcpReqWork() { 55 | if (ct_) ct_->RemoveConn(sockfd_); 56 | if (sockfd_ >= 0) close(sockfd_); 57 | } 58 | 59 | protected: 60 | int sockfd_; 61 | ConnTracker *ct_; 62 | }; 63 | 64 | class TcpSrvWork : public Work { 65 | public: 66 | TcpSrvWork(Listener *listener, BusinessWorkBuilder *work_builder, 67 | ConnTracker *ct) 68 | : listener_(listener), work_builder_(work_builder), ct_(ct) {} 69 | virtual ~TcpSrvWork() { delete listener_; } 70 | 71 | int Run() { 72 | int ret = RunTcpSrv(); 73 | if (ret < 0) { 74 | LOG("server error: %d", ret); 75 | exit(EXIT_FAILURE); 76 | } 77 | } 78 | 79 | private: 80 | int RunTcpSrv() { 81 | int accepted = 0; 82 | 83 | while (true) { 84 | // avoid thundering herd 85 | if (accepted++ > 15) { 86 | Frame::Sleep(50); 87 | accepted = 0; 88 | continue; 89 | } 90 | 91 | if (listener_->GetMtx()->TryLock() < 0) { 92 | // lock error, schedule out 93 | Frame::Sleep(50); 94 | continue; 95 | } 96 | 97 | LOG_DEBUG("accept on listenfd=%d", listener_->GetSocketFd()); 98 | 99 | struct sockaddr_in server, client; 100 | socklen_t socklen = sizeof(client); 101 | auto fd = Frame::accept(listener_->GetSocketFd(), 102 | (struct sockaddr *)&client, &socklen); 103 | if (fd < 0) { 104 | LOG("Frame::accept error: ret=%d|errno=%d", fd, errno); 105 | break; 106 | } 107 | 108 | listener_->GetMtx()->Unlock(); 109 | 110 | if (network::SetNonBlock(fd) < 0) { 111 | LOG_ERROR("set nonblock error"); 112 | break; 113 | } 114 | 115 | if (!work_builder_) { 116 | close(fd); 117 | continue; 118 | } 119 | 120 | auto b = work_builder_->BuildStreamBusinessWork(listener_->GetPort()); 121 | b->SetFd(fd); 122 | 123 | if (ct_) { 124 | b->SetConnTracker(ct_); 125 | ConnTracker::Conn conn; 126 | ct_->NewConn(fd, conn); 127 | } 128 | 129 | Frame::CreateThread(b); 130 | 131 | if (ct_->GracefulShutdown()) { 132 | break; 133 | } 134 | } 135 | 136 | return 0; 137 | } 138 | 139 | private: 140 | Listener *listener_; 141 | BusinessWorkBuilder *work_builder_; 142 | ConnTracker *ct_; 143 | }; 144 | 145 | struct UdpReqInfo { 146 | uint32_t sockfd; 147 | std::string reqpkg; 148 | sockaddr_in srcaddr; 149 | sockaddr_in dstaddr; 150 | }; 151 | 152 | class UdpReqWork : public Work { 153 | public: 154 | UdpReqWork() {} 155 | virtual ~UdpReqWork() {} 156 | void SetReqInfo(const UdpReqInfo &req) { req_ = req; } 157 | int Run() = 0; 158 | int Reply(const std::string &rsp) { 159 | return Frame::sendto(req_.sockfd, rsp.data(), rsp.size(), 0, 160 | (sockaddr *)&req_.srcaddr, sizeof(req_.srcaddr)); 161 | } 162 | 163 | protected: 164 | UdpReqInfo req_; 165 | }; 166 | 167 | class UdpSrvWork : public Work { 168 | public: 169 | UdpSrvWork(Listener *listener, BusinessWorkBuilder *work_builder) 170 | : listener_(listener), work_builder_(work_builder) {} 171 | virtual ~UdpSrvWork() { delete listener_; } 172 | int Run() { 173 | int ret = RunUdpSrv(); 174 | if (ret < 0) { 175 | LOG("server error: %d", ret); 176 | exit(EXIT_FAILURE); 177 | } 178 | } 179 | 180 | private: 181 | int RunUdpSrv() { 182 | char recvbuf[65536]; 183 | 184 | sockaddr_in server = {0}; 185 | server.sin_family = AF_INET; 186 | server.sin_addr.s_addr = listener_->GetIP().af_inet_ip; 187 | server.sin_port = htons(listener_->GetPort()); 188 | while (true) { 189 | if (listener_->GetMtx()->TryLock() < 0) { 190 | // lock error, schedule out 191 | Frame::Sleep(500); 192 | continue; 193 | } 194 | 195 | sockaddr_in client = {0}; 196 | socklen_t clisocklen = sizeof(client); 197 | int ret = 198 | Frame::recvfrom(listener_->GetSocketFd(), recvbuf, sizeof(recvbuf), 0, 199 | (struct sockaddr *)&client, &clisocklen); 200 | if (ret > 0) { 201 | listener_->GetMtx()->Unlock(); 202 | 203 | // create tread to handle request 204 | UdpReqInfo req; 205 | req.srcaddr = client; 206 | req.dstaddr = server; 207 | req.reqpkg.assign(recvbuf, ret); 208 | req.sockfd = listener_->GetSocketFd(); 209 | 210 | if (!work_builder_) { 211 | continue; 212 | } 213 | 214 | auto b = work_builder_->BuildUdpBusinessWork(listener_->GetPort()); 215 | b->SetReqInfo(req); 216 | 217 | Frame::CreateThread(b); 218 | } else if (ret < 0) { 219 | LOG("Frame::recvfrom error: %d", ret); 220 | return -__LINE__; 221 | } 222 | } 223 | 224 | return 0; 225 | } 226 | 227 | BusinessWorkBuilder *work_builder_; 228 | Listener *listener_; 229 | }; 230 | 231 | inline int TcpSrv(uint32_t ip, uint16_t port, 232 | BusinessWorkBuilder *work_builder) { 233 | if (!Frame::Init()) return -__LINE__; 234 | 235 | auto l = new TcpListener(); 236 | if (l->Listen(network::IP{ip}, port) < 0) return -__LINE__; 237 | 238 | auto t = Frame::CreateThread(new TcpSrvWork(l, work_builder, NULL)); 239 | while (!t.IsDead()) { 240 | Frame::Sleep(1000); 241 | } 242 | delete l; 243 | 244 | Frame::Fini(); 245 | return 0; 246 | } 247 | 248 | inline int UdpSrv(uint32_t ip, uint16_t port, 249 | BusinessWorkBuilder *work_builder) { 250 | if (!Frame::Init()) return -__LINE__; 251 | auto l = new UdpListener(); 252 | if (l->Listen(network::IP{ip}, port) < 0) return -__LINE__; 253 | 254 | auto t = Frame::CreateThread(new UdpSrvWork(l, work_builder)); 255 | while (!t.IsDead()) { 256 | Frame::Sleep(1000); 257 | } 258 | delete l; 259 | 260 | Frame::Fini(); 261 | return 0; 262 | } 263 | 264 | class Server { 265 | public: 266 | Server() : sig_read_fd_(-1), sig_write_fd_(-1) {} 267 | virtual ~Server() {} 268 | virtual int Initialize(int argc, char *argv[]) = 0; 269 | virtual int Run() = 0; 270 | virtual void SignalCallback(int signo) = 0; 271 | virtual int GetSigReadFd() const { return sig_read_fd_; } 272 | virtual int GetSigWriteFd() const { return sig_write_fd_; } 273 | virtual int Daemonize() = 0; 274 | 275 | protected: 276 | virtual int ServerLoop() = 0; 277 | 278 | // use to notify eventloop to do signal action 279 | int sig_read_fd_; 280 | int sig_write_fd_; 281 | }; 282 | 283 | class ServerImpl : public Server, 284 | public BusinessWorkBuilder, 285 | public ConnTracker { 286 | public: 287 | ServerImpl(); 288 | virtual ~ServerImpl(); 289 | virtual int Initialize(int argc, char *argv[]); 290 | virtual int Run(); 291 | virtual void SignalCallback(int signo); 292 | 293 | // restful_mappings example: 294 | // "/v1/account/add > add," 295 | // "/v1/account/del > del," 296 | // "/v1/account/query > query" 297 | bool AddRpcService(google::protobuf::Service *service, 298 | const std::string &restful_mappings); 299 | 300 | struct MethodDescriptor { 301 | google::protobuf::Service *service; 302 | std::string method; 303 | std::string full_name; 304 | }; 305 | 306 | private: 307 | virtual int Daemonize(); 308 | 309 | bool ParseConfig(); 310 | virtual int ServerLoop(); 311 | int InitSigAction(); 312 | int InitSrv(); 313 | int InitListener(const std::string &proto); 314 | void FreeAllListener(); 315 | void SetProcTitle(const char *title); 316 | 317 | void MasterRun(); 318 | void WorkerRun(); 319 | 320 | std::shared_ptr config_; 321 | std::set listeners_; 322 | std::set clients_; 323 | 324 | // master 325 | struct Worker { 326 | std::string name; 327 | int pid; 328 | }; 329 | 330 | std::vector worker_processes_; 331 | 332 | enum WorkMode { 333 | WM_UNKNOWN = 0, 334 | WM_MASTER = 1, 335 | WM_WORKER = 2, 336 | }; 337 | WorkMode mode_; 338 | 339 | // worker abnormal exit(segment fault, etc...) 340 | std::vector restart_worker_pid_; 341 | void GetWorkerStatus(); 342 | 343 | protected: 344 | std::unordered_map method_map_; 345 | }; 346 | 347 | extern "C" { 348 | void setproctitle(const char *fmt, ...); 349 | void spt_init(int argc, char *argv[]); 350 | } 351 | } 352 | 353 | #endif 354 | -------------------------------------------------------------------------------- /static/img/tinyco_perf_8c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daoluan/tinyco/f4d1eee561e4f26beb4b6285820708e479494b22/static/img/tinyco_perf_8c.jpg -------------------------------------------------------------------------------- /thread.cc: -------------------------------------------------------------------------------- 1 | #include "thread.h" 2 | 3 | #include "frame.h" 4 | 5 | namespace tinyco { 6 | bool Stack::Init(size_t size) { 7 | if (stack_) delete stack_; 8 | 9 | size_ = size; 10 | stack_ = new char[size_]; 11 | return true; 12 | } 13 | 14 | char *Stack::GetStackBeginPoint() { return stack_; } 15 | 16 | size_t Stack::Size() { return size_; } 17 | 18 | Thread::Thread() {} 19 | 20 | Thread::~Thread() {} 21 | 22 | bool Thread::Init() { 23 | if (!stack_.Init(Stack::kMaxStackSize)) { 24 | return false; 25 | } 26 | 27 | state_ = TS_STOP; 28 | return true; 29 | } 30 | 31 | void Thread::RestoreContext() { setcontext(&uc_); } 32 | 33 | void Thread::Schedule() { getcontext(&uc_); } 34 | 35 | void Thread::SetContext(BeginFrom func, void *arg) { 36 | fun_ = func; 37 | getcontext(&uc_); 38 | uc_.uc_stack.ss_sp = stack_.GetStackBeginPoint(); 39 | uc_.uc_stack.ss_size = stack_.Size(); 40 | uc_.uc_link = NULL; 41 | uc_.uc_stack.ss_flags = 0; 42 | makecontext(&uc_, reinterpret_cast(func), 1, arg); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /thread.h: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_H_ 2 | #define THREAD_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace tinyco { 11 | class Stack { 12 | public: 13 | Stack(size_t size) { 14 | if (size > kMaxStackSize) size = kMaxStackSize; 15 | size_ = size; 16 | stack_ = new char[size_]; 17 | } 18 | 19 | Stack() : stack_(NULL), size_(0) {} 20 | 21 | virtual ~Stack() { 22 | delete stack_; 23 | stack_ = NULL; 24 | } 25 | 26 | bool Init(size_t size); 27 | 28 | char *GetStackBeginPoint(); 29 | 30 | size_t Size(); 31 | 32 | public: 33 | const static size_t kMaxStackSize = 128 * 1024; 34 | 35 | private: 36 | char *stack_; 37 | size_t size_; 38 | }; 39 | 40 | class Thread { 41 | public: 42 | Thread(); 43 | virtual ~Thread(); 44 | 45 | bool Init(); 46 | 47 | enum ThreadState { 48 | TS_STOP, 49 | TS_RUNNABLE, 50 | TS_PENDING, 51 | TS_DEAD, 52 | }; 53 | 54 | void SetState(int state) { state_ = state; } 55 | int GetState() const { return state_; } 56 | 57 | // restore me 58 | void RestoreContext(); 59 | 60 | // schedule me out 61 | void Schedule(); 62 | 63 | typedef int (*BeginFrom)(void *argc); 64 | typedef void (*ContextFun)(); 65 | void SetContext(BeginFrom func, void *argc); 66 | 67 | void Pending(uint64_t ms) { wakeup_ts_ = ms; } 68 | 69 | uint64_t GetWakeupTime() const { return wakeup_ts_; } 70 | 71 | private: 72 | BeginFrom fun_; 73 | Stack stack_; 74 | int state_; 75 | ucontext_t uc_; 76 | int thread_id_; 77 | Thread *parent_; 78 | uint64_t wakeup_ts_; 79 | }; 80 | 81 | class ThreadWrapper { 82 | public: 83 | explicit ThreadWrapper(Thread *t) : t(t) {} 84 | bool IsDead() { return t->GetState() == Thread::TS_DEAD; } 85 | bool IsValid() { return !!t; } 86 | 87 | private: 88 | Thread *t; 89 | }; 90 | } 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /util/defer.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFER_H_ 2 | #define DEFER_H_ 3 | 4 | namespace tinyco { 5 | namespace util { 6 | 7 | class defer { 8 | public: 9 | defer(std::function &&t) : t(t) {} 10 | ~defer() { t(); } 11 | 12 | private: 13 | std::function t; 14 | }; 15 | } 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /util/log.cc: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "util/time.h" 11 | 12 | namespace tinyco { 13 | 14 | #define COMMONLOG() \ 15 | va_list args; \ 16 | va_start(args, fmt); \ 17 | CommonLog(uin, line, func, fmt, &args); \ 18 | va_end(args); 19 | 20 | inline std::ifstream::pos_type filesize(const char *filename) { 21 | std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary); 22 | return in.tellg(); 23 | } 24 | 25 | Log *LocalLog::Instance() { 26 | static LocalLog ll; 27 | return ≪ 28 | } 29 | 30 | int LocalLog::Initialize(const void *arg) { 31 | if (!arg) return -1; 32 | 33 | const char *filepath = static_cast(arg); 34 | current_file_ = filepath; 35 | current_file_ += ".log"; 36 | filepath_ = filepath; 37 | 38 | file_.open(current_file_.c_str(), std::ofstream::out | std::ofstream::app); 39 | return 0; 40 | } 41 | 42 | #define COMMONLOG() \ 43 | va_list args; \ 44 | va_start(args, fmt); \ 45 | CommonLog(uin, line, func, fmt, &args); \ 46 | va_end(args); 47 | 48 | void LocalLog::Debug(uint64_t uin, uint32_t line, const char *func, 49 | const char *fmt, ...) { 50 | if (loglevel_ > LL_DEBUG) return; 51 | 52 | COMMONLOG(); 53 | } 54 | 55 | void LocalLog::Info(uint64_t uin, uint32_t line, const char *func, 56 | const char *fmt, ...) { 57 | if (loglevel_ > LL_INFO) return; 58 | 59 | COMMONLOG(); 60 | } 61 | 62 | void LocalLog::Warning(uint64_t uin, uint32_t line, const char *func, 63 | const char *fmt, ...) { 64 | if (loglevel_ > LL_WARNING) return; 65 | 66 | COMMONLOG(); 67 | } 68 | 69 | void LocalLog::Error(uint64_t uin, uint32_t line, const char *func, 70 | const char *fmt, ...) { 71 | if (loglevel_ > LL_ERROR) return; 72 | 73 | COMMONLOG(); 74 | } 75 | 76 | void Log::CommonLog(uint64_t uin, uint32_t line, const char *func, 77 | const char *fmt, va_list *args) { 78 | content_.clear(); 79 | AppendLogItemHeader(uin, line, func); 80 | char tmp[kLogItemSize]; 81 | vsnprintf(tmp, sizeof(tmp), fmt, *args); 82 | content_.append(tmp); 83 | WriteLog(); 84 | } 85 | 86 | uint32_t Log::AppendLogItemHeader(uint64_t uin, uint32_t line, 87 | const char *func) { 88 | std::stringstream ss; 89 | uint64_t now = time::mstime(); 90 | time_t nows = static_cast(now / 1000llu); 91 | 92 | auto tminfo = localtime(&nows); 93 | 94 | char tmp[32]; 95 | uint32_t sz = strftime(tmp, sizeof(tmp), "[%Y:%m:%d %H:%M:%S", tminfo); 96 | ss << tmp << "." << std::to_string(now % 1000llu) << "]"; 97 | ss << "[" << func << "]"; 98 | ss << "[" << line << "]"; 99 | ss << "[" << uin << "] "; 100 | content_ = ss.str(); 101 | return content_.size(); 102 | } 103 | 104 | void LocalLog::WriteLog() { 105 | const uint32_t kMaxFilesize = 1024 * 1024; 106 | const uint32_t kMaxFilsNum = 100; 107 | 108 | // write to file 109 | if (!file_.is_open()) { 110 | #define ANSI_COLOR_RED "\x1b[31m" 111 | #define ANSI_COLOR_RESET "\x1b[0m" 112 | 113 | fprintf(stderr, "%s" ANSI_COLOR_RED 114 | "(uninitialized LocalLog obj)" ANSI_COLOR_RESET "\n", 115 | content_.c_str()); 116 | return; 117 | } 118 | 119 | file_ << content_.c_str() << std::endl; 120 | file_.flush(); 121 | 122 | // only workers reopen log file in N seconds 123 | if (getppid() > 1) { 124 | static uint32_t lasts = ::time(NULL); 125 | uint32_t nows = ::time(NULL); 126 | if (lasts + 10 < nows) { 127 | if (file_.is_open()) file_.close(); 128 | file_.open(current_file_.c_str(), 129 | std::ofstream::out | std::ofstream::app); 130 | nows = nows; 131 | } 132 | } 133 | 134 | // simple log rotation 135 | // only master retate the log. workers reopen log file every N seconds. it 136 | // may cause some workers log would write into old files in N seconds. 137 | if (getppid() == 1) { 138 | // check log file size and rename log file if needed 139 | uint32_t fs = filesize(current_file_.c_str()); 140 | if (fs < kMaxFilesize) { 141 | return; 142 | } 143 | 144 | for (int i = kMaxFilsNum - 1; i >= 0; i--) { 145 | std::string fp; 146 | if (i > 0) 147 | fp = filepath_ + "_" + std::to_string(i) + ".log"; 148 | else 149 | fp = filepath_ + ".log"; 150 | 151 | if (access(fp.c_str(), F_OK) != -1) { 152 | if (i == kMaxFilsNum - 1) { 153 | remove(fp.c_str()); 154 | } else { 155 | const std::string &fp_new = 156 | filepath_ + "_" + std::to_string(i + 1) + ".log"; 157 | rename(fp.c_str(), fp_new.c_str()); 158 | } 159 | } 160 | } 161 | 162 | file_.close(); 163 | file_.open(current_file_.c_str(), std::ofstream::out | std::ofstream::app); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /util/log.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef LOG_H_ 3 | #define LOG_H_ 4 | 5 | #include 6 | 7 | namespace tinyco { 8 | 9 | enum LOGLEVEL { 10 | LL_DEBUG = 0, 11 | LL_INFO, 12 | LL_WARNING, 13 | LL_ERROR, 14 | }; 15 | 16 | class Log { 17 | public: 18 | virtual int Initialize(const void *arg) = 0; 19 | virtual void SetLogLevel(int level) { loglevel_ = level; } 20 | virtual void Debug(uint64_t uin, uint32_t line, const char *func, 21 | const char *fmt, ...) = 0; 22 | virtual void Info(uint64_t uin, uint32_t line, const char *func, 23 | const char *fmt, ...) = 0; 24 | virtual void Warning(uint64_t uin, uint32_t line, const char *func, 25 | const char *fmt, ...) = 0; 26 | virtual void Error(uint64_t uin, uint32_t line, const char *func, 27 | const char *fmt, ...) = 0; 28 | 29 | protected: 30 | virtual void CommonLog(uint64_t uin, uint32_t line, const char *func, 31 | const char *fmt, va_list *args); 32 | 33 | virtual uint32_t AppendLogItemHeader(uint64_t uin, uint32_t line, 34 | const char *func); 35 | 36 | virtual void WriteLog() {} 37 | 38 | protected: 39 | int loglevel_; 40 | std::string content_; 41 | const static uint32_t kLogItemSize = 2048; 42 | }; 43 | 44 | class LocalLog : public Log { 45 | public: 46 | static Log *Instance(); 47 | 48 | virtual int Initialize(const void *arg); 49 | 50 | virtual void Debug(uint64_t uin, uint32_t line, const char *func, 51 | const char *fmt, ...); 52 | 53 | virtual void Info(uint64_t uin, uint32_t line, const char *func, 54 | const char *fmt, ...); 55 | 56 | virtual void Warning(uint64_t uin, uint32_t line, const char *func, 57 | const char *fmt, ...); 58 | 59 | virtual void Error(uint64_t uin, uint32_t line, const char *func, 60 | const char *fmt, ...); 61 | 62 | private: 63 | virtual void WriteLog(); 64 | 65 | private: 66 | std::string filepath_; 67 | std::string current_file_; 68 | std::ofstream file_; 69 | }; 70 | 71 | #if 0 72 | #define LOG(fmt, arg...) \ 73 | printf("[%s][%s][%u]: " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##arg) 74 | 75 | #define LOG_ERROR(fmt, arg...) \ 76 | printf("[%s][%s][%u]: " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##arg) 77 | 78 | #define LOG_WARNING(fmt, arg...) \ 79 | printf("[%s][%s][%u]: " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##arg) 80 | 81 | #define LOG_NOTICE(fmt, arg...) \ 82 | printf("[%s][%s][%u]: " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##arg) 83 | 84 | #define LOG_INFO(fmt, arg...) \ 85 | printf("[%s][%s][%u]: " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##arg) 86 | 87 | #define LOG_DEBUG(fmt, arg...) \ 88 | printf("[%s][%s][%u]: " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##arg) 89 | #endif 90 | 91 | #if 1 92 | #define LOG(fmt, arg...) \ 93 | LocalLog::Instance()->Debug(LL_DEBUG, __LINE__, __FUNCTION__, fmt, ##arg) 94 | 95 | #define LOG_ERROR(fmt, arg...) \ 96 | LocalLog::Instance()->Error(LL_ERROR, __LINE__, __FUNCTION__, fmt, ##arg) 97 | 98 | #define LOG_WARNING(fmt, arg...) \ 99 | LocalLog::Instance()->Warning(LL_WARNING, __LINE__, __FUNCTION__, fmt, ##arg) 100 | 101 | #define LOG_INFO(fmt, arg...) \ 102 | LocalLog::Instance()->Info(LL_INFO, __LINE__, __FUNCTION__, fmt, ##arg) 103 | 104 | #define LOG_DEBUG(fmt, arg...) \ 105 | LocalLog::Instance()->Debug(LL_DEBUG, __LINE__, __FUNCTION__, fmt, ##arg) 106 | #endif 107 | } 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /util/network.cc: -------------------------------------------------------------------------------- 1 | #include "network.h" 2 | 3 | #include 4 | #include 5 | #include /* for strncpy */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace tinyco { 15 | namespace network { 16 | 17 | bool GetEthAddr(const char *eth, IP *ip) { 18 | int fd; 19 | struct ifreq ifr; 20 | 21 | fd = socket(AF_INET, SOCK_DGRAM, 0); 22 | 23 | /* I want to get an IPv4 IP address */ 24 | ifr.ifr_addr.sa_family = AF_INET; 25 | 26 | /* I want IP address attached to "eth0" */ 27 | strncpy(ifr.ifr_name, eth, IFNAMSIZ - 1); 28 | 29 | ioctl(fd, SIOCGIFADDR, &ifr); 30 | 31 | close(fd); 32 | 33 | ip->af_inet_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; 34 | 35 | return true; 36 | } 37 | 38 | int SetNonBlock(int fd) { 39 | auto flags = fcntl(fd, F_GETFL, 0); 40 | if (flags < 0) { 41 | return flags; 42 | } 43 | 44 | flags = flags | O_NONBLOCK; 45 | return fcntl(fd, F_SETFL, flags); 46 | } 47 | 48 | int SetReuseAddr(int fd) { 49 | int enable = 1; 50 | return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); 51 | } 52 | 53 | std::string ntoa(const IP &ip) { 54 | in_addr ia; 55 | ia.s_addr = ip.af_inet_ip; 56 | return inet_ntoa(ia); 57 | } 58 | 59 | std::string InetAddrToString(const sockaddr_in &addr) { 60 | const std::string &ip = inet_ntoa(addr.sin_addr); 61 | return ip + ":" + std::to_string(ntohs(addr.sin_port)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /util/network.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef NETWORK_H_ 3 | #define NETWORK_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace tinyco { 12 | namespace network { 13 | 14 | union IP { 15 | uint32_t af_inet_ip; 16 | uint64_t af_inet6_ip; 17 | }; 18 | 19 | bool GetEthAddr(const char *eth, IP *ip); 20 | 21 | int SetNonBlock(int fd); 22 | 23 | int SetReuseAddr(int fd); 24 | 25 | std::string ntoa(const IP &ip); 26 | 27 | std::string InetAddrToString(const sockaddr_in &addr); 28 | 29 | struct EndPoint { 30 | IP ip; 31 | uint16_t port; 32 | 33 | EndPoint() : ip(IP{0}), port(0) {} 34 | EndPoint(IP ip, uint16_t port) : ip(ip), port(port) {} 35 | 36 | std::string ToString() { return ntoa(ip) + ":" + std::to_string(port); } 37 | }; 38 | } 39 | } 40 | #endif 41 | -------------------------------------------------------------------------------- /util/setproctitle.c: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * setproctitle.c - Linux/Darwin setproctitle. 3 | * -------------------------------------------------------------------------- 4 | * Copyright (C) 2010 William Ahern 5 | * Copyright (C) 2013 Salvatore Sanfilippo 6 | * Copyright (C) 2013 Stam He 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to permit 13 | * persons to whom the Software is furnished to do so, subject to the 14 | * following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included 17 | * in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | * ========================================================================== 27 | */ 28 | #ifndef _GNU_SOURCE 29 | #define _GNU_SOURCE 30 | #endif 31 | 32 | #include /* NULL size_t */ 33 | #include /* va_list va_start va_end */ 34 | #include /* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */ 35 | #include /* vsnprintf(3) snprintf(3) */ 36 | 37 | #include /* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */ 38 | 39 | #include /* errno program_invocation_name program_invocation_short_name */ 40 | 41 | #if !defined(HAVE_SETPROCTITLE) 42 | #define HAVE_SETPROCTITLE (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__) 43 | #endif 44 | 45 | 46 | #if !HAVE_SETPROCTITLE 47 | #if (defined __linux || defined __APPLE__) 48 | 49 | extern char **environ; 50 | 51 | static struct { 52 | /* original value */ 53 | const char *arg0; 54 | 55 | /* title space available */ 56 | char *base, *end; 57 | 58 | /* pointer to original nul character within base */ 59 | char *nul; 60 | 61 | _Bool reset; 62 | int error; 63 | } SPT; 64 | 65 | 66 | #ifndef SPT_MIN 67 | #define SPT_MIN(a, b) (((a) < (b))? (a) : (b)) 68 | #endif 69 | 70 | static inline size_t spt_min(size_t a, size_t b) { 71 | return SPT_MIN(a, b); 72 | } /* spt_min() */ 73 | 74 | 75 | /* 76 | * For discussion on the portability of the various methods, see 77 | * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html 78 | */ 79 | static int spt_clearenv(void) { 80 | #if __GLIBC__ 81 | clearenv(); 82 | 83 | return 0; 84 | #else 85 | extern char **environ; 86 | static char **tmp; 87 | 88 | if (!(tmp = malloc(sizeof *tmp))) 89 | return errno; 90 | 91 | tmp[0] = NULL; 92 | environ = tmp; 93 | 94 | return 0; 95 | #endif 96 | } /* spt_clearenv() */ 97 | 98 | 99 | static int spt_copyenv(char *oldenv[]) { 100 | extern char **environ; 101 | char *eq; 102 | int i, error; 103 | 104 | if (environ != oldenv) 105 | return 0; 106 | 107 | if ((error = spt_clearenv())) 108 | goto error; 109 | 110 | for (i = 0; oldenv[i]; i++) { 111 | if (!(eq = strchr(oldenv[i], '='))) 112 | continue; 113 | 114 | *eq = '\0'; 115 | error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0; 116 | *eq = '='; 117 | 118 | if (error) 119 | goto error; 120 | } 121 | 122 | return 0; 123 | error: 124 | environ = oldenv; 125 | 126 | return error; 127 | } /* spt_copyenv() */ 128 | 129 | 130 | static int spt_copyargs(int argc, char *argv[]) { 131 | char *tmp; 132 | int i; 133 | 134 | for (i = 1; i < argc || (i >= argc && argv[i]); i++) { 135 | if (!argv[i]) 136 | continue; 137 | 138 | if (!(tmp = strdup(argv[i]))) 139 | return errno; 140 | 141 | argv[i] = tmp; 142 | } 143 | 144 | return 0; 145 | } /* spt_copyargs() */ 146 | 147 | 148 | void spt_init(int argc, char *argv[]) { 149 | char **envp = environ; 150 | char *base, *end, *nul, *tmp; 151 | int i, error; 152 | 153 | if (!(base = argv[0])) 154 | return; 155 | 156 | nul = &base[strlen(base)]; 157 | end = nul + 1; 158 | 159 | for (i = 0; i < argc || (i >= argc && argv[i]); i++) { 160 | if (!argv[i] || argv[i] < end) 161 | continue; 162 | 163 | end = argv[i] + strlen(argv[i]) + 1; 164 | } 165 | 166 | for (i = 0; envp[i]; i++) { 167 | if (envp[i] < end) 168 | continue; 169 | 170 | end = envp[i] + strlen(envp[i]) + 1; 171 | } 172 | 173 | if (!(SPT.arg0 = strdup(argv[0]))) 174 | goto syerr; 175 | 176 | #if __GLIBC__ 177 | if (!(tmp = strdup(program_invocation_name))) 178 | goto syerr; 179 | 180 | program_invocation_name = tmp; 181 | 182 | if (!(tmp = strdup(program_invocation_short_name))) 183 | goto syerr; 184 | 185 | program_invocation_short_name = tmp; 186 | #elif __APPLE__ 187 | if (!(tmp = strdup(getprogname()))) 188 | goto syerr; 189 | 190 | setprogname(tmp); 191 | #endif 192 | 193 | 194 | if ((error = spt_copyenv(envp))) 195 | goto error; 196 | 197 | if ((error = spt_copyargs(argc, argv))) 198 | goto error; 199 | 200 | SPT.nul = nul; 201 | SPT.base = base; 202 | SPT.end = end; 203 | 204 | return; 205 | syerr: 206 | error = errno; 207 | error: 208 | SPT.error = error; 209 | } /* spt_init() */ 210 | 211 | 212 | #ifndef SPT_MAXTITLE 213 | #define SPT_MAXTITLE 255 214 | #endif 215 | 216 | void setproctitle(const char *fmt, ...) { 217 | char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */ 218 | va_list ap; 219 | char *nul; 220 | int len, error; 221 | 222 | if (!SPT.base) 223 | return; 224 | 225 | if (fmt) { 226 | va_start(ap, fmt); 227 | len = vsnprintf(buf, sizeof buf, fmt, ap); 228 | va_end(ap); 229 | } else { 230 | len = snprintf(buf, sizeof buf, "%s", SPT.arg0); 231 | } 232 | 233 | if (len <= 0) 234 | { error = errno; goto error; } 235 | 236 | if (!SPT.reset) { 237 | memset(SPT.base, 0, SPT.end - SPT.base); 238 | SPT.reset = 1; 239 | } else { 240 | memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base)); 241 | } 242 | 243 | len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1); 244 | memcpy(SPT.base, buf, len); 245 | nul = &SPT.base[len]; 246 | 247 | if (nul < SPT.nul) { 248 | *SPT.nul = '.'; 249 | } else if (nul == SPT.nul && &nul[1] < SPT.end) { 250 | *SPT.nul = ' '; 251 | *++nul = '\0'; 252 | } 253 | 254 | return; 255 | error: 256 | SPT.error = error; 257 | } /* setproctitle() */ 258 | 259 | 260 | #endif /* __linux || __APPLE__ */ 261 | #endif /* !HAVE_SETPROCTITLE */ -------------------------------------------------------------------------------- /util/string.cc: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tinyco { 9 | namespace string { 10 | 11 | template 12 | void Split(const std::string &s, char delim, Out result) { 13 | std::stringstream ss; 14 | ss.str(s); 15 | std::string item; 16 | while (std::getline(ss, item, delim)) { 17 | *(result++) = item; 18 | } 19 | } 20 | 21 | std::vector Split(const std::string &s, char delim) { 22 | std::vector elems; 23 | Split(s, delim, std::back_inserter(elems)); 24 | if (elems.empty()) elems.push_back(s); 25 | return elems; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /util/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_UTILS_H_ 2 | #define STRING_UTILS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace tinyco { 11 | namespace string { 12 | 13 | std::vector Split(const std::string &s, char delim); 14 | 15 | // trim from start (in place) 16 | inline void ltrim(std::string &s) { 17 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), 18 | [](int ch) { return !std::isspace(ch); })); 19 | } 20 | 21 | // trim from end (in place) 22 | inline void rtrim(std::string &s) { 23 | s.erase(std::find_if(s.rbegin(), s.rend(), 24 | [](int ch) { return !std::isspace(ch); }).base(), 25 | s.end()); 26 | } 27 | 28 | // trim from both ends (in place) 29 | inline void trim(std::string &s) { 30 | ltrim(s); 31 | rtrim(s); 32 | } 33 | 34 | // trim from start (copying) 35 | inline std::string ltrim_copy(std::string s) { 36 | ltrim(s); 37 | return s; 38 | } 39 | 40 | // trim from end (copying) 41 | inline std::string rtrim_copy(std::string s) { 42 | rtrim(s); 43 | return s; 44 | } 45 | 46 | // trim from both ends (copying) 47 | inline std::string trim_copy(std::string s) { 48 | trim(s); 49 | return s; 50 | } 51 | } 52 | } 53 | #endif 54 | -------------------------------------------------------------------------------- /util/time.h: -------------------------------------------------------------------------------- 1 | #ifndef TINYCO_TIME_H_ 2 | #define TINYCO_TIME_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace tinyco { 8 | namespace time { 9 | 10 | inline uint64_t mstime() { 11 | struct timeval tv; 12 | 13 | gettimeofday(&tv, NULL); 14 | 15 | return (unsigned long long)(tv.tv_sec) * 1000 + 16 | (unsigned long long)(tv.tv_usec) / 1000; 17 | } 18 | } 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /work.h: -------------------------------------------------------------------------------- 1 | #ifndef WORK_H_ 2 | #define WORK_H_ 3 | 4 | namespace tinyco { 5 | // wrapper for business 6 | // user can inherit from it and implement Run function 7 | class Work { 8 | private: 9 | /* data */ 10 | public: 11 | Work() {} 12 | 13 | virtual ~Work() {} 14 | 15 | virtual int Run() = 0; 16 | }; 17 | } 18 | 19 | #endif 20 | --------------------------------------------------------------------------------