Fawkes API  Fawkes Development Version
server.cpp
1 
2 /***************************************************************************
3  * server.cpp - Web server encapsulation around libmicrohttpd
4  *
5  * Created: Sun Aug 30 17:40:54 2009
6  * Copyright 2006-2014 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include <webview/server.h>
23 #include <webview/request_dispatcher.h>
24 #include <webview/request.h>
25 #include <webview/request_manager.h>
26 #include <webview/access_log.h>
27 #include <core/threading/thread.h>
28 #include <core/exception.h>
29 #include <core/exceptions/system.h>
30 #include <logging/logger.h>
31 
32 #include <sys/socket.h>
33 #include <cstdlib>
34 #include <cstdio>
35 #include <cerrno>
36 #include <microhttpd.h>
37 
38 namespace fawkes {
39 #if 0 /* just to make Emacs auto-indent happy */
40 }
41 #endif
42 
43 
44 /** @class WebServer <webview/server.h>
45  * Encapsulation of the libmicrohttpd webserver.
46  * This class opens a port serving websites and calls the supplied dispatcher
47  * for requests.
48  * @author Tim Niemueller
49  */
50 
51 /** Constructor.
52  * @param port TCP port to listen on
53  * @param dispatcher dispatcher to call for requests
54  * @param logger optional logger, used to output possible run-time problems
55  * @param enable_ipv4 enable IPv4 support
56  * @param enable_ipv6 enable IPv6 support
57  */
58 WebServer::WebServer(unsigned short int port, WebRequestDispatcher *dispatcher,
59  fawkes::Logger *logger,
60  bool enable_ipv4, bool enable_ipv6)
61 {
62  __port = port;
63  __dispatcher = dispatcher;
64  __logger = logger;
65  __request_manager = NULL;
66 
67  __ssl_key_mem = NULL;
68  __ssl_cert_mem = NULL;
69 
70  unsigned int flags = MHD_NO_FLAG;
71 #if MHD_VERSION >= 0x00090280
72  if (enable_ipv4 && enable_ipv6) {
73  flags |= MHD_USE_DUAL_STACK;
74  } else if (enable_ipv6) {
75  flags |= MHD_USE_IPv6;
76  } else if (! enable_ipv4 && ! enable_ipv6) {
77  throw fawkes::Exception("WebServer: neither IPv4 nor IPv6 enabled");
78  }
79 #endif
80 
81  __daemon = MHD_start_daemon(flags,
82  __port,
83  NULL,
84  NULL,
86  (void *)__dispatcher,
87  MHD_OPTION_NOTIFY_COMPLETED,
88  WebRequestDispatcher::request_completed_cb, (void *)__dispatcher,
89  MHD_OPTION_URI_LOG_CALLBACK,
90  WebRequestDispatcher::uri_log_cb, (void *)__dispatcher,
91  MHD_OPTION_END);
92 
93  if ( __daemon == NULL ) {
94  throw fawkes::Exception("Could not start microhttpd");
95  }
96 
97 }
98 
99 /** SSL constructor.
100  * @param port TCP port to listen on
101  * @param dispatcher dispatcher to call for requests
102  * @param key_pem_filepath path to PEM formatted file containing the key
103  * @param cert_pem_filepath path to PEM formatted file containing the certificate
104  * @param cipher_suite which cipers to use for SSL/TLS connections
105  * @param logger optional logger, used to output possible run-time problems
106  * @param enable_ipv4 enable IPv4 support
107  * @param enable_ipv6 enable IPv6 support
108  */
109 WebServer::WebServer(unsigned short int port, WebRequestDispatcher *dispatcher,
110  const char *key_pem_filepath, const char *cert_pem_filepath,
111  const char *cipher_suite, fawkes::Logger *logger,
112  bool enable_ipv4, bool enable_ipv6)
113 {
114  __port = port;
115  __dispatcher = dispatcher;
116  __logger = logger;
117  __request_manager = NULL;
118 
119  __ssl_key_mem = read_file(key_pem_filepath);
120  __ssl_cert_mem = read_file(cert_pem_filepath);
121  if (cipher_suite == NULL) {
122  cipher_suite = WEBVIEW_DEFAULT_CIPHERS;
123  }
124 
125  unsigned int flags = MHD_USE_SSL;
126 #if MHD_VERSION >= 0x00090280
127  if (enable_ipv4 && enable_ipv6) {
128  flags |= MHD_USE_DUAL_STACK;
129  } else if (enable_ipv6) {
130  flags |= MHD_USE_IPv6;
131  } else if (! enable_ipv4 && ! enable_ipv6) {
132  throw fawkes::Exception("WebServer: neither IPv4 nor IPv6 enabled");
133  }
134 #endif
135 
136  __daemon = MHD_start_daemon(flags,
137  __port,
138  NULL,
139  NULL,
141  (void *)__dispatcher,
142  MHD_OPTION_NOTIFY_COMPLETED,
143  WebRequestDispatcher::request_completed_cb, (void *)__dispatcher,
144  MHD_OPTION_URI_LOG_CALLBACK,
145  WebRequestDispatcher::uri_log_cb, (void *)__dispatcher,
146  MHD_OPTION_HTTPS_MEM_KEY, __ssl_key_mem,
147  MHD_OPTION_HTTPS_MEM_CERT, __ssl_cert_mem,
148  MHD_OPTION_HTTPS_PRIORITIES, cipher_suite,
149  MHD_OPTION_END);
150 
151  if ( __daemon == NULL ) {
152  throw fawkes::Exception("Could not start microhttpd (SSL)");
153  }
154 
155 }
156 
157 
158 /** Destructor. */
160 {
161  if (__request_manager) {
162  __request_manager->set_server(NULL);
163  }
164 
165  MHD_stop_daemon(__daemon);
166  __daemon = NULL;
167  __dispatcher = NULL;
168 
169  if (__ssl_key_mem) free(__ssl_key_mem);
170  if (__ssl_cert_mem) free(__ssl_cert_mem);
171 }
172 
173 
174 /** Read file into memory.
175  * @param filename file path
176  * @return memory location of file content, free after done
177  */
178 char *
179 WebServer::read_file(const char *filename)
180 {
181  FILE *f = fopen(filename, "rb");
182  if (! f) {
183  throw CouldNotOpenFileException(filename, errno);
184  }
185 
186  long size = 0;
187  if ((fseek(f, 0, SEEK_END) != 0) || ((size = ftell(f)) == 1)) {
188  fclose(f);
189  throw Exception("Cannot determine file size of %s", filename);
190  }
191  fseek(f, 0, SEEK_SET);
192 
193  if ( size == 0 ) {
194  fclose(f);
195  throw Exception("File %s has zero length", filename);
196  } else if (size > 1024 * 1024) {
197  // keys or certs should not be that long...
198  fclose(f);
199  throw Exception("File %s is unexpectedly large", filename);
200  }
201 
202  char *rv = (char *)malloc(size);
203  if (fread(rv, size, 1, f) != 1) {
204  int terrno = errno;
205  fclose(f);
206  free(rv);
207  throw FileReadException(filename, terrno);
208  }
209 
210  fclose(f);
211 
212  return rv;
213 }
214 
215 
216 /** Setup basic authentication.
217  * @param realm authentication realm to display to the user
218  * @param verifier verifier to use for checking credentials
219  */
220 void
221 WebServer::setup_basic_auth(const char *realm, WebUserVerifier *verifier)
222 {
223  __dispatcher->setup_basic_auth(realm, verifier);
224 }
225 
226 
227 /** Setup access log.
228  * @param filename access log file name
229  */
230 void
231 WebServer::setup_access_log(const char *filename)
232 {
233  __dispatcher->setup_access_log(filename);
234 }
235 
236 
237 /** Setup this server as request manager.
238  * The registration will be cancelled automatically on destruction.
239  * @param request_manager request manager to register with
240  */
241 void
243 {
244  request_manager->set_server(this);
245  __request_manager = request_manager;
246 }
247 
248 /** Get number of active requests.
249  * @return number of ongoing requests.
250  */
251 unsigned int
253 {
254  return __dispatcher->active_requests();
255 }
256 
257 /** Get time when last request was completed.
258  * @return Time when last request was completed
259  */
260 Time
262 {
263  return __dispatcher->last_request_completion_time();
264 }
265 
266 
267 /** Process requests.
268  * This method waits for new requests and processes them when received.
269  */
270 void
272 {
273  fd_set read_fd, write_fd, except_fd;
274  int max_fd = 0;
275  FD_ZERO(&read_fd); FD_ZERO(&write_fd); FD_ZERO(&except_fd);
276  if ( MHD_get_fdset(__daemon, &read_fd, &write_fd, &except_fd, &max_fd) != MHD_YES ) {
277  if (__logger)
278  __logger->log_warn("WebviewThread", "Could not get microhttpd fdsets");
279  return;
280  }
281  select(max_fd + 1, &read_fd, &write_fd, &except_fd, NULL);
282  Thread::CancelState old_state;
284  MHD_run(__daemon);
285  Thread::set_cancel_state(old_state);
286 }
287 
288 } // end namespace fawkes
unsigned int active_requests() const
Get number of active requests.
Definition: server.cpp:252
void setup_basic_auth(const char *realm, WebUserVerifier *verifier)
Setup basic authentication.
Definition: server.cpp:221
Web request dispatcher.
File could not be opened.
Definition: system.h:53
File could not be read.
Definition: system.h:61
unsigned int active_requests() const
Get number of active requests.
WebServer(unsigned short int port, WebRequestDispatcher *dispatcher, fawkes::Logger *logger=0, bool enable_ipv4=true, bool enable_ipv6=true)
Constructor.
Definition: server.cpp:58
Fawkes library namespace.
void process()
Process requests.
Definition: server.cpp:271
static void request_completed_cb(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe)
Process request completion.
A class for handling time.
Definition: time.h:91
thread cannot be cancelled
Definition: thread.h:62
static int process_request_cb(void *callback_data, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **session_data)
Process request callback for libmicrohttpd.
static void set_cancel_state(CancelState new_state, CancelState *old_state=0)
Set the cancel state of the current thread.
Definition: thread.cpp:1350
void setup_basic_auth(const char *realm, WebUserVerifier *verifier)
Setup basic authentication.
Interface for user verification.
Definition: user_verifier.h:31
~WebServer()
Destructor.
Definition: server.cpp:159
Base class for exceptions in Fawkes.
Definition: exception.h:36
void setup_request_manager(WebRequestManager *request_manager)
Setup this server as request manager.
Definition: server.cpp:242
Probides information about ongoing requests.
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
Time last_request_completion_time() const
Get time when last request was completed.
Time last_request_completion_time() const
Get time when last request was completed.
Definition: server.cpp:261
static void * uri_log_cb(void *cls, const char *uri)
Callback for new requests.
void setup_access_log(const char *filename)
Setup access log.
Definition: server.cpp:231
void setup_access_log(const char *filename)
Setup access log.
CancelState
Cancel state.
Definition: thread.h:60
Interface for logging.
Definition: logger.h:34