Quellcode durchsuchen

add comments for httpd

hewei.it vor 4 Jahren
Ursprung
Commit
4524dbd77b
3 geänderte Dateien mit 76 neuen und 0 gelöschten Zeilen
  1. 21 0
      examples/httpd/handler.h
  2. 46 0
      examples/httpd/httpd.cpp
  3. 9 0
      examples/httpd/router.h

+ 21 - 0
examples/httpd/handler.h

@@ -15,14 +15,20 @@ public:
         // if (req->content_type != APPLICATION_JSON) {
         //     return response_status(resp, HTTP_STATUS_BAD_REQUEST);
         // }
+
+        // 解析body字符串到对应结构体(json、form、kv)
         req->ParseBody();
+        // 响应格式默认为application/json
         resp->content_type = APPLICATION_JSON;
 #if 0
+        // 前处理中我们可以做一些公共逻辑,如请求统计、请求拦截、API鉴权等,
+        // 下面是一段简单的Token头校验代码
         // authentication sample code
         if (strcmp(req->path.c_str(), "/login") != 0) {
             string token = req->GetHeader("token");
             if (token.empty()) {
                 response_status(resp, 10011, "Miss token");
+                // 返回错误码将直接发送响应,不再走后续的处理
                 return HTTP_STATUS_UNAUTHORIZED;
             }
             else if (strcmp(token.c_str(), "abcdefg") != 0) {
@@ -32,10 +38,12 @@ public:
             return 0;
         }
 #endif
+        // 返回0表示继续走后面的处理流程
         return 0;
     }
 
     static int postprocessor(HttpRequest* req, HttpResponse* resp) {
+        // 后处理中我们可以做一些后处理公共逻辑,如响应状态码统计、响应数据加密等
         // printf("%s\n", resp->Dump(true, true).c_str());
         return 0;
     }
@@ -46,6 +54,7 @@ public:
         if (!strTime.empty()) {
             int ms = atoi(strTime.c_str());
             if (ms > 0) {
+                // 该操作会阻塞当前IO线程,仅用做测试接口,实际应用中请勿在回调中做耗时操作
                 hv_delay(ms);
             }
         }
