Procházet zdrojové kódy

Disable multi-processes mode for debugging

hewei.it před 4 roky
rodič
revize
7e4502845c

+ 7 - 0
CMakeLists.txt

@@ -78,6 +78,13 @@ set(LIBDIRS . lib 3rd/lib)
 include_directories(${INCDIRS} ${SRCDIR})
 link_directories(${LIBDIRS})
 
+message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
+if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
+    add_definitions(-DDEBUG)
+else()
+    add_definitions(-DNDEBUG)
+endif()
+
 if(ENABLE_IPV6)
     add_definitions(-DENABLE_IPV6)
 endif()

+ 1 - 1
cpputil/hmain.h

@@ -95,7 +95,7 @@ void signal_handler(int signo);
 
 // global var
 #define DEFAULT_WORKER_PROCESSES    4
-#define MAXNUM_WORKER_PROCESSES     1024
+#define MAXNUM_WORKER_PROCESSES     256
 HV_EXPORT extern main_ctx_t   g_main_ctx;
 
 // master-workers processes

+ 10 - 1
etc/httpd.conf

@@ -6,8 +6,17 @@ loglevel = INFO
 log_remain_days = 3
 log_filesize = 64M
 
-# worker_processes = auto # auto = ncpu
+# multi-processes mode
+# auto = ncpu
 worker_processes = auto
+worker_threads = 1
+
+# multi-threads mode
+# worker_processes = 1
+# worker_threads = auto
+
+# Disable multi-processes mode for debugging
+# worker_processes = 0
 
 # http server
 http_port = 8080

+ 17 - 2
examples/hmain_test.cpp

