/** * main.c - Cocoon 入口 * * 解析命令行参数和配置文件,初始化并启动服务器。 * * @author xfy */ #include "cocoon.h" #include "server.h" #include "config.h" #include "platform.h" #include "access_log.h" #include "plugin.h" #include #include #include #include /** * g_ctx - 全局服务器上下文(用于信号处理) */ static server_context_t *g_ctx = NULL; /** * g_reload_plugins - 插件热重载标志(由 SIGUSR1 触发) */ static volatile int g_reload_plugins = 0; /** * signal_handler - 优雅关闭信号处理 * * 捕获 SIGINT/SIGTERM,触发服务器停止。 * * @param sig 信号编号 */ static void signal_handler(int sig) { (void)sig; if (g_ctx) { printf("\n[Cocoon] 收到关闭信号,正在优雅停止...\n"); server_stop(g_ctx); } } /** * reload_handler - 插件热重载信号处理 * * 捕获 SIGUSR1,设置热重载标志。 * 主循环中检测到标志后执行 cocoon_plugin_reload()。 * * @param sig 信号编号 */ static void reload_handler(int sig) { (void)sig; printf("\n[Cocoon] 收到 SIGUSR1,热重载插件...\n"); cocoon_plugin_reload(); } static void sighup_handler(int sig) { (void)sig; printf("\n[Cocoon] 收到 SIGHUP,准备热重载配置...\n"); server_request_reload(g_ctx); } /** * print_usage - 打印使用说明 * * @param prog 程序名 */ static void print_usage(const char *prog) { printf("Usage: %s [options]\n\n", prog); printf("Options:\n"); printf(" -c JSON 配置文件路径\n"); printf(" -r 静态资源根目录(必填,或配置文件指定)\n"); printf(" -p 监听端口(默认 8080)\n"); printf(" -t 启用多线程调度\n"); printf(" -w 工作线程数(默认自动检测 CPU 核心)\n"); printf(" -m 最大并发连接数(默认无限制)\n"); printf(" -o 连接空闲超时毫秒(默认 30000)\n"); printf(" -l 日志级别: error, warn, info, debug(默认 info)\n"); printf(" -v 详细日志输出(等同于 -l debug)\n"); printf(" --cert TLS 证书路径(启用 HTTPS)\n"); printf(" --key TLS 私钥路径\n"); printf(" --tls 显式启用 TLS(需同时指定 --cert 和 --key)\n"); printf(" --no-gzip 禁用 gzip 压缩\n"); printf(" --access-log 访问日志文件路径(- 表示 stdout)\n"); printf(" --cors 启用 CORS 支持\n"); printf(" --auth-user Basic Auth 用户名\n"); printf(" --auth-pass Basic Auth 密码\n"); printf(" --rate-limit 每秒最大请求数(限流)\n"); printf(" --plugin 加载插件(可多次指定)\n"); printf(" --acme 启用 ACME 自动证书\n"); printf("\nSignals:\n"); printf(" SIGHUP 热重载配置文件(无需重启服务器)\n"); printf(" SIGUSR1 热重载所有插件(无需重启服务器)\n"); printf("\nExample:\n"); printf(" %s -c cocoon.json\n", prog); printf(" %s -r ./www -p 8080\n", prog); printf(" %s -c cocoon.json -p 9090 # 命令行覆盖配置文件的端口\n", prog); } /** * parse_args - 解析命令行参数 * * 支持配置文件 + 命令行覆盖的混合模式。 * * @param argc 参数个数 * @param argv 参数数组 * @param config 输出配置结构体 * @return true 成功,false 参数错误 */ static bool parse_args(int argc, char *argv[], cocoon_config_t *config) { /* 默认值 */ memset(config, 0, sizeof(*config)); config->port = 8080; config->log_level = LOG_LEVEL_INFO; config->gzip_enabled = true; config->brotli_enabled = true; /* 第一阶段:查找配置文件路径 */ const char *config_file = NULL; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-c") == 0) { if (++i >= argc) return false; config_file = argv[i]; } } /* 第二阶段:加载配置文件(如果有) */ if (config_file) { if (!config_load_from_file(config_file, config)) { fprintf(stderr, "Error: 无法加载配置文件: %s\n", config_file); return false; } } /* 第三阶段:解析命令行参数,覆盖配置文件 */ for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-c") == 0) { i++; /* 已处理,跳过 */ } else if (strcmp(argv[i], "-r") == 0) { if (++i >= argc) return false; free((void *)config->root_dir); config->root_dir = strdup(argv[i]); } else if (strcmp(argv[i], "-p") == 0) { if (++i >= argc) return false; config->port = (uint16_t)atoi(argv[i]); if (config->port == 0) config->port = 8080; } else if (strcmp(argv[i], "-t") == 0) { config->threaded = true; } else if (strcmp(argv[i], "-w") == 0) { if (++i >= argc) return false; config->num_workers = (uint32_t)atoi(argv[i]); } else if (strcmp(argv[i], "-m") == 0) { if (++i >= argc) return false; config->max_connections = (uint32_t)atoi(argv[i]); } else if (strcmp(argv[i], "-o") == 0) { if (++i >= argc) return false; config->timeout_ms = (uint32_t)atoi(argv[i]); } else if (strcmp(argv[i], "-l") == 0) { if (++i >= argc) return false; if (strcmp(argv[i], "error") == 0) config->log_level = LOG_LEVEL_ERROR; else if (strcmp(argv[i], "warn") == 0) config->log_level = LOG_LEVEL_WARN; else if (strcmp(argv[i], "info") == 0) config->log_level = LOG_LEVEL_INFO; else if (strcmp(argv[i], "debug") == 0) config->log_level = LOG_LEVEL_DEBUG; else { fprintf(stderr, "Unknown log level: %s\n", argv[i]); return false; } } else if (strcmp(argv[i], "-v") == 0) { config->log_level = LOG_LEVEL_DEBUG; } else if (strcmp(argv[i], "--no-gzip") == 0) { config->gzip_enabled = false; } else if (strcmp(argv[i], "--no-brotli") == 0) { config->brotli_enabled = false; } else if (strcmp(argv[i], "--cert") == 0) { if (++i >= argc) return false; free((void *)config->tls_cert); config->tls_cert = strdup(argv[i]); } else if (strcmp(argv[i], "--key") == 0) { if (++i >= argc) return false; free((void *)config->tls_key); config->tls_key = strdup(argv[i]); } else if (strcmp(argv[i], "--tls") == 0) { config->tls_enabled = true; } else if (strcmp(argv[i], "--access-log") == 0) { if (++i >= argc) return false; free((void *)config->access_log_path); config->access_log_path = strdup(argv[i]); } else if (strcmp(argv[i], "--cors") == 0) { config->cors_enabled = true; } else if (strcmp(argv[i], "--auth-user") == 0) { if (++i >= argc) return false; free((void *)config->auth_user); config->auth_user = strdup(argv[i]); } else if (strcmp(argv[i], "--auth-pass") == 0) { if (++i >= argc) return false; free((void *)config->auth_pass); config->auth_pass = strdup(argv[i]); } else if (strcmp(argv[i], "--rate-limit") == 0) { if (++i >= argc) return false; config->rate_limit = (uint32_t)atoi(argv[i]); } else if (strcmp(argv[i], "--plugin") == 0) { if (++i >= argc) return false; if (config->num_plugins < COCOON_MAX_PLUGINS) { config->plugins[config->num_plugins++] = strdup(argv[i]); } else { fprintf(stderr, "Error: 最多支持 %d 个插件\n", COCOON_MAX_PLUGINS); return false; } } else if (strcmp(argv[i], "--acme") == 0) { config->acme_enabled = true; } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { print_usage(argv[0]); exit(0); } else { fprintf(stderr, "Unknown option: %s\n", argv[i]); return false; } } if (!config->root_dir) { fprintf(stderr, "Error: 必须指定静态资源根目录(-r 或配置文件)\n"); return false; } return true; } /** * main - 程序入口 * * 解析参数,注册信号,创建并启动服务器。 * * @param argc 参数个数 * @param argv 参数数组 * @return 0 成功,1 失败 */ int main(int argc, char *argv[]) { cocoon_config_t config = {0}; const char *config_file_path = NULL; if (!parse_args(argc, argv, &config)) { print_usage(argv[0]); return 1; } /* 从参数中查找配置文件路径 */ for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) { config_file_path = argv[i + 1]; break; } } /* 初始化 socket 子系统(Windows 下 WSAStartup) */ if (cocoon_socket_init() != 0) { fprintf(stderr, "[Cocoon] socket 子系统初始化失败\n"); return 1; } /* 注册信号处理 */ cocoon_signal_setup(signal_handler); signal(SIGUSR1, reload_handler); signal(SIGHUP, sighup_handler); signal(SIGPIPE, SIG_IGN); /* 忽略 SIGPIPE,防止写入已关闭的连接时进程终止 */ /* 设置日志级别 */ log_set_level(config.log_level); /* 初始化访问日志 */ if (config.access_log_path) { access_log_init(config.access_log_path); } /* 创建服务器 */ g_ctx = server_create(&config, config_file_path); if (!g_ctx) { fprintf(stderr, "[Cocoon] 创建服务器失败\n"); return 1; } /* 启动(阻塞) */ int ret = server_start(g_ctx); /* 清理 */ server_destroy(g_ctx); g_ctx = NULL; /* 释放 socket 子系统(Windows 下 WSACleanup) */ cocoon_socket_cleanup(); /* 关闭访问日志 */ access_log_close(); /* 释放配置文件分配的内存 */ /* 释放插件路径 */ for (size_t i = 0; i < config.num_plugins; i++) { free((void *)config.plugins[i]); } if (config.root_dir) free((void *)config.root_dir); if (config.tls_cert) free((void *)config.tls_cert); if (config.tls_key) free((void *)config.tls_key); if (config.access_log_path) free((void *)config.access_log_path); if (config.auth_user) free((void *)config.auth_user); if (config.auth_pass) free((void *)config.auth_pass); return ret == COCOON_OK ? 0 : 1; }