18
18
19
19
using namespace httplib ;
20
20
21
- auto SERVER_NAME = std::format(" cpp-httplib-server/{}" , CPPHTTPLIB_VERSION);
21
+ const auto SERVER_NAME =
22
+ std::format (" cpp-httplib-server/{}" , CPPHTTPLIB_VERSION);
22
23
23
24
Server svr;
24
25
25
26
void signal_handler (int signal) {
26
27
if (signal == SIGINT || signal == SIGTERM) {
27
- std::cout << std::format (" \n Received signal, shutting down gracefully..." )
28
- << std::endl;
28
+ std::cout << " \n Received signal, shutting down gracefully...\n " ;
29
29
svr.stop ();
30
30
}
31
31
}
@@ -73,17 +73,16 @@ std::string get_client_ip(const Request &req) {
73
73
// $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent
74
74
// "$http_referer" "$http_user_agent"
75
75
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);
77
77
std::string remote_user =
78
78
" -" ; // 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" );
85
84
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" );
87
86
if (http_user_agent.empty ()) http_user_agent = " -" ;
88
87
89
88
std::cout << std::format (" {} - {} [{}] \" {}\" {} {} \" {}\" \" {}\" " ,
@@ -97,14 +96,14 @@ void nginx_access_logger(const Request &req, const Response &res) {
97
96
// YYYY/MM/DD HH:MM:SS [level] message, client: client_ip, request: "request",
98
97
// host: "host"
99
98
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 ();
101
100
std::string level = " error" ;
102
101
103
102
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 =
106
105
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" );
108
107
if (host.empty ()) host = " -" ;
109
108
110
109
std::cerr << std::format (" {} [{}] {}, client: {}, request: "
@@ -120,38 +119,113 @@ void nginx_error_logger(const Error &err, const Request *req) {
120
119
}
121
120
122
121
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)"
126
126
<< 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"
130
135
<< 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;
131
143
}
132
144
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
+ }
137
213
}
214
+ return ParseResult::SUCCESS;
215
+ }
138
216
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) {
144
218
svr.set_logger (nginx_access_logger);
145
219
svr.set_error_logger (nginx_error_logger);
146
220
147
- auto ret = svr.set_mount_point (mount_point, document_root);
221
+ auto ret = svr.set_mount_point (config. mount_point , config. document_root );
148
222
if (!ret) {
149
223
std::cerr
150
224
<< std::format (
151
225
" Error: Cannot mount '{}' to '{}'. Directory may not exist." ,
152
- mount_point, document_root)
226
+ config. mount_point , config. document_root )
153
227
<< std::endl;
154
- return 1 ;
228
+ return false ;
155
229
}
156
230
157
231
svr.set_file_extension_and_mimetype_mapping (" html" , " text/html" );
@@ -191,16 +265,31 @@ int main(int argc, char *argv[]) {
191
265
signal (SIGINT, signal_handler);
192
266
signal (SIGTERM, signal_handler);
193
267
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
199
285
<< 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;
200
289
201
- ret = svr.listen (hostname, port);
290
+ auto ret = svr.listen (config. hostname , config. port );
202
291
203
- std::cout << std::format ( " Server has been shut down." ) << std::endl;
292
+ std::cout << " Server has been shut down." << std::endl;
204
293
205
294
return ret ? 0 : 1 ;
206
295
}
0 commit comments