libutil++  1.9.3
 All Classes Functions Variables
httpd.cpp
1 /*
2 ** libutil++
3 ** $Id: httpd.cpp 1664 2016-04-04 03:57:40Z sella $
4 ** Copyright (c) 2011-2016 Digital Genesis, LLC. All Rights Reserved.
5 ** Released under the LGPL Version 2.1 License.
6 ** http://www.digitalgenesis.com
7 */
8 
9 static const char rcsid[] __attribute__((used)) = "$Id: httpd.cpp 1664 2016-04-04 03:57:40Z sella $";
10 
11 #include "test.h"
12 
13 #include <stdio.h>
14 #include <signal.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <pthread.h>
18 
19 #include <mutex>
20 #include <condition_variable>
21 
22 #include "alias.h"
23 #include "version.h"
24 #include "sella/util/Log.h"
25 #include "sella/util/String.h"
26 #include "sella/server/HTTP.h"
27 #include "sella/server/callbacks.h"
28 #include "sella/util/CommandLine.h"
29 #include "sella/net/IPAddressPort.h"
30 
31 namespace sella {
32  namespace httpd {
33  bool running = true, restart = false;
34  size_t workers = sella::server::HTTP::MinimumWorkersDefault;
35  std::string listeners = "0.0.0.0:8080, [::]:8080"; // {IPAddressPort("0.0.0.0:8080"), IPAddressPort("[::]:8080") }
36  std::string secureListeners = "0.0.0.0:8443, [::]:8443"; // {IPAddressPort("0.0.0.0:8443"), IPAddressPort("[::]:8443") }
37  std::string root = sella::server::HTTP::RootDefault();
38  std::mutex mutex;
39  std::condition_variable cv;
40  enum Mode { NonBlock, Block, Async } mode = Block;
41  int debug = 0;
42  }
43 }
44 
45 using namespace sella::httpd;
46 using namespace sella::util;
47 using namespace sella::net;
48 using namespace sella::server;
49 
50 bool init_config(int argc, char **argv);
51 bool init_signals(void);
52 void sighandler(int sig);
53 
54 bool acl_callback(HTTPConnection::shared connection) {
55  return true;
56 }
57 
58 bool authentication_callback(HTTPConnection::shared connection) {
59  return true;
60 }
61 
62 bool request_callback(HTTPConnection::shared connection) {
63  const auto &r = connection->getRequest();
64 
65  INFO("%s: %s - %s {%s}", r.getMethodRaw().c_str(), r.getHost().c_str(), r.getURI().c_str(), r.getUserAgent().c_str());
66 
67  return http::request_callback(connection); /* Passing to builtin as an example. */
68 }
69 
70 bool error_callback(HTTPConnection::shared connection, int code, const std::string &detail = std::string()) {
71  return http::error_callback(connection, code, detail); /* Passing to builtin as an example. */
72 }
73 
74 int main(int argc, char **argv) {
75  SET_FORMAT(SimpleLogFormat());
76  SET_IDENTIFIER("httpd");
77  SET_OPTION(Log::WithStderrOption);
78 
79  init_config(argc, argv);
80  init_signals();
81 
82  INFO("Using %s v%s", libutilxx__package(), libutilxx__version());
83 
84  HTTP http;
85 
86  http.setListeners(listeners);
87  http.setSecureListeners(secureListeners);
88  http.setRoot(root);
89  http.setCustomHeaders({ "X-Powered-By: unicorn tears", "Age: 0" });
90  http.setAllows({ HTTPRequest::Options, HTTPRequest::Get, HTTPRequest::Head, HTTPRequest::Trace });
91 
92  http.setCertificateFile("../contrib/cert.pem");
93  http.setPrivateKeyFile("../contrib/key.pem");
94  //http.setPasswordFile("../contrib/key.secret");
95 
96  /* Example of overriding builtin callbacks */
97  http.setACLCallback(acl_callback);
98  http.setAuthenticationCallback(authentication_callback);
99  http.setRequestCallback(request_callback);
100  http.setErrorCallback(error_callback);
101 
102  if (IS_TRACE("print")) {
103  http.print();
104  }
105 
106  try {
107  while (running) {
108  std::unique_lock<std::mutex> lock(mutex);
109 
110  INFO("Starting up on unsecure port(s): %s", http.getListenersString().c_str());
111  INFO("Starting up on secure port(s): %s", http.getSecureListenersString().c_str());
112 
113  if (mode == Block) {
114  http.run(workers);
115  } else if (mode == NonBlock) {
116  http.start(workers);
117 
118  INFO("%s - Startup complete", http.getBanner().c_str());
119 
120  while (running) {
121  cv.wait(lock);
122  }
123 
124  http.stop();
125  } else if (mode == Async) {
126  while (running) {
127  http.receive();
128 
129  INFO("Doing something else...");
130  usleep(500000);
131  }
132  } else {
133  WARNING("Guru Meditation #00000004.0000AAC0");
134 
135  running = false;
136  }
137 
138  if (restart) {
139  running = true;
140  restart = false;
141  }
142  }
143 
144  INFO("%s - Shutdown complete", http.getBanner().c_str());
145  } catch (ServerException &e) {
146  INFO(e.what());
147 
148  return EXIT_FAILURE;
149  }
150 
151  return EXIT_SUCCESS;
152 }
153 
154 bool init_config(int argc, char **argv) {
155  std::string m;
156  sella::util::CommandLine cmdln(argc, argv);
157 
158  cmdln.add('m', "mode", "use MODE block, nonblock or async", "MODE", "block");
159  cmdln.add('r', "root", "use DIR as root directory", "DIR", root);
160  cmdln.add('l', "listeners", "use LIST as addresses/ports to listen", "LIST", listeners);
161  cmdln.add('s', "secure-listeners", "use LIST as addresses/ports to secure listen", "LIST", secureListeners);
162  cmdln.add('w', "workers", "use INT workers", "INT", workers, 1, 256);
163  cmdln.add('d', "debug", "set debug level to INT", "INT", debug, 0, 9);
164  cmdln.add();
165  cmdln.add('h', "help", "display this help screen");
166  cmdln.add('v', "version", "display version information");
167 
168  std::map<std::string, std::string> conf = cmdln.parse(stdin);
169 
170  if (conf.find("help") != conf.end()) {
171  printf("%s v%s\n", libutilxx__package(), libutilxx__version());
172  cmdln.usage(stdout, EXIT_SUCCESS);
173  } else if (conf.find("version") != conf.end()) {
174  printf("%s v%s\n", libutilxx__package(), libutilxx__version());
175 
176  exit(EXIT_SUCCESS);
177  }
178 
179  if (conf.find("mode") != conf.end()) {
180  if (conf["mode"] == "block") {
181  mode = Block;
182  } else if (conf["mode"] == "nonblock") {
183  mode = NonBlock;
184  } else if (conf["mode"] == "async") {
185  mode = Async;
186  }
187  }
188 
189  if (conf.find("listeners") != conf.end()) {
190  listeners = conf["listeners"];
191  }
192 
193  if (conf.find("root") != conf.end()) {
194  root = conf["root"];
195  }
196 
197  if (conf.find("workers") != conf.end()) {
198  workers = atoi(conf["workers"].c_str());
199  }
200 
201  if (conf.find("debug") != conf.end()) {
202  debug = atoi(conf["debug"].c_str());
203 
204  switch (debug) {
205  case 9:
206  SET_TRACE("all");
207  SET_LOGMASK(LOG_UPTO(LOG_DEBUG));
208 
209  case 3:
210  SET_TRACE(PACKAGE "-detail");
211 
212  case 2:
213  SET_TRACE(PACKAGE);
214 
215  case 1:
216  SET_TRACE("print");
217  }
218  }
219 
220  return true;
221 }
222 
223 bool init_signals(void) {
224  struct sigaction act, old;
225 
226  act.sa_handler = sighandler;
227  sigemptyset(&act.sa_mask);
228  act.sa_flags = SA_RESTART;
229 
230  if (sigaction(SIGINT, NULL, &old) < 0) {
231  fprintf(stderr, "Failed to retrieve signal handler for SIGINT.\n");
232  } else if (old.sa_handler == SIG_DFL) {
233  if (sigaction(SIGINT, &act, NULL) < 0) {
234  fprintf(stderr, "Failed to install signal handler for SIGINT.\n");
235  }
236  }
237 
238  if (sigaction(SIGTERM, NULL, &old) < 0) {
239  fprintf(stderr, "Failed to retrieve signal handler for SIGTERM.\n");
240  } else if (old.sa_handler == SIG_DFL) {
241  if (sigaction(SIGTERM, &act, NULL) < 0) {
242  fprintf(stderr, "Failed to install signal handler for SIGTERM.\n");
243  }
244  }
245 
246  if (sigaction(SIGHUP, NULL, &old) < 0) {
247  fprintf(stderr, "Failed to retrieve signal handler for SIGHUP.\n");
248  } else if (old.sa_handler == SIG_DFL) {
249  if (sigaction(SIGHUP, &act, NULL) < 0) {
250  fprintf(stderr, "Failed to install signal handler for SIGHUP.\n");
251  }
252  }
253 
254  if (sigaction(SIGUSR1, NULL, &old) < 0) {
255  fprintf(stderr, "Failed to retrieve signal handler for SIGUSR1.\n");
256  } else if (old.sa_handler == SIG_DFL) {
257  if (sigaction(SIGUSR1, &act, NULL) < 0) {
258  fprintf(stderr, "Failed to install signal handler for SIGUSR1.\n");
259  }
260  }
261 
262  if (sigaction(SIGUSR2, NULL, &old) < 0) {
263  fprintf(stderr, "Failed to retrieve signal handler for SIGUSR2.\n");
264  } else if (old.sa_handler == SIG_DFL) {
265  if (sigaction(SIGUSR2, &act, NULL) < 0) {
266  fprintf(stderr, "Failed to install signal handler for SIGUSR2.\n");
267  }
268  }
269 
270  if (sigaction(SIGPIPE, NULL, &old) < 0) { /* Prevents send() and write() on broken socket from terminating. */
271  fprintf(stderr, "Failed to retrieve signal handler for SIGPIPE.\n");
272  } else if (old.sa_handler == SIG_DFL) {
273  if (sigaction(SIGPIPE, &act, NULL) < 0) {
274  fprintf(stderr, "Failed to install signal handler for SIGPIPE.\n");
275  }
276  }
277 
278  return true;
279 }
280 
281 void sighandler(int sig) { /* Do not call syslog within handler. Will cause hangs/crash. */
282  switch (sig) {
283  case SIGINT:
284  case SIGTERM:
285  restart = false;
286  running = false;
287  cv.notify_one();
288 
289  break;
290 
291  case SIGHUP:
292  restart = true;
293  running = false;
294 
295  break;
296 
297  case SIGUSR1:
298  break;
299 
300  case SIGUSR2:
301  break;
302 
303  case SIGPIPE:
304  break;
305 
306  default:
307  break;
308  }
309 }
310 
311 /*
312 ** vim: noet ts=3 sw=3
313 */