Browse Source

optimize code from @mtdxc

ithewei 3 years ago
parent
commit
1f0e6fb250

+ 26 - 25
README-CN.md

@@ -19,6 +19,32 @@
 
 `libhv`是一个类似于`libevent、libev、libuv`的跨平台网络库,提供了更易用的接口和更丰富的协议。
 
+## 📚 中文资料
+
+- **libhv QQ群**: `739352073`,欢迎加群交流
+- **libhv 源码剖析**: <https://hewei.blog.csdn.net/article/details/123295998>
+- **libhv 接口手册**: <https://hewei.blog.csdn.net/article/details/103976875>
+- **libhv 教程目录**: <https://hewei.blog.csdn.net/article/details/113733758>
+- [libhv教程01--介绍与体验](https://hewei.blog.csdn.net/article/details/113702536)
+- [libhv教程02--编译与安装](https://hewei.blog.csdn.net/article/details/113704737)
+- [libhv教程03--链库与使用](https://hewei.blog.csdn.net/article/details/113706378)
+- [libhv教程04--编写一个完整的命令行程序](https://hewei.blog.csdn.net/article/details/113719503)
+- [libhv教程05--事件循环以及定时器的简单使用](https://hewei.blog.csdn.net/article/details/113724474)
+- [libhv教程06--创建一个简单的TCP服务端](https://hewei.blog.csdn.net/article/details/113737580)
+- [libhv教程07--创建一个简单的TCP客户端](https://hewei.blog.csdn.net/article/details/113738900)
+- [libhv教程08--创建一个简单的UDP服务端](https://hewei.blog.csdn.net/article/details/113871498)
+- [libhv教程09--创建一个简单的UDP客户端](https://hewei.blog.csdn.net/article/details/113871724)
+- [libhv教程10--创建一个简单的HTTP服务端](https://hewei.blog.csdn.net/article/details/113982999)
+- [libhv教程11--创建一个简单的HTTP客户端](https://hewei.blog.csdn.net/article/details/113984302)
+- [libhv教程12--创建一个简单的WebSocket服务端](https://hewei.blog.csdn.net/article/details/113985321)
+- [libhv教程13--创建一个简单的WebSocket客户端](https://hewei.blog.csdn.net/article/details/113985895)
+- [libhv教程14--200行实现一个纯C版jsonrpc框架](https://hewei.blog.csdn.net/article/details/119920540)
+- [libhv教程15--200行实现一个C++版protorpc框架](https://hewei.blog.csdn.net/article/details/119966701)
+- [libhv教程16--多线程/多进程服务端编程](https://hewei.blog.csdn.net/article/details/120366024)
+- [libhv教程17--Qt中使用libhv](https://hewei.blog.csdn.net/article/details/120699890)
+- [libhv教程18--动手写一个tinyhttpd](https://hewei.blog.csdn.net/article/details/121706604)
+- [libhv教程19--MQTT的实现与使用](https://hewei.blog.csdn.net/article/details/122753665)
+
 ## ✨ 特征
 
 - 跨平台(Linux, Windows, MacOS, Solaris, Android, iOS)
@@ -473,31 +499,6 @@ ab -c 100 -n 100000 http://127.0.0.1:8080/
 
 以上测试结果可以在 [Github Actions](https://github.com/ithewei/libhv/actions/workflows/benchmark.yml) 中查看。
 
-## 📚 中文资料
-
-- **libhv QQ群**: `739352073`,欢迎加群交流
-- **libhv 源码剖析**: <https://hewei.blog.csdn.net/article/details/123295998>
-- **libhv 教程目录**: <https://hewei.blog.csdn.net/article/details/113733758>
-- [libhv教程01--介绍与体验](https://hewei.blog.csdn.net/article/details/113702536)
-- [libhv教程02--编译与安装](https://hewei.blog.csdn.net/article/details/113704737)
-- [libhv教程03--链库与使用](https://hewei.blog.csdn.net/article/details/113706378)
-- [libhv教程04--编写一个完整的命令行程序](https://hewei.blog.csdn.net/article/details/113719503)
-- [libhv教程05--事件循环以及定时器的简单使用](https://hewei.blog.csdn.net/article/details/113724474)
-- [libhv教程06--创建一个简单的TCP服务端](https://hewei.blog.csdn.net/article/details/113737580)
-- [libhv教程07--创建一个简单的TCP客户端](https://hewei.blog.csdn.net/article/details/113738900)
-- [libhv教程08--创建一个简单的UDP服务端](https://hewei.blog.csdn.net/article/details/113871498)
-- [libhv教程09--创建一个简单的UDP客户端](https://hewei.blog.csdn.net/article/details/113871724)
-- [libhv教程10--创建一个简单的HTTP服务端](https://hewei.blog.csdn.net/article/details/113982999)
-- [libhv教程11--创建一个简单的HTTP客户端](https://hewei.blog.csdn.net/article/details/113984302)
-- [libhv教程12--创建一个简单的WebSocket服务端](https://hewei.blog.csdn.net/article/details/113985321)
-- [libhv教程13--创建一个简单的WebSocket客户端](https://hewei.blog.csdn.net/article/details/113985895)
-- [libhv教程14--200行实现一个纯C版jsonrpc框架](https://hewei.blog.csdn.net/article/details/119920540)
-- [libhv教程15--200行实现一个C++版protorpc框架](https://hewei.blog.csdn.net/article/details/119966701)
-- [libhv教程16--多线程/多进程服务端编程](https://hewei.blog.csdn.net/article/details/120366024)
-- [libhv教程17--Qt中使用libhv](https://hewei.blog.csdn.net/article/details/120699890)
-- [libhv教程18--动手写一个tinyhttpd](https://hewei.blog.csdn.net/article/details/121706604)
-- [libhv教程19--MQTT的实现与使用](https://hewei.blog.csdn.net/article/details/122753665)
-
 ## 💎 用户案例
 
 如果您在使用`libhv`,欢迎通过PR将信息提交至此列表,让更多的用户了解`libhv`的实际使用场景,以建立更好的网络生态。

+ 2 - 4
base/hbuf.h

@@ -128,8 +128,7 @@ public:
     void push_front(void* ptr, size_t len) {
         if (len > this->len - _size) {
             size_t newsize = MAX(this->len, len)*2;
-            base = (char*)hv_realloc(base, newsize, this->len);
-            this->len = newsize;
+            resize(newsize);
         }
 
         if (_offset < len) {
@@ -146,8 +145,7 @@ public:
     void push_back(void* ptr, size_t len) {
         if (len > this->len - _size) {
             size_t newsize = MAX(this->len, len)*2;
-            base = (char*)hv_realloc(base, newsize, this->len);
-            this->len = newsize;
+            resize(newsize);
         }
         else if (len > this->len - _offset - _size) {
             // move => start

+ 1 - 4
base/hendian.h

@@ -113,10 +113,7 @@ static inline int detect_endian() {
         short s;
     } u;
     u.s = 0x1122;
-    if (u.c == 0x11) {
-        return BIG_ENDIAN;
-    }
-    return LITTLE_ENDIAN;
+    return u.c ==0x11 ? BIG_ENDIAN : LITTLE_ENDIAN;
 }
 
 #ifdef __cplusplus

+ 2 - 1
cpputil/ThreadLocalStorage.cpp

@@ -19,11 +19,12 @@ void ThreadLocalStorage::setThreadName(const char* name) {
 }
 
 const char* ThreadLocalStorage::threadName() {
-    static char unnamed[32] = {0};
     void* value = get(THREAD_NAME);
     if (value) {
         return (char*)value;
     }
+
+    static char unnamed[32] = {0};
     snprintf(unnamed, sizeof(unnamed)-1, "thread-%ld", hv_gettid());
     return unnamed;
 }

+ 4 - 26
evpp/TcpClient.h

@@ -10,15 +10,12 @@
 
 namespace hv {
 
-typedef struct reconn_setting_s ReconnectInfo; // Deprecated
-
 template<class TSocketChannel = SocketChannel>
 class TcpClientTmpl {
 public:
     typedef std::shared_ptr<TSocketChannel> TSocketChannelPtr;
 
     TcpClientTmpl() {
-        tls = false;
         connect_timeout = 5000;
         reconn_setting = NULL;
         unpack_setting = NULL;
@@ -33,10 +30,11 @@ public:
         return loop_thread.loop();
     }
 
+    //NOTE: By default, not bind local port. If necessary, you can call system api bind() after createsocket().
     //@retval >=0 connfd, <0 error
-    int createsocket(int port, const char* host = "127.0.0.1") {
+    int createsocket(int remote_port, const char* remote_host = "127.0.0.1") {
         memset(&peeraddr, 0, sizeof(peeraddr));
-        int ret = sockaddr_set_ipport(&peeraddr, host, port);
+        int ret = sockaddr_set_ipport(&peeraddr, remote_host, remote_port);
         if (ret != 0) {
             return -1;
         }
@@ -66,9 +64,6 @@ public:
 
     int startConnect() {
         assert(channel != NULL);
-        if (tls) {
-            channel->enableSSL();
-        }
         if (connect_timeout) {
             channel->setConnectTimeout(connect_timeout);
         }
@@ -148,25 +143,9 @@ public:
         return send(str.data(), str.size());
     }
 
-    // deprecated: use withTLS(opt) after createsocket
-    int withTLS(const char* cert_file = NULL, const char* key_file = NULL, bool verify_peer = false) {
-        if (cert_file) {
-            hssl_ctx_init_param_t param;
-            memset(&param, 0, sizeof(param));
-            param.crt_file = cert_file;
-            param.key_file = key_file;
-            param.verify_peer = verify_peer ? 1 : 0;
-            param.endpoint = HSSL_CLIENT;
-            if (hssl_ctx_init(&param) == NULL) {
-                fprintf(stderr, "hssl_ctx_init failed!\n");
-                return -1;
-            }
-        }
-        tls = true;
-        return 0;
-    }
     int withTLS(hssl_ctx_opt_t* opt) {
         if (!channel) return -1;
+        opt->endpoint = HSSL_CLIENT;
         return channel->newSslCtx(opt);
     }
 
@@ -203,7 +182,6 @@ public:
     TSocketChannelPtr       channel;
 
     sockaddr_u              peeraddr;
-    bool                    tls;
     int                     connect_timeout;
     reconn_setting_t*       reconn_setting;
     unpack_setting_t*       unpack_setting;

+ 3 - 2
evpp/UdpClient.h

@@ -26,9 +26,10 @@ public:
         return loop_thread.loop();
     }
 
+    //NOTE: By default, not bind local port. If necessary, you can call system api bind() after createsocket().
     //@retval >=0 sockfd, <0 error
-    int createsocket(int port, const char* host = "127.0.0.1") {
-        hio_t* io = hloop_create_udp_client(loop_thread.hloop(), host, port);
+    int createsocket(int remote_port, const char* remote_host = "127.0.0.1") {
+        hio_t* io = hloop_create_udp_client(loop_thread.hloop(), remote_host, remote_port);
         if (io == NULL) return -1;
         channel.reset(new TSocketChannel(io));
         return channel->fd();

+ 3 - 2
examples/multi-thread/multi-acceptor-processes.c

@@ -15,7 +15,6 @@
 static const char* host = "0.0.0.0";
 static int port = 1234;
 static int process_num = 4;
-static int listenfd = INVALID_SOCKET;
 
 static void on_close(hio_t* io) {
     printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
@@ -41,6 +40,7 @@ static void on_accept(hio_t* io) {
 }
 
 static void loop_proc(void* userdata) {
+    int listenfd = (int)(intptr_t)(userdata);
     hloop_t* loop = hloop_new(HLOOP_FLAG_AUTO_FREE);
     haccept(loop, listenfd, on_accept);
     hloop_run(loop);
@@ -53,7 +53,7 @@ int main(int argc, char** argv) {
     }
     port = atoi(argv[1]);
 
-    listenfd = Listen(port, host);
+    int listenfd = Listen(port, host);
     if (listenfd < 0) {
         exit(1);
     }
@@ -61,6 +61,7 @@ int main(int argc, char** argv) {
     proc_ctx_t ctx;
     memset(&ctx, 0, sizeof(ctx));
     ctx.proc = loop_proc;
+    ctx.proc_userdata = (void*)(intptr_t)listenfd;
     for (int i = 0; i < process_num; ++i) {
         hproc_spawn(&ctx);
     }

+ 3 - 3
examples/multi-thread/multi-acceptor-threads.c

@@ -14,7 +14,6 @@
 static const char* host = "0.0.0.0";
 static int port = 1234;
 static int thread_num = 4;
-static int listenfd = INVALID_SOCKET;
 
 static void on_close(hio_t* io) {
     printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
@@ -40,6 +39,7 @@ static void on_accept(hio_t* io) {
 }
 
 static HTHREAD_ROUTINE(loop_thread) {
+    int listenfd = (int)(intptr_t)(userdata);
     hloop_t* loop = hloop_new(HLOOP_FLAG_AUTO_FREE);
     haccept(loop, listenfd, on_accept);
     hloop_run(loop);
@@ -53,13 +53,13 @@ int main(int argc, char** argv) {
     }
     port = atoi(argv[1]);
 
-    listenfd = Listen(port, host);
+    int listenfd = Listen(port, host);
     if (listenfd < 0) {
         exit(1);
     }
 
     for (int i = 0; i < thread_num; ++i) {
-        hthread_create(loop_thread, NULL);
+        hthread_create(loop_thread, (void*)(intptr_t)listenfd);
     }
 
     while(1) hv_sleep(1);

+ 1 - 1
http/HttpMessage.cpp

@@ -331,7 +331,7 @@ void HttpMessage::FillContentLength() {
 
 bool HttpMessage::IsChunked() {
     auto iter = headers.find("Transfer-Encoding");
-    return iter == headers.end() ? false : stricmp(iter->second.c_str(), "chunked") == 0;
+    return iter != headers.end() && stricmp(iter->second.c_str(), "chunked") == 0;
 }
 
 bool HttpMessage::IsKeepAlive() {

+ 1 - 1
http/HttpMessage.h

@@ -399,7 +399,7 @@ public:
     // client_addr
     hv::NetAddr         client_addr; // for http server save client addr of request
     // for HttpClient
-    int                 timeout;
+    int                 timeout;     // unit: s
     int                 retry_count; // just for AsyncHttpClient fail retry
     int                 retry_delay; // just for AsyncHttpClient fail retry
     unsigned            redirect: 1;

+ 1 - 1
http/WebSocketParser.cpp

@@ -49,7 +49,7 @@ static int on_frame_end(websocket_parser* parser) {
     return 0;
 }
 
-websocket_parser_settings WebSocketParser::cbs = {
+static websocket_parser_settings cbs = {
     on_frame_header,
     on_frame_body,
     on_frame_end

+ 0 - 2
http/WebSocketParser.h

@@ -15,11 +15,9 @@ enum websocket_parser_state {
     WS_FRAME_FIN,
 };
 
-struct websocket_parser_settings;
 struct websocket_parser;
 class HV_EXPORT WebSocketParser {
 public:
-    static websocket_parser_settings    cbs;
     websocket_parser*                   parser;
     websocket_parser_state              state;
     int                                 opcode;

+ 1 - 1
http/client/WebSocketClient.cpp

@@ -51,7 +51,7 @@ int WebSocketClient::open(const char* _url, const http_headers& headers) {
     // wss
     bool wss = strncmp(url.c_str(), "wss", 3) == 0;
     if (wss) {
-        withTLS();
+        channel->enableSSL();
     }
 
     for (auto& header : headers) {

+ 1 - 4
http/client/http_client.cpp

@@ -508,7 +508,7 @@ send:
     size_t len  = 0;
     while (cli->parser->GetSendData(&data, &len)) {
         total_nsend = 0;
-        while (1) {
+        while (total_nsend < len) {
             if (timeout > 0) {
                 cur_time = time(NULL);
                 if (cur_time - start_time >= timeout) {
@@ -534,9 +534,6 @@ send:
                 }
             }
             total_nsend += nsend;
-            if (total_nsend == len) {
-                break;
-            }
         }
     }
     cli->parser->InitResponse(resp);

+ 8 - 0
http/client/http_client.h

@@ -89,6 +89,14 @@ public:
         return http_client_set_timeout(_client, timeout);
     }
 
+    // SSL/TLS
+    int setSslCtx(hssl_ctx_t ssl_ctx) {
+        return http_client_set_ssl_ctx(_client, ssl_ctx);
+    }
+    int newSslCtx(hssl_ctx_opt_t* opt) {
+        return http_client_new_ssl_ctx(_client, opt);
+    }
+
     // headers
     int clearHeaders() {
         return http_client_clear_headers(_client);

+ 1 - 4
http/server/FileCache.h

@@ -32,10 +32,7 @@ typedef struct file_cache_s {
     bool is_modified() {
         time_t mtime = st.st_mtime;
         stat(filepath.c_str(), &st);
-        if (mtime == st.st_mtime) {
-            return false;
-        }
-        return true;
+        return mtime != st.st_mtime;
     }
 
     bool is_complete() {

+ 7 - 4
http/server/HttpHandler.cpp

@@ -13,8 +13,10 @@ int HttpHandler::customHttpHandler(const http_handler& handler) {
 int HttpHandler::invokeHttpHandler(const http_handler* handler) {
     int status_code = HTTP_STATUS_NOT_IMPLEMENTED;
     if (handler->sync_handler) {
+        // NOTE: sync_handler run on IO thread
         status_code = handler->sync_handler(req.get(), resp.get());
     } else if (handler->async_handler) {
+        // NOTE: async_handler run on hv::async threadpool
         hv::async(std::bind(handler->async_handler, req, writer));
         status_code = HTTP_STATUS_UNFINISHED;
     } else if (handler->ctx_handler) {
@@ -23,6 +25,7 @@ int HttpHandler::invokeHttpHandler(const http_handler* handler) {
         ctx->request = req;
         ctx->response = resp;
         ctx->writer = writer;
+        // NOTE: ctx_handler run on IO thread, you can easily post HttpContextPtr to your consumer thread for processing.
         status_code = handler->ctx_handler(ctx);
         if (writer && writer->state != hv::HttpResponseWriter::SEND_BEGIN) {
             status_code = HTTP_STATUS_UNFINISHED;
@@ -42,6 +45,7 @@ int HttpHandler::HandleHttpRequest() {
     pReq->client_addr.port = port;
     pReq->Host();
     pReq->ParseUrl();
+    // NOTE: Not all users want to parse body, we comment it out.
     // pReq->ParseBody();
 
 preprocessor:
@@ -143,7 +147,7 @@ int HttpHandler::defaultStaticHandler() {
     if (!is_dir || is_index_of) {
         FileCache::OpenParam param;
         bool has_range = req->headers.find("Range") != req->headers.end();
-        param.need_read = req->method == HTTP_HEAD || has_range ? false : true;
+        param.need_read = !(req->method == HTTP_HEAD || has_range);
         param.path = req_path;
         fc = files->Open(filepath.c_str(), &param);
         if (fc == NULL) {
@@ -181,9 +185,7 @@ int HttpHandler::defaultStaticHandler() {
 int HttpHandler::defaultErrorHandler() {
     // error page
     if (service->error_page.size() != 0) {
-        std::string filepath = service->document_root;
-        filepath += '/';
-        filepath += service->error_page;
+        std::string filepath = service->document_root + '/' + service->error_page;
         FileCache::OpenParam param;
         fc = files->Open(filepath.c_str(), &param);
     }
@@ -268,6 +270,7 @@ int HttpHandler::GetSendData(char** data, size_t* len) {
                         state = SEND_DONE;
                         goto return_nobody;
                     }
+                    pResp->status_code = HTTP_STATUS_PARTIAL_CONTENT;
                     pResp->SetRange(from, to, total);
                     state = SEND_BODY;
                     goto return_header;

+ 4 - 8
http/server/HttpHandler.h

@@ -103,10 +103,8 @@ public:
         req.reset(new HttpRequest);
         resp.reset(new HttpResponse);
         if (http_version == 2) {
-            req->http_major = 2;
-            req->http_minor = 0;
-            resp->http_major = 2;
-            resp->http_minor = 0;
+            resp->http_major = req->http_major = 2;
+            resp->http_minor = req->http_minor = 0;
         }
         parser->InitRequest(req.get());
         if (io) {
@@ -122,10 +120,8 @@ public:
             return false;
         }
         protocol = HTTP_V2;
-        req->http_major = 2;
-        req->http_minor = 0;
-        resp->http_major = 2;
-        resp->http_minor = 0;
+        resp->http_major = req->http_major = 2;
+        resp->http_minor = req->http_minor = 0;
         parser->InitRequest(req.get());
         return true;
     }

+ 6 - 6
http/server/HttpServer.cpp

@@ -268,13 +268,13 @@ static void on_accept(hio_t* io) {
     HttpHandler* handler = new HttpHandler;
     // ssl
     handler->ssl = hio_is_ssl(io);
-    // ip
-    sockaddr_ip((sockaddr_u*)hio_peeraddr(io), handler->ip, sizeof(handler->ip));
-    // port
-    handler->port = sockaddr_port((sockaddr_u*)hio_peeraddr(io));
-    // service
+    // ip:port
+    sockaddr_u* peeraddr = (sockaddr_u*)hio_peeraddr(io);
+    sockaddr_ip(peeraddr, handler->ip, sizeof(handler->ip));
+    handler->port = sockaddr_port(peeraddr);
+    // http service
     handler->service = service;
-    // ws
+    // websocket service
     handler->ws_service = server->ws;
     // FileCache
     handler->files = default_filecache();

+ 2 - 2
http/server/HttpServer.h

@@ -17,8 +17,8 @@ typedef struct http_server_s {
     int http_version;
     int worker_processes;
     int worker_threads;
-    HttpService* service;
-    WebSocketService* ws;
+    HttpService* service; // http service
+    WebSocketService* ws; // websocket service
     void* userdata;
 //private:
     int listenfd[2]; // 0: http, 1: https

+ 4 - 1
http/server/HttpService.h

@@ -27,8 +27,11 @@
  *          http_status_code:   handle done
  */
 #define HTTP_STATUS_UNFINISHED  0
+// NOTE: http_sync_handler run on IO thread
 typedef std::function<int(HttpRequest* req, HttpResponse* resp)>                            http_sync_handler;
+// NOTE: http_async_handler run on hv::async threadpool
 typedef std::function<void(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer)> http_async_handler;
+// NOTE: http_ctx_handler run on IO thread, you can easily post HttpContextPtr to your consumer thread for processing.
 typedef std::function<int(const HttpContextPtr& ctx)>                                       http_ctx_handler;
 
 struct http_handler {
@@ -121,8 +124,8 @@ struct HV_EXPORT HttpService {
         keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
     }
 
-    // @retval 0 OK, else HTTP_STATUS_NOT_FOUND, HTTP_STATUS_METHOD_NOT_ALLOWED
     void AddApi(const char* path, http_method method, const http_handler& handler);
+    // @retval 0 OK, else HTTP_STATUS_NOT_FOUND, HTTP_STATUS_METHOD_NOT_ALLOWED
     int  GetApi(const char* url,  http_method method, http_handler** handler);
     // RESTful API /:field/ => req->query_params["field"]
     int  GetApi(HttpRequest* req, http_handler** handler);