@@ -109,6 +109,10 @@ int parse_confile(const char* confile) {
 
     // worker_processes
     int worker_processes = 0;
+#ifdef DEBUG
+    // Disable multi-processes mode for debugging
+    worker_processes = 0;
+#else
     str = g_conf_ctx.parser->GetValue("worker_processes");
     if (str.size() != 0) {
         if (strcmp(str.c_str(), "auto") == 0) {
@@ -119,10 +123,21 @@ int parse_confile(const char* confile) {
             worker_processes = atoi(str.c_str());
         }
     }
+#endif
     g_conf_ctx.worker_processes = LIMIT(0, worker_processes, MAXNUM_WORKER_PROCESSES);
     // worker_threads
-    int worker_threads = g_conf_ctx.parser->Get<int>("worker_threads");
-    g_conf_ctx.worker_threads = LIMIT(0, worker_threads, 16);
+    int worker_threads = 0;
+    str = g_conf_ctx.parser->GetValue("worker_threads");
+    if (str.size() != 0) {
+        if (strcmp(str.c_str(), "auto") == 0) {
+            worker_threads = get_ncpu();
+            hlogd("worker_threads=ncpu=%d", worker_threads);
+        }
+        else {
+            worker_threads = atoi(str.c_str());
+        }
+    }
+    g_conf_ctx.worker_threads = LIMIT(0, worker_threads, 64);
 
     // port
     int port = 0;

+ 258 - 0
examples/httpd/handler.cpp

@@ -0,0 +1,258 @@
+#include "handler.h"
+
+#include <thread>   // import std::thread
+#include <chrono>   // import std::chrono
+
+#include "hbase.h"
+#include "htime.h"
+#include "hfile.h"
+#include "hstring.h"
+#include "EventLoop.h" // import setTimeout, setInterval
+
+int Handler::preprocessor(HttpRequest* req, HttpResponse* resp) {
+    // printf("%s:%d\n", req->client_addr.ip.c_str(), req->client_addr.port);
+    // printf("%s\n", req->Dump(true, true).c_str());
+    // if (req->content_type != APPLICATION_JSON) {
+    //     return response_status(resp, HTTP_STATUS_BAD_REQUEST);
+    // }
+    req->ParseBody();
+    resp->content_type = APPLICATION_JSON;
+    // cors
+    resp->headers["Access-Control-Allow-Origin"] = "*";
+    if (req->method == HTTP_OPTIONS) {
+        resp->headers["Access-Control-Allow-Origin"] = req->GetHeader("Origin", "*");
+        resp->headers["Access-Control-Allow-Methods"] = req->GetHeader("Access-Control-Request-Method", "OPTIONS, HEAD, GET, POST, PUT, DELETE, PATCH");
+        resp->headers["Access-Control-Allow-Headers"] = req->GetHeader("Access-Control-Request-Headers", "Content-Type");
+        return HTTP_STATUS_NO_CONTENT;
+    }
+#if 0
+    // 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) {
+            response_status(resp, 10012, "Token wrong");
+            return HTTP_STATUS_UNAUTHORIZED;
+        }
+        return HTTP_STATUS_UNFINISHED;
+    }
+#endif
+    return HTTP_STATUS_UNFINISHED;
+}
+
+int Handler::postprocessor(HttpRequest* req, HttpResponse* resp) {
+    // printf("%s\n", resp->Dump(true, true).c_str());
+    return resp->status_code;
+}
+
+int Handler::errorHandler(const HttpContextPtr& ctx) {
+    int error_code = ctx->response->status_code;
+    return response_status(ctx, error_code);
+}
+
+int Handler::largeFileHandler(const HttpContextPtr& ctx) {
+    std::thread([ctx](){
+        ctx->writer->Begin();
+        std::string filepath = ctx->service->document_root + ctx->request->Path();
+        HFile file;
+        if (file.open(filepath.c_str(), "rb") != 0) {
+            ctx->writer->WriteStatus(HTTP_STATUS_NOT_FOUND);
+            ctx->writer->WriteHeader("Content-Type", "text/html");
+            ctx->writer->WriteBody("<center><h1>404 Not Found</h1></center>");
+            ctx->writer->End();
+            return;
+        }
+        http_content_type content_type = CONTENT_TYPE_NONE;
+        const char* suffix = hv_suffixname(filepath.c_str());
+        if (suffix) {
+            content_type = http_content_type_enum_by_suffix(suffix);
+        }
+        if (content_type == CONTENT_TYPE_NONE || content_type == CONTENT_TYPE_UNDEFINED) {
+            content_type = APPLICATION_OCTET_STREAM;
+        }
+        size_t filesize = file.size();
+        ctx->writer->WriteHeader("Content-Type", http_content_type_str(content_type));
+        ctx->writer->WriteHeader("Content-Length", filesize);
+        // ctx->writer->WriteHeader("Transfer-Encoding", "chunked");
+        ctx->writer->EndHeaders();
+
+        char* buf = NULL;
+        int len = 4096; // 4K
+        SAFE_ALLOC(buf, len);
+        size_t total_readbytes = 0;
+        int last_progress = 0;
+        auto start_time = std::chrono::steady_clock::now();
+        auto end_time = start_time;
+        while (total_readbytes < filesize) {
+            size_t readbytes = file.read(buf, len);
+            if (readbytes <= 0) {
+                ctx->writer->close();
+                break;
+            }
+            if (ctx->writer->WriteBody(buf, readbytes) < 0) {
+                break;
+            }
+            total_readbytes += readbytes;
+            int cur_progress = total_readbytes * 100 / filesize;
+            if (cur_progress > last_progress) {
+                // printf("<< %s progress: %ld/%ld = %d%%\n",
+                //     ctx->request->path.c_str(), (long)total_readbytes, (long)filesize, (int)cur_progress);
+                last_progress = cur_progress;
+            }
+            end_time += std::chrono::milliseconds(len / 1024); // 1KB/ms = 1MB/s = 8Mbps
+            std::this_thread::sleep_until(end_time);
+        }
+        ctx->writer->End();
+        SAFE_FREE(buf);
+        // auto elapsed_time = std::chrono::duration_cast<std::chrono::seconds>(end_time - start_time);
+        // printf("<< %s taked %ds\n", ctx->request->path.c_str(), (int)elapsed_time.count());
+    }).detach();
+    return HTTP_STATUS_UNFINISHED;
+}
+
+int Handler::sleep(const HttpContextPtr& ctx) {
+    ctx->set("start_ms", gettimeofday_ms());
+    std::string strTime = ctx->param("t", "1000");
+    if (!strTime.empty()) {
+        int ms = atoi(strTime.c_str());
+        if (ms > 0) {
+            hv_delay(ms);
+        }
+    }
+    ctx->set("end_ms", gettimeofday_ms());
+    response_status(ctx, 0, "OK");
+    return 200;
+}
+
+int Handler::setTimeout(const HttpContextPtr& ctx) {
+    ctx->set("start_ms", gettimeofday_ms());
+    std::string strTime = ctx->param("t", "1000");
+    if (!strTime.empty()) {
+        int ms = atoi(strTime.c_str());
+        if (ms > 0) {
+            hv::setTimeout(ms, [ctx](hv::TimerID timerID){
+                ctx->set("end_ms", gettimeofday_ms());
+                response_status(ctx, 0, "OK");
+                ctx->send();
+            });
+        }
+    }
+    return HTTP_STATUS_UNFINISHED;
+}
+
+int Handler::query(const HttpContextPtr& ctx) {
+    // scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
+    // ?query => HttpRequest::query_params
+    for (auto& param : ctx->params()) {
+        ctx->set(param.first.c_str(), param.second);
+    }
+    response_status(ctx, 0, "OK");
+    return 200;
+}
+
+int Handler::kv(HttpRequest* req, HttpResponse* resp) {
+    if (req->content_type != APPLICATION_URLENCODED) {
+        return response_status(resp, HTTP_STATUS_BAD_REQUEST);
+    }
+    resp->content_type = APPLICATION_URLENCODED;
+    resp->kv = req->kv;
+    resp->kv["int"] = hv::to_string(123);
+    resp->kv["float"] = hv::to_string(3.14);
+    resp->kv["string"] = "hello";
+    return 200;
+}
+
+int Handler::json(HttpRequest* req, HttpResponse* resp) {
+    if (req->content_type != APPLICATION_JSON) {
+        return response_status(resp, HTTP_STATUS_BAD_REQUEST);
+    }
+    resp->content_type = APPLICATION_JSON;
+    resp->json = req->json;
+    resp->json["int"] = 123;
+    resp->json["float"] = 3.14;
+    resp->json["string"] = "hello";
+    return 200;
+}
+
+int Handler::form(HttpRequest* req, HttpResponse* resp) {
+    if (req->content_type != MULTIPART_FORM_DATA) {
+        return response_status(resp, HTTP_STATUS_BAD_REQUEST);
+    }
+    resp->content_type = MULTIPART_FORM_DATA;
+    resp->form = req->form;
+    resp->form["int"] = 123;
+    resp->form["float"] = 3.14;
+    resp->form["string"] = "hello";
+    // resp->form["file"] = FormFile("test.jpg");
+    // resp->FormFile("file", "test.jpg");
+    return 200;
+}
+
+int Handler::grpc(HttpRequest* req, HttpResponse* resp) {
+    if (req->content_type != APPLICATION_GRPC) {
+        return response_status(resp, HTTP_STATUS_BAD_REQUEST);
+    }
+    // parse protobuf
+    // ParseFromString(req->body);
+    // resp->content_type = APPLICATION_GRPC;
+    // serailize protobuf
+    // resp->body = SerializeAsString(xxx);
+    response_status(resp, 0, "OK");
+    return 200;
+}
+
+int Handler::test(const HttpContextPtr& ctx) {
+    ctx->setContentType(ctx->type());
+    ctx->set("bool", ctx->get<bool>("bool"));
+    ctx->set("int", ctx->get<int>("int"));
+    ctx->set("float", ctx->get<float>("float"));
+    ctx->set("string", ctx->get("string"));
+    response_status(ctx, 0, "OK");
+    return 200;
+}
+
+int Handler::restful(const HttpContextPtr& ctx) {
+    // RESTful /:field/ => HttpRequest::query_params
+    // path=/group/:group_name/user/:user_id
+    std::string group_name = ctx->param("group_name");
+    std::string user_id = ctx->param("user_id");
+    ctx->set("group_name", group_name);
+    ctx->set("user_id", user_id);
+    response_status(ctx, 0, "OK");
+    return 200;
+}
+
+int Handler::login(const HttpContextPtr& ctx) {
+    string username = ctx->get("username");
+    string password = ctx->get("password");
+    if (username.empty() || password.empty()) {
+        response_status(ctx, 10001, "Miss username or password");
+        return HTTP_STATUS_BAD_REQUEST;
+    }
+    else if (strcmp(username.c_str(), "admin") != 0) {
+        response_status(ctx, 10002, "Username not exist");
+        return HTTP_STATUS_BAD_REQUEST;
+    }
+    else if (strcmp(password.c_str(), "123456") != 0) {
+        response_status(ctx, 10003, "Password wrong");
+        return HTTP_STATUS_BAD_REQUEST;
+    }
+    else {
+        ctx->set("token", "abcdefg");
+        response_status(ctx, 0, "OK");
+        return HTTP_STATUS_OK;
+    }
+}
+
+int Handler::upload(const HttpContextPtr& ctx) {
+    int status_code = 200;
+    if (ctx->is(MULTIPART_FORM_DATA)) {
+        status_code = ctx->request->SaveFormFile("file", "html/uploads/");
+    } else {
+        status_code = ctx->request->SaveFile("html/uploads/upload.txt");
+    }
+    return response_status(ctx, status_code);
+}

