Skip to content

Commit a2bb6f6

Browse files
committed
Update docker/main.cc
1 parent 7012e76 commit a2bb6f6

File tree

1 file changed

+129
-40
lines changed

1 file changed

+129
-40
lines changed

docker/main.cc

Lines changed: 129 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818

1919
using namespace httplib;
2020

21-
auto SERVER_NAME = std::format("cpp-httplib-server/{}", CPPHTTPLIB_VERSION);
21+
const auto SERVER_NAME =
22+
std::format("cpp-httplib-server/{}", CPPHTTPLIB_VERSION);
2223

2324
Server svr;
2425

2526
void signal_handler(int signal) {
2627
if (signal == SIGINT || signal == SIGTERM) {
27-
std::cout << std::format("\nReceived signal, shutting down gracefully...")
28-
<< std::endl;
28+
std::cout << "\nReceived signal, shutting down gracefully...\n";
2929
svr.stop();
3030
}
3131
}
@@ -73,17 +73,16 @@ std::string get_client_ip(const Request &req) {
7373
// $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent
7474
// "$http_referer" "$http_user_agent"
7575
void nginx_access_logger(const Request &req, const Response &res) {
76-
std::string remote_addr = get_client_ip(req);
76+
auto remote_addr = get_client_ip(req);
7777
std::string remote_user =
7878
"-"; // cpp-httplib doesn't have built-in auth user tracking
79-
std::string time_local = get_time_format();
80-
std::string request =
81-
std::format("{} {} {}", req.method, req.path, req.version);
82-
int status = res.status;
83-
size_t body_bytes_sent = res.body.size();
84-
std::string http_referer = req.get_header_value("Referer");
79+
auto time_local = get_time_format();
80+
auto request = std::format("{} {} {}", req.method, req.path, req.version);
81+
auto status = res.status;
82+
auto body_bytes_sent = res.body.size();
83+
auto http_referer = req.get_header_value("Referer");
8584
if (http_referer.empty()) http_referer = "-";
86-
std::string http_user_agent = req.get_header_value("User-Agent");
85+
auto http_user_agent = req.get_header_value("User-Agent");
8786
if (http_user_agent.empty()) http_user_agent = "-";
8887

8988
std::cout << std::format("{} - {} [{}] \"{}\" {} {} \"{}\" \"{}\"",
@@ -97,14 +96,14 @@ void nginx_access_logger(const Request &req, const Response &res) {
9796
// YYYY/MM/DD HH:MM:SS [level] message, client: client_ip, request: "request",
9897
// host: "host"
9998
void nginx_error_logger(const Error &err, const Request *req) {
100-
std::string time_local = get_error_time_format();
99+
auto time_local = get_error_time_format();
101100
std::string level = "error";
102101

103102
if (req) {
104-
std::string client_ip = get_client_ip(*req);
105-
std::string request =
103+
auto client_ip = get_client_ip(*req);
104+
auto request =
106105
std::format("{} {} {}", req->method, req->path, req->version);
107-
std::string host = req->get_header_value("Host");
106+
auto host = req->get_header_value("Host");
108107
if (host.empty()) host = "-";
109108

110109
std::cerr << std::format("{} [{}] {}, client: {}, request: "
@@ -120,38 +119,113 @@ void nginx_error_logger(const Error &err, const Request *req) {
120119
}
121120

122121
void print_usage(const char *program_name) {
123-
std::cout << std::format("Usage: {} <hostname> <port> <mount_point> "
124-
"<document_root_directory>",
125-
program_name)
122+
std::cout << "Usage: " << program_name << " [OPTIONS]" << std::endl;
123+
std::cout << std::endl;
124+
std::cout << "Options:" << std::endl;
125+
std::cout << " --host <hostname> Server hostname (default: localhost)"
126126
<< std::endl;
127-
128-
std::cout << std::format("Example: {} localhost 8080 /var/www/html .",
129-
program_name)
127+
std::cout << " --port <port> Server port (default: 8080)"
128+
<< std::endl;
129+
std::cout << " --mount <mount:path> Mount point and document root"
130+
<< std::endl;
131+
std::cout << " Format: mount_point:document_root"
132+
<< std::endl;
133+
std::cout << " (default: /:./html)" << std::endl;
134+
std::cout << " --version Show version information"
130135
<< std::endl;
136+
std::cout << " --help Show this help message" << std::endl;
137+
std::cout << std::endl;
138+
std::cout << "Examples:" << std::endl;
139+
std::cout << " " << program_name
140+
<< " --host localhost --port 8080 --mount /:./html" << std::endl;
141+
std::cout << " " << program_name
142+
<< " --host 0.0.0.0 --port 3000 --mount /api:./api" << std::endl;
131143
}
132144

133-
int main(int argc, char *argv[]) {
134-
if (argc != 5) {
135-
print_usage(argv[0]);
136-
return 1;
145+
struct ServerConfig {
146+
std::string hostname = "localhost";
147+
int port = 8080;
148+
std::string mount_point = "/";
149+
std::string document_root = "./html";
150+
};
151+
152+
enum class ParseResult { SUCCESS, HELP_REQUESTED, VERSION_REQUESTED, ERROR };
153+
154+
ParseResult parse_command_line(int argc, char *argv[], ServerConfig &config) {
155+
for (int i = 1; i < argc; i++) {
156+
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
157+
print_usage(argv[0]);
158+
return ParseResult::HELP_REQUESTED;
159+
} else if (strcmp(argv[i], "--host") == 0) {
160+
if (i + 1 >= argc) {
161+
std::cerr << "Error: --host requires a hostname argument" << std::endl;
162+
print_usage(argv[0]);
163+
return ParseResult::ERROR;
164+
}
165+
config.hostname = argv[++i];
166+
} else if (strcmp(argv[i], "--port") == 0) {
167+
if (i + 1 >= argc) {
168+
std::cerr << "Error: --port requires a port number argument"
169+
<< std::endl;
170+
print_usage(argv[0]);
171+
return ParseResult::ERROR;
172+
}
173+
config.port = std::atoi(argv[++i]);
174+
if (config.port <= 0 || config.port > 65535) {
175+
std::cerr << "Error: Invalid port number. Must be between 1 and 65535"
176+
<< std::endl;
177+
return ParseResult::ERROR;
178+
}
179+
} else if (strcmp(argv[i], "--mount") == 0) {
180+
if (i + 1 >= argc) {
181+
std::cerr
182+
<< "Error: --mount requires mount_point:document_root argument"
183+
<< std::endl;
184+
print_usage(argv[0]);
185+
return ParseResult::ERROR;
186+
}
187+
std::string mount_arg = argv[++i];
188+
auto colon_pos = mount_arg.find(':');
189+
if (colon_pos == std::string::npos) {
190+
std::cerr << "Error: --mount argument must be in format "
191+
"mount_point:document_root"
192+
<< std::endl;
193+
print_usage(argv[0]);
194+
return ParseResult::ERROR;
195+
}
196+
config.mount_point = mount_arg.substr(0, colon_pos);
197+
config.document_root = mount_arg.substr(colon_pos + 1);
198+
199+
if (config.mount_point.empty() || config.document_root.empty()) {
200+
std::cerr
201+
<< "Error: Both mount_point and document_root must be non-empty"
202+
<< std::endl;
203+
return ParseResult::ERROR;
204+
}
205+
} else if (strcmp(argv[i], "--version") == 0) {
206+
std::cout << CPPHTTPLIB_VERSION << std::endl;
207+
return ParseResult::VERSION_REQUESTED;
208+
} else {
209+
std::cerr << "Error: Unknown option '" << argv[i] << "'" << std::endl;
210+
print_usage(argv[0]);
211+
return ParseResult::ERROR;
212+
}
137213
}
214+
return ParseResult::SUCCESS;
215+
}
138216

139-
std::string hostname = argv[1];
140-
auto port = std::atoi(argv[2]);
141-
std::string mount_point = argv[3];
142-
std::string document_root = argv[4];
143-
217+
bool setup_server(Server &svr, const ServerConfig &config) {
144218
svr.set_logger(nginx_access_logger);
145219
svr.set_error_logger(nginx_error_logger);
146220

147-
auto ret = svr.set_mount_point(mount_point, document_root);
221+
auto ret = svr.set_mount_point(config.mount_point, config.document_root);
148222
if (!ret) {
149223
std::cerr
150224
<< std::format(
151225
"Error: Cannot mount '{}' to '{}'. Directory may not exist.",
152-
mount_point, document_root)
226+
config.mount_point, config.document_root)
153227
<< std::endl;
154-
return 1;
228+
return false;
155229
}
156230

157231
svr.set_file_extension_and_mimetype_mapping("html", "text/html");
@@ -191,16 +265,31 @@ int main(int argc, char *argv[]) {
191265
signal(SIGINT, signal_handler);
192266
signal(SIGTERM, signal_handler);
193267

194-
std::cout << std::format("Serving HTTP on {}:{}", hostname, port)
195-
<< std::endl;
196-
std::cout << std::format("Mount point: {} -> {}", mount_point, document_root)
197-
<< std::endl;
198-
std::cout << std::format("Press Ctrl+C to shutdown gracefully...")
268+
return true;
269+
}
270+
271+
int main(int argc, char *argv[]) {
272+
ServerConfig config;
273+
274+
auto result = parse_command_line(argc, argv, config);
275+
switch (result) {
276+
case ParseResult::HELP_REQUESTED:
277+
case ParseResult::VERSION_REQUESTED: return 0;
278+
case ParseResult::ERROR: return 1;
279+
case ParseResult::SUCCESS: break;
280+
}
281+
282+
if (!setup_server(svr, config)) { return 1; }
283+
284+
std::cout << "Serving HTTP on " << config.hostname << ":" << config.port
199285
<< std::endl;
286+
std::cout << "Mount point: " << config.mount_point << " -> "
287+
<< config.document_root << std::endl;
288+
std::cout << "Press Ctrl+C to shutdown gracefully..." << std::endl;
200289

201-
ret = svr.listen(hostname, port);
290+
auto ret = svr.listen(config.hostname, config.port);
202291

203-
std::cout << std::format("Server has been shut down.") << std::endl;
292+
std::cout << "Server has been shut down." << std::endl;
204293

205294
return ret ? 0 : 1;
206295
}

0 commit comments

Comments
 (0)