@@ -60,6 +69,7 @@ public:
         if (!strTime.empty()) {
             int ms = atoi(strTime.c_str());
             if (ms > 0) {
+                // 在回调线程中可直接使用setTimeout/setInterval定时器接口
                 hv::setTimeout(ms, [writer](hv::TimerID timerID){
                     writer->Begin();
                     HttpResponse* resp = writer->response.get();
@@ -74,6 +84,7 @@ public:
     static int query(HttpRequest* req, HttpResponse* resp) {
         // scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
         // ?query => HttpRequest::query_params
+        // URL里携带的请求参数已被解析到query_params数据结构里,可通过GetParam("key")获取
         for (auto& param : req->query_params) {
             resp->Set(param.first.c_str(), param.second);
         }
@@ -85,6 +96,7 @@ public:
         if (req->content_type != APPLICATION_URLENCODED) {
             return response_status(resp, HTTP_STATUS_BAD_REQUEST);
         }
+        // 设置响应格式为application/x-www-form-urlencoded
         resp->content_type = APPLICATION_URLENCODED;
         resp->kv = req->kv;
         resp->kv["int"] = hv::to_string(123);
@@ -97,6 +109,7 @@ public:
         if (req->content_type != APPLICATION_JSON) {
             return response_status(resp, HTTP_STATUS_BAD_REQUEST);
         }
+        // 设置响应格式为application/json
         resp->content_type = APPLICATION_JSON;
         resp->json = req->json;
         resp->json["int"] = 123;
@@ -109,16 +122,19 @@ public:
         if (req->content_type != MULTIPART_FORM_DATA) {
             return response_status(resp, HTTP_STATUS_BAD_REQUEST);
         }
+        // 设置响应格式为multipart/form-data
         resp->content_type = MULTIPART_FORM_DATA;
         resp->form = req->form;
         resp->form["int"] = 123;
         resp->form["float"] = 3.14;
         resp->form["string"] = "hello";
+        // 使用formdata格式传输文件
         // resp->form["file"] = FormData(NULL, "test.jpg");
         // resp->UploadFormFile("file", "test.jpg");
         return 200;
     }
 
+    // 通过 Get/Set 接口可以 获取/设置 key:value 到对应数据结构体 json/form/kv
     static int test(HttpRequest* req, HttpResponse* resp) {
         // bool b = req->Get<bool>("bool");
         // int64_t n = req->Get<int64_t>("int");
@@ -141,6 +157,7 @@ public:
         if (req->content_type != APPLICATION_GRPC) {
             return response_status(resp, HTTP_STATUS_BAD_REQUEST);
         }
+        // 需引入protobuf库序列化/反序列化body
         // parse protobuf
         // ParseFromString(req->body);
         // resp->content_type = APPLICATION_GRPC;
@@ -153,6 +170,7 @@ public:
     static int restful(HttpRequest* req, HttpResponse* resp) {
         // RESTful /:field/ => HttpRequest::query_params
         // path=/group/:group_name/user/:user_id
+        // restful风格URL里的参数已被解析到query_params数据结构里,可通过GetParam("key")获取
         // string group_name = req->GetParam("group_name");
         // string user_id = req->GetParam("user_id");
         for (auto& param : req->query_params) {
@@ -162,6 +180,7 @@ public:
         return 200;
     }
 
+    // 登录示例:校验用户名,密码,返回一个token
     static int login(HttpRequest* req, HttpResponse* resp) {
         string username = req->GetString("username");
         string password = req->GetString("password");
@@ -184,6 +203,7 @@ public:
         }
     }
 
+    // 上传文件示例
     static int upload(HttpRequest* req, HttpResponse* resp) {
         // return resp->SaveFormFile("file", "html/uploads/test.jpg");
         if (req->content_type != MULTIPART_FORM_DATA) {
@@ -205,6 +225,7 @@ public:
     }
 
 private:
+    // 统一的响应格式
     static int response_status(HttpResponse* resp, int code = 200, const char* message = NULL) {
         resp->Set("code", code);
         if (message == NULL) message = http_status_str((enum http_status)code);

+ 46 - 0
examples/httpd/httpd.cpp

@@ -1,3 +1,9 @@
+/*
+ * @介绍:httpd即HTTP服务端后台程序,该示例程序展示了如何使用libhv构建一个功能完备的HTTP服务端。
+ *        打算使用libhv做HTTP服务端的同学建议通读此程序,你将受益匪浅。
+ *
+ */
+
 #include "hv.h"
 #include "hmain.h"
 #include "iniparser.h"
@@ -46,7 +52,9 @@ void print_help() {
     printf("Options:\n%s\n", detail_options);
 }
 
+// 解析配置文件
 int parse_confile(const char* confile) {
+    // 加载配置文件
     IniParser ini;
     int ret = ini.LoadFromFile(confile);
     if (ret != 0) {
@@ -54,27 +62,36 @@ int parse_confile(const char* confile) {
         exit(-40);
     }
 
+    // 设置日志文件
     // logfile
     string str = ini.GetValue("logfile");
     if (!str.empty()) {
         strncpy(g_main_ctx.logfile, str.c_str(), sizeof(g_main_ctx.logfile));
     }
     hlog_set_file(g_main_ctx.logfile);
+
+    // 设置日志等级
     // loglevel
     str = ini.GetValue("loglevel");
     if (!str.empty()) {
         hlog_set_level_by_str(str.c_str());
     }
+
+    // 设置日志文件大小
     // log_filesize
     str = ini.GetValue("log_filesize");
     if (!str.empty()) {
         hlog_set_max_filesize_by_str(str.c_str());
     }
+
+    // 设置日志保留天数
     // log_remain_days
     str = ini.GetValue("log_remain_days");
     if (!str.empty()) {
         hlog_set_remain_days(atoi(str.c_str()));
     }
+
+    // 是否启用fsync强制刷新日志缓存到磁盘
     // log_fsync
     str = ini.GetValue("log_fsync");
     if (!str.empty()) {
@@ -83,6 +100,7 @@ int parse_confile(const char* confile) {
     hlogi("%s version: %s", g_main_ctx.program_name, hv_compile_version());
     hlog_fsync();
 
+    // 设置工作进程数
     // worker_processes
     int worker_processes = 0;
     str = ini.GetValue("worker_processes");
@@ -96,10 +114,13 @@ int parse_confile(const char* confile) {
         }
     }
     g_http_server.worker_processes = LIMIT(0, worker_processes, MAXNUM_WORKER_PROCESSES);
+
+    // 设置工作进程数
     // worker_threads
     int worker_threads = ini.Get<int>("worker_threads");
     g_http_server.worker_threads = LIMIT(0, worker_threads, 16);
 
+    // 设置http监听端口
     // http_port
     int port = 0;
     const char* szPort = get_arg("p");
@@ -113,6 +134,8 @@ int parse_confile(const char* confile) {
         port = ini.Get<int>("http_port");
     }
     g_http_server.port = port;
+
+    // 设置https监听端口
     // https_port
     if (HV_WITH_SSL) {
         g_http_server.https_port = ini.Get<int>("https_port");
@@ -122,31 +145,42 @@ int parse_confile(const char* confile) {
         exit(-10);
     }
 
+    // 设置url前缀
     // base_url
     str = ini.GetValue("base_url");
     if (str.size() != 0) {
         g_http_service.base_url = str;
     }
+
+    // 设置html文档根目录
     // document_root
     str = ini.GetValue("document_root");
     if (str.size() != 0) {
         g_http_service.document_root = str;
     }
+
+    // 设置首页
     // home_page
     str = ini.GetValue("home_page");
     if (str.size() != 0) {
         g_http_service.home_page = str;
     }
+
+    // 设置错误页面
     // error_page
     str = ini.GetValue("error_page");
     if (str.size() != 0) {
         g_http_service.error_page = str;
     }
+
+    // 设置indexof目录
     // index_of
     str = ini.GetValue("index_of");
     if (str.size() != 0) {
         g_http_service.index_of = str;
     }
+
+    // 设置SSL证书
     // ssl
     if (g_http_server.https_port > 0) {
         std::string crt_file = ini.GetValue("ssl_certificate");
@@ -170,6 +204,7 @@ int parse_confile(const char* confile) {
     return 0;
 }
 
+// reload信号处理: 重新加载配置文件
 static void on_reload(void* userdata) {
     hlogi("reload confile [%s]", g_main_ctx.confile);
     parse_confile(g_main_ctx.confile);
@@ -178,6 +213,7 @@ static void on_reload(void* userdata) {
 int main(int argc, char** argv) {
     // g_main_ctx
     main_ctx_init(argc, argv);
+    // 解析命令行
     //int ret = parse_opt(argc, argv, options);
     int ret = parse_opt_long(argc, argv, long_options, ARRAY_SIZE(long_options));
     if (ret != 0) {
@@ -205,31 +241,37 @@ int main(int argc, char** argv) {
     printf("================================================\n");
     */
 
+    // -h 打印帮助信息
     // help
     if (get_arg("h")) {
         print_help();
         exit(0);
     }
 
+    // -v 打印版本信息
     // version
     if (get_arg("v")) {
         print_version();
         exit(0);
     }
 
+    // -c 设置配置文件
     // parse_confile
     const char* confile = get_arg("c");
     if (confile) {
         strncpy(g_main_ctx.confile, confile, sizeof(g_main_ctx.confile));
     }
+    // 解析配置文件
     parse_confile(g_main_ctx.confile);
 
+    // -t 测试配置文件
     // test
     if (get_arg("t")) {
         printf("Test confile [%s] OK!\n", g_main_ctx.confile);
         exit(0);
     }
 
+    // -s 信号处理
     // signal
     signal_init(on_reload);
     const char* signal = get_arg("s");
@@ -238,6 +280,7 @@ int main(int argc, char** argv) {
     }
 
 #ifdef OS_UNIX
+    // -d 后台运行
     // daemon
     if (get_arg("d")) {
         // nochdir, noclose
@@ -249,12 +292,15 @@ int main(int argc, char** argv) {
     }
 #endif
 
+    // 创建pid文件
     // pidfile
     create_pidfile();
 
     // http_server
+    // 注册路由
     Router::Register(g_http_service);
     g_http_server.service = &g_http_service;
+    // 运行http服务
     ret = http_server_run(&g_http_server);
     return ret;
 }

+ 9 - 0
examples/httpd/router.h

@@ -11,38 +11,45 @@
 class Router {
 public:
     static void Register(HttpService& router) {
+        // 前处理 => 处理 => 后处理
         // preprocessor => Handler => postprocessor
         router.preprocessor = Handler::preprocessor;
         router.postprocessor = Handler::postprocessor;
 
         // curl -v http://ip:port/ping
         router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
+            // 发送字符串
             return resp->String("pong");
         });
 
         // curl -v http://ip:port/data
         router.GET("/data", [](HttpRequest* req, HttpResponse* resp) {
             static char data[] = "0123456789";
+            // 发送二进制数据
             return resp->Data(data, 10);
         });
 
         // curl -v http://ip:port/html/index.html
         router.GET("/html/index.html", [](HttpRequest* req, HttpResponse* resp) {
+            // 发送文件内容
             return resp->File("html/index.html");
         });
 
         // curl -v http://ip:port/paths
         router.GET("/paths", [&router](HttpRequest* req, HttpResponse* resp) {
+            // 发送json
             return resp->Json(router.Paths());
         });
 
         // curl -v http://ip:port/echo -d "hello,world!"
         router.POST("/echo", [](HttpRequest* req, HttpResponse* resp) {
+            // 回显请求
             resp->content_type = req->content_type;
             resp->body = req->body;
             return 200;
         });
 
+        // 通配符匹配
         // wildcard *
         // curl -v http://ip:port/wildcard/any
         router.GET("/wildcard*", [](HttpRequest* req, HttpResponse* resp) {
@@ -50,6 +57,7 @@ public:
             return resp->String(str);
         });
 
+        // 异步响应
         // curl -v http://ip:port/async
         router.GET("/async", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
             writer->response->headers["X-Request-tid"] = hv::to_string(hv_gettid());
@@ -67,6 +75,7 @@ public:
         router.GET("/www.*", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
             HttpRequestPtr req2(new HttpRequest);
             req2->url = req->path.substr(1);
+            // 异步HTTP客户端请求 + 异步响应
             http_client_send_async(req2, [writer](const HttpResponsePtr& resp2){
                 writer->Begin();
                 if (resp2 == NULL) {