+ 15 - 260
examples/httpd/handler.h

@@ -1,276 +1,31 @@
 #ifndef HV_HTTPD_HANDLER_H
 #define HV_HTTPD_HANDLER_H
 
-#include <thread>   // import std::thread
-#include <chrono>   // import std::chrono
-
-#include "hbase.h"
-#include "htime.h"
-#include "hfile.h"
-#include "hstring.h"
-#include "EventLoop.h" // import setTimeout, setInterval
 #include "HttpService.h"
 
 class Handler {
 public:
     // preprocessor => api_handlers => postprocessor
-    static int preprocessor(HttpRequest* req, HttpResponse* resp) {
-        // printf("%s:%d\n", req->client_addr.ip.c_str(), req->client_addr.port);
-        // printf("%s\n", req->Dump(true, true).c_str());
-        // if (req->content_type != APPLICATION_JSON) {
-        //     return response_status(resp, HTTP_STATUS_BAD_REQUEST);
-        // }
-        req->ParseBody();
-        resp->content_type = APPLICATION_JSON;
-        // cors
-        resp->headers["Access-Control-Allow-Origin"] = "*";
-        if (req->method == HTTP_OPTIONS) {
-            resp->headers["Access-Control-Allow-Origin"] = req->GetHeader("Origin", "*");
-            resp->headers["Access-Control-Allow-Methods"] = req->GetHeader("Access-Control-Request-Method", "OPTIONS, HEAD, GET, POST, PUT, DELETE, PATCH");
-            resp->headers["Access-Control-Allow-Headers"] = req->GetHeader("Access-Control-Request-Headers", "Content-Type");
-            return HTTP_STATUS_NO_CONTENT;
-        }
-#if 0
-        // 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) {
-                response_status(resp, 10012, "Token wrong");
-                return HTTP_STATUS_UNAUTHORIZED;
-            }
-            return HTTP_STATUS_UNFINISHED;
-        }
-#endif
-        return HTTP_STATUS_UNFINISHED;
-    }
-
-    static int postprocessor(HttpRequest* req, HttpResponse* resp) {
-        // printf("%s\n", resp->Dump(true, true).c_str());
-        return resp->status_code;
-    }
-
-    static int errorHandler(const HttpContextPtr& ctx) {
-        int error_code = ctx->response->status_code;
-        return response_status(ctx, error_code);
-    }
-
-    static int largeFileHandler(const HttpContextPtr& ctx) {
-        std::thread([ctx](){
-            ctx->writer->Begin();
-            std::string filepath = ctx->service->document_root + ctx->request->Path();
-            HFile file;
-            if (file.open(filepath.c_str(), "rb") != 0) {
-                ctx->writer->WriteStatus(HTTP_STATUS_NOT_FOUND);
-                ctx->writer->WriteHeader("Content-Type", "text/html");
-                ctx->writer->WriteBody("<center><h1>404 Not Found</h1></center>");
-                ctx->writer->End();
-                return;
-            }
-            http_content_type content_type = CONTENT_TYPE_NONE;
-            const char* suffix = hv_suffixname(filepath.c_str());
-            if (suffix) {
-                content_type = http_content_type_enum_by_suffix(suffix);
-            }
-            if (content_type == CONTENT_TYPE_NONE || content_type == CONTENT_TYPE_UNDEFINED) {
-                content_type = APPLICATION_OCTET_STREAM;
-            }
-            size_t filesize = file.size();
-            ctx->writer->WriteHeader("Content-Type", http_content_type_str(content_type));
-            ctx->writer->WriteHeader("Content-Length", filesize);
-            // ctx->writer->WriteHeader("Transfer-Encoding", "chunked");
-            ctx->writer->EndHeaders();
-
-            char* buf = NULL;
-            int len = 4096; // 4K
-            SAFE_ALLOC(buf, len);
-            size_t total_readbytes = 0;
-            int last_progress = 0;
-            auto start_time = std::chrono::steady_clock::now();
-            auto end_time = start_time;
-            while (total_readbytes < filesize) {
-                size_t readbytes = file.read(buf, len);
-                if (readbytes <= 0) {
-                    ctx->writer->close();
-                    break;
-                }
-                if (ctx->writer->WriteBody(buf, readbytes) < 0) {
-                    break;
-                }
-                total_readbytes += readbytes;
-                int cur_progress = total_readbytes * 100 / filesize;
-                if (cur_progress > last_progress) {
-                    // printf("<< %s progress: %ld/%ld = %d%%\n",
-                    //     ctx->request->path.c_str(), (long)total_readbytes, (long)filesize, (int)cur_progress);
-                    last_progress = cur_progress;
-                }
-                end_time += std::chrono::milliseconds(len / 1024); // 1KB/ms = 1MB/s = 8Mbps
-                std::this_thread::sleep_until(end_time);
-            }
-            ctx->writer->End();
-            SAFE_FREE(buf);
-            // auto elapsed_time = std::chrono::duration_cast<std::chrono::seconds>(end_time - start_time);
-            // printf("<< %s taked %ds\n", ctx->request->path.c_str(), (int)elapsed_time.count());
-        }).detach();
-        return HTTP_STATUS_UNFINISHED;
-    }
-
-    static int sleep(const HttpContextPtr& ctx) {
-        ctx->set("start_ms", gettimeofday_ms());
-        std::string strTime = ctx->param("t", "1000");
-        if (!strTime.empty()) {
-            int ms = atoi(strTime.c_str());
-            if (ms > 0) {
-                hv_delay(ms);
-            }
-        }
-        ctx->set("end_ms", gettimeofday_ms());
-        response_status(ctx, 0, "OK");
-        return 200;
-    }
-
-    static int setTimeout(const HttpContextPtr& ctx) {
-        ctx->set("start_ms", gettimeofday_ms());
-        std::string strTime = ctx->param("t", "1000");
-        if (!strTime.empty()) {
-            int ms = atoi(strTime.c_str());
-            if (ms > 0) {
-                hv::setTimeout(ms, [ctx](hv::TimerID timerID){
-                    ctx->set("end_ms", gettimeofday_ms());
-                    response_status(ctx, 0, "OK");
-                    ctx->send();
-                });
-            }
-        }
-        return HTTP_STATUS_UNFINISHED;
-    }
+    static int preprocessor(HttpRequest* req, HttpResponse* resp);
+    static int postprocessor(HttpRequest* req, HttpResponse* resp);
 
-    static int query(const HttpContextPtr& ctx) {
-        // scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
-        // ?query => HttpRequest::query_params
-        for (auto& param : ctx->params()) {
-            ctx->set(param.first.c_str(), param.second);
-        }
-        response_status(ctx, 0, "OK");
-        return 200;
-    }
+    static int errorHandler(const HttpContextPtr& ctx);
+    static int largeFileHandler(const HttpContextPtr& ctx);
 
-    static int kv(HttpRequest* req, HttpResponse* resp) {
-        if (req->content_type != APPLICATION_URLENCODED) {
-            return response_status(resp, HTTP_STATUS_BAD_REQUEST);
-        }
-        resp->content_type = APPLICATION_URLENCODED;
-        resp->kv = req->kv;
-        resp->kv["int"] = hv::to_string(123);
-        resp->kv["float"] = hv::to_string(3.14);
-        resp->kv["string"] = "hello";
-        return 200;
-    }
+    static int sleep(const HttpContextPtr& ctx);
+    static int setTimeout(const HttpContextPtr& ctx);
+    static int query(const HttpContextPtr& ctx);
 
-    static int json(HttpRequest* req, HttpResponse* resp) {
-        if (req->content_type != APPLICATION_JSON) {
-            return response_status(resp, HTTP_STATUS_BAD_REQUEST);
-        }
-        resp->content_type = APPLICATION_JSON;
-        resp->json = req->json;
-        resp->json["int"] = 123;
-        resp->json["float"] = 3.14;
-        resp->json["string"] = "hello";
-        return 200;
-    }
+    static int kv(HttpRequest* req, HttpResponse* resp);
+    static int json(HttpRequest* req, HttpResponse* resp);
+    static int form(HttpRequest* req, HttpResponse* resp);
+    static int grpc(HttpRequest* req, HttpResponse* resp);
 
-    static int form(HttpRequest* req, HttpResponse* resp) {
-        if (req->content_type != MULTIPART_FORM_DATA) {
-            return response_status(resp, HTTP_STATUS_BAD_REQUEST);
-        }
-        resp->content_type = MULTIPART_FORM_DATA;
-        resp->form = req->form;
-        resp->form["int"] = 123;
-        resp->form["float"] = 3.14;
-        resp->form["string"] = "hello";
-        // resp->form["file"] = FormFile("test.jpg");
-        // resp->FormFile("file", "test.jpg");
-        return 200;
-    }
+    static int test(const HttpContextPtr& ctx);
+    static int restful(const HttpContextPtr& ctx);
 
-    static int grpc(HttpRequest* req, HttpResponse* resp) {
-        if (req->content_type != APPLICATION_GRPC) {
-            return response_status(resp, HTTP_STATUS_BAD_REQUEST);
-        }
-        // parse protobuf
-        // ParseFromString(req->body);
-        // resp->content_type = APPLICATION_GRPC;
-        // serailize protobuf
-        // resp->body = SerializeAsString(xxx);
-        response_status(resp, 0, "OK");
-        return 200;
-    }
-
-    static int test(const HttpContextPtr& ctx) {
-        ctx->setContentType(ctx->type());
-        ctx->set("bool", ctx->get<bool>("bool"));
-        ctx->set("int", ctx->get<int>("int"));
-        ctx->set("float", ctx->get<float>("float"));
-        ctx->set("string", ctx->get("string"));
-        response_status(ctx, 0, "OK");
-        return 200;
-    }
-
-    static int restful(const HttpContextPtr& ctx) {
-        // RESTful /:field/ => HttpRequest::query_params
-        // path=/group/:group_name/user/:user_id
-        std::string group_name = ctx->param("group_name");
-        std::string user_id = ctx->param("user_id");
-        ctx->set("group_name", group_name);
-        ctx->set("user_id", user_id);
-        response_status(ctx, 0, "OK");
-        return 200;
-    }
-
-    static int login(const HttpContextPtr& ctx) {
-        string username = ctx->get("username");
-        string password = ctx->get("password");
-        if (username.empty() || password.empty()) {
-            response_status(ctx, 10001, "Miss username or password");
-            return HTTP_STATUS_BAD_REQUEST;
-        }
-        else if (strcmp(username.c_str(), "admin") != 0) {
-            response_status(ctx, 10002, "Username not exist");
-            return HTTP_STATUS_BAD_REQUEST;
-        }
-        else if (strcmp(password.c_str(), "123456") != 0) {
-            response_status(ctx, 10003, "Password wrong");
-            return HTTP_STATUS_BAD_REQUEST;
-        }
-        else {
-            ctx->set("token", "abcdefg");
-            response_status(ctx, 0, "OK");
-            return HTTP_STATUS_OK;
-        }
-    }
-
-    static int upload(HttpRequest* req, HttpResponse* resp) {
-        // return resp->SaveFormFile("file", "html/uploads/test.jpg");
-        if (req->content_type != MULTIPART_FORM_DATA) {
-            return response_status(resp, HTTP_STATUS_BAD_REQUEST);
-        }
-        const FormData& file = req->form["file"];
-        if (file.content.empty()) {
-            return response_status(resp, HTTP_STATUS_BAD_REQUEST);
-        }
-        string filepath("html/uploads/");
-        filepath += file.filename;
-        FILE* fp = fopen(filepath.c_str(), "wb");
-        if (fp) {
-            fwrite(file.content.data(), 1, file.content.size(), fp);
-            fclose(fp);
-        }
-        response_status(resp, 0, "OK");
-        return 200;
-    }
+    static int login(const HttpContextPtr& ctx);
+    static int upload(const HttpContextPtr& ctx);
 
 private:
     static int response_status(HttpResponse* resp, int code = 200, const char* message = NULL) {

+ 17 - 2
examples/httpd/httpd.cpp

@@ -86,6 +86,10 @@ int parse_confile(const char* confile) {
 
     // worker_processes
     int worker_processes = 0;
+#ifdef DEBUG
+    // Disable multi-processes mode for debugging
+    worker_processes = 0;
+#else
     str = ini.GetValue("worker_processes");
     if (str.size() != 0) {
         if (strcmp(str.c_str(), "auto") == 0) {
@@ -96,10 +100,21 @@ int parse_confile(const char* confile) {
             worker_processes = atoi(str.c_str());
         }
     }
+#endif
     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);
+    int worker_threads = 0;
+    str = ini.GetValue("worker_threads");
+    if (str.size() != 0) {
+        if (strcmp(str.c_str(), "auto") == 0) {
+            worker_threads = get_ncpu();
+            hlogd("worker_threads=ncpu=%d", worker_threads);
+        }
+        else {
+            worker_threads = atoi(str.c_str());
+        }
+    }
+    g_http_server.worker_threads = LIMIT(0, worker_threads, 64);
 
     // http_port
     int port = 0;

+ 139 - 0
examples/httpd/router.cpp

@@ -0,0 +1,139 @@
+#include "router.h"
+
+#include <future> // import std::async
+
+#include "handler.h"
+#include "hthread.h"
+#include "requests.h"
+
+void Router::Register(HttpService& router) {
+    // preprocessor => Handler => postprocessor
+    router.preprocessor = Handler::preprocessor;
+    router.postprocessor = Handler::postprocessor;
+    router.largeFileHandler = Handler::largeFileHandler;
+    // router.errorHandler = Handler::errorHandler;
+
+    // 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 /*, false */);
+    });
+
+    // 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) {
+        return resp->Json(router.Paths());
+    });
+
+    // curl -v http://ip:port/get?env=1
+    router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
+        resp->json["origin"] = req->client_addr.ip;
+        resp->json["url"] = req->url;
+        resp->json["args"] = req->query_params;
+        resp->json["headers"] = req->headers;
+        return 200;
+    });
+
+    // curl -v http://ip:port/service
+    router.GET("/service", [](const HttpContextPtr& ctx) {
+        ctx->setContentType("application/json");
+        ctx->set("base_url", ctx->service->base_url);
+        ctx->set("document_root", ctx->service->document_root);
+        ctx->set("home_page", ctx->service->home_page);
+        ctx->set("error_page", ctx->service->error_page);
+        ctx->set("index_of", ctx->service->index_of);
+        return 200;
+    });
+
+    // curl -v http://ip:port/echo -d "hello,world!"
+    router.POST("/echo", [](const HttpContextPtr& ctx) {
+        return ctx->send(ctx->body(), ctx->type());
+    });
+
+    // wildcard *
+    // curl -v http://ip:port/wildcard/any
+    router.GET("/wildcard*", [](HttpRequest* req, HttpResponse* resp) {
+        std::string str = req->path + " match /wildcard*";
+        return resp->String(str);
+    });
+
+    // curl -v http://ip:port/async
+    router.GET("/async", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
+        writer->WriteHeader("X-Request-tid", hv_gettid());
+        std::async([req, writer](){
+            writer->WriteHeader("X-Response-tid", hv_gettid());
+            writer->WriteHeader("Content-Type", "text/plain");
+            writer->WriteBody("This is an async response.\n");
+            writer->End();
+        });
+    });
+
+    // curl -v http://ip:port/www.*
+    // curl -v http://ip:port/www.example.com
+    router.GET("/www.*", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
+        HttpRequestPtr req2(new HttpRequest);
+        req2->url = req->path.substr(1);
+        requests::async(req2, [writer](const HttpResponsePtr& resp2){
+            writer->Begin();
+            if (resp2 == NULL) {
+                writer->WriteStatus(HTTP_STATUS_NOT_FOUND);
+                writer->WriteHeader("Content-Type", "text/html");
+                writer->WriteBody("<center><h1>404 Not Found</h1></center>");
+            } else {
+                writer->WriteResponse(resp2.get());
+            }
+            writer->End();
+        });
+    });
+
+    // curl -v http://ip:port/sleep?t=1000
+    router.GET("/sleep", Handler::sleep);
+
+    // curl -v http://ip:port/setTimeout?t=1000
+    router.GET("/setTimeout", Handler::setTimeout);
+
+    // curl -v http://ip:port/query?page_no=1\&page_size=10
+    router.GET("/query", Handler::query);
+
+    // Content-Type: application/x-www-form-urlencoded
+    // curl -v http://ip:port/kv -H "content-type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
+    router.POST("/kv", Handler::kv);
+
+    // Content-Type: application/json
+    // curl -v http://ip:port/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
+    router.POST("/json", Handler::json);
+
+    // Content-Type: multipart/form-data
+    // bin/curl -v http://ip:port/form -F "user=admin pswd=123456"
+    router.POST("/form", Handler::form);
+
+    // curl -v http://ip:port/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
+    // curl -v http://ip:port/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
+    // bin/curl -v http://ip:port/test -F 'bool=1 int=123 float=3.14 string=hello'
+    router.POST("/test", Handler::test);
+
+    // Content-Type: application/grpc
+    // bin/curl -v --http2 http://ip:port/grpc -H "content-type:application/grpc" -d 'protobuf'
+    router.POST("/grpc", Handler::grpc);
+
+    // RESTful API: /group/:group_name/user/:user_id
+    // curl -v -X DELETE http://ip:port/group/test/user/123
+    router.Delete("/group/:group_name/user/:user_id", Handler::restful);
+    // router.Delete("/group/{group_name}/user/{user_id}", Handler::restful);
+
+    // curl -v http://ip:port/login -H "Content-Type:application/json" -d '{"username":"admin","password":"123456"}'
+    router.POST("/login", Handler::login);
+
+    // curl -v http://ip:port/upload -d "hello,world!"
+    // curl -v http://ip:port/upload -F "file=@LICENSE"
+    router.POST("/upload", Handler::upload);
+}

+ 1 - 135
examples/httpd/router.h

@@ -1,145 +1,11 @@
 #ifndef HV_HTTPD_ROUTER_H
 #define HV_HTTPD_ROUTER_H
 
-#include <future> // import std::async
-
 #include "HttpService.h"
-#include "requests.h"
-
-#include "handler.h"
 
 class Router {
 public:
-    static void Register(HttpService& router) {
-        // preprocessor => Handler => postprocessor
-        router.preprocessor = Handler::preprocessor;
-        router.postprocessor = Handler::postprocessor;
-        router.largeFileHandler = Handler::largeFileHandler;
-        // router.errorHandler = Handler::errorHandler;
-
-        // 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 /*, false */);
-        });
-
-        // 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) {
-            return resp->Json(router.Paths());
-        });
-
-        // curl -v http://ip:port/get?env=1
-        router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
-            resp->json["origin"] = req->client_addr.ip;
-            resp->json["url"] = req->url;
-            resp->json["args"] = req->query_params;
-            resp->json["headers"] = req->headers;
-            return 200;
-        });
-
-        // curl -v http://ip:port/service
-        router.GET("/service", [](const HttpContextPtr& ctx) {
-            ctx->setContentType("application/json");
-            ctx->set("base_url", ctx->service->base_url);
-            ctx->set("document_root", ctx->service->document_root);
-            ctx->set("home_page", ctx->service->home_page);
-            ctx->set("error_page", ctx->service->error_page);
-            ctx->set("index_of", ctx->service->index_of);
-            return 200;
-        });
-
-        // curl -v http://ip:port/echo -d "hello,world!"
-        router.POST("/echo", [](const HttpContextPtr& ctx) {
-            return ctx->send(ctx->body(), ctx->type());
-        });
-
-        // wildcard *
-        // curl -v http://ip:port/wildcard/any
-        router.GET("/wildcard*", [](HttpRequest* req, HttpResponse* resp) {
-            std::string str = req->path + " match /wildcard*";
-            return resp->String(str);
-        });
-
-        // curl -v http://ip:port/async
-        router.GET("/async", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
-            writer->WriteHeader("X-Request-tid", hv_gettid());
-            std::async([req, writer](){
-                writer->WriteHeader("X-Response-tid", hv_gettid());
-                writer->WriteHeader("Content-Type", "text/plain");
-                writer->WriteBody("This is an async response.\n");
-                writer->End();
-            });
-        });
-
-        // curl -v http://ip:port/www.*
-        // curl -v http://ip:port/www.example.com
-        router.GET("/www.*", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
-            HttpRequestPtr req2(new HttpRequest);
-            req2->url = req->path.substr(1);
-            requests::async(req2, [writer](const HttpResponsePtr& resp2){
-                writer->Begin();
-                if (resp2 == NULL) {
-                    writer->WriteStatus(HTTP_STATUS_NOT_FOUND);
-                    writer->WriteHeader("Content-Type", "text/html");
-                    writer->WriteBody("<center><h1>404 Not Found</h1></center>");
-                } else {
-                    writer->WriteResponse(resp2.get());
-                }
-                writer->End();
-            });
-        });
-
-        // curl -v http://ip:port/sleep?t=1000
-        router.GET("/sleep", Handler::sleep);
-
-        // curl -v http://ip:port/setTimeout?t=1000
-        router.GET("/setTimeout", Handler::setTimeout);
-
-        // curl -v http://ip:port/query?page_no=1\&page_size=10
-        router.GET("/query", Handler::query);
-
-        // Content-Type: application/x-www-form-urlencoded
-        // curl -v http://ip:port/kv -H "content-type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
-        router.POST("/kv", Handler::kv);
-
-        // Content-Type: application/json
-        // curl -v http://ip:port/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
-        router.POST("/json", Handler::json);
-
-        // Content-Type: multipart/form-data
-        // bin/curl -v localhost:8080/form -F "user=admin pswd=123456"
-        router.POST("/form", Handler::form);
-
-        // curl -v http://ip:port/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
-        // curl -v http://ip:port/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
-        // bin/curl -v http://ip:port/test -F 'bool=1 int=123 float=3.14 string=hello'
-        router.POST("/test", Handler::test);
-
-        // Content-Type: application/grpc
-        // bin/curl -v --http2 http://ip:port/grpc -H "content-type:application/grpc" -d 'protobuf'
-        router.POST("/grpc", Handler::grpc);
-
-        // RESTful API: /group/:group_name/user/:user_id
-        // curl -v -X DELETE http://ip:port/group/test/user/123
-        router.Delete("/group/:group_name/user/:user_id", Handler::restful);
-        // router.Delete("/group/{group_name}/user/{user_id}", Handler::restful);
-
-        // bin/curl -v localhost:8080/upload -F "file=@LICENSE"
-        router.POST("/upload", Handler::upload);
-
-        // curl -v http://ip:port/login -H "Content-Type:application/json" -d '{"username":"admin","password":"123456"}'
-        router.POST("/login", Handler::login);
-    }
+    static void Register(HttpService& router);
 };
 
 #endif // HV_HTTPD_ROUTER_H