Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
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-2011 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include <webview/server.h>
24 #include <webview/request_dispatcher.h>
25 #include <core/exception.h>
26 #include <core/exceptions/system.h>
27 #include <logging/logger.h>
28 
29 #include <sys/socket.h>
30 #include <cstdlib>
31 #include <cstdio>
32 #include <cerrno>
33 #include <microhttpd.h>
34 
35 namespace fawkes {
36 #if 0 /* just to make Emacs auto-indent happy */
37 }
38 #endif
39 
40 /** @class WebServer <webview/server.h>
41  * Encapsulation of the libmicrohttpd webserver.
42  * This class opens a port serving websites and calls the supplied dispatcher
43  * for requests.
44  * @author Tim Niemueller
45  */
46 
47 /** Constructor.
48  * @param port TCP port to listen on
49  * @param dispatcher dispatcher to call for requests
50  * @param logger optional logger, used to output possible run-time problems
51  */
52 WebServer::WebServer(unsigned short int port, WebRequestDispatcher *dispatcher,
53  fawkes::Logger *logger)
54 {
55  __port = port;
56  __dispatcher = dispatcher;
57  __logger = logger;
58 
59  __ssl_key_mem = NULL;
60  __ssl_cert_mem = NULL;
61 
62  __daemon = MHD_start_daemon(MHD_NO_FLAG,
63  __port,
64  NULL,
65  NULL,
66  WebRequestDispatcher::process_request_cb,
67  (void *)__dispatcher,
68  MHD_OPTION_END);
69 
70  if ( __daemon == NULL ) {
71  throw fawkes::Exception("Could not start microhttpd");
72  }
73 
74 }
75 
76 /** SSL constructor.
77  * @param port TCP port to listen on
78  * @param dispatcher dispatcher to call for requests
79  * @param key_pem_filepath path to PEM formatted file containing the key
80  * @param cert_pem_filepath path to PEM formatted file containing the certificate
81  * @param logger optional logger, used to output possible run-time problems
82  */
83 WebServer::WebServer(unsigned short int port, WebRequestDispatcher *dispatcher,
84  const char *key_pem_filepath, const char *cert_pem_filepath,
85  fawkes::Logger *logger)
86 {
87  __port = port;
88  __dispatcher = dispatcher;
89  __logger = logger;
90 
91  __ssl_key_mem = read_file(key_pem_filepath);
92  __ssl_cert_mem = read_file(cert_pem_filepath);
93 
94  __daemon = MHD_start_daemon(MHD_USE_SSL,
95  __port,
96  NULL,
97  NULL,
98  WebRequestDispatcher::process_request_cb,
99  (void *)__dispatcher,
100  MHD_OPTION_HTTPS_MEM_KEY, __ssl_key_mem,
101  MHD_OPTION_HTTPS_MEM_CERT, __ssl_cert_mem,
102  MHD_OPTION_END);
103 
104  if ( __daemon == NULL ) {
105  throw fawkes::Exception("Could not start microhttpd (SSL)");
106  }
107 
108 }
109 
110 
111 /** Destructor. */
112 WebServer::~WebServer()
113 {
114  MHD_stop_daemon(__daemon);
115  __daemon = NULL;
116  __dispatcher = NULL;
117 
118  if (__ssl_key_mem) free(__ssl_key_mem);
119  if (__ssl_cert_mem) free(__ssl_cert_mem);
120 }
121 
122 
123 /** Read file into memory.
124  * @param filename file path
125  * @return memory location of file content, free after done
126  */
127 char *
128 WebServer::read_file(const char *filename)
129 {
130  FILE *f = fopen(filename, "rb");
131  if (! f) {
132  throw CouldNotOpenFileException(filename, errno);
133  }
134 
135  long size = 0;
136  if ((fseek(f, 0, SEEK_END) != 0) || ((size = ftell(f)) == 1)) {
137  fclose(f);
138  throw Exception("Cannot determine file size of %s", filename);
139  }
140  fseek(f, 0, SEEK_SET);
141 
142  if ( size == 0 ) {
143  fclose(f);
144  throw Exception("File %s has zero length", filename);
145  } else if (size > 1024 * 1024) {
146  // keys or certs should not be that long...
147  fclose(f);
148  throw Exception("File %s is unexpectedly large", filename);
149  }
150 
151  char *rv = (char *)malloc(size);
152  if (fread(rv, size, 1, f) != 1) {
153  int terrno = errno;
154  fclose(f);
155  free(rv);
156  throw FileReadException(filename, terrno);
157  }
158 
159  fclose(f);
160 
161  return rv;
162 }
163 
164 
165 /** Setup basic authentication.
166  * @param realm authentication realm to display to the user
167  * @param verifier verifier to use for checking credentials
168  */
169 void
170 WebServer::setup_basic_auth(const char *realm, WebUserVerifier *verifier)
171 {
172  __dispatcher->setup_basic_auth(realm, verifier);
173 }
174 
175 
176 /** Process requests.
177  * This method waits for new requests and processes them when received.
178  */
179 void
180 WebServer::process()
181 {
182  fd_set read_fd, write_fd, except_fd;
183  int max_fd = 0;
184  FD_ZERO(&read_fd); FD_ZERO(&write_fd); FD_ZERO(&except_fd);
185  if ( MHD_get_fdset(__daemon, &read_fd, &write_fd, &except_fd, &max_fd) != MHD_YES ) {
186  if (__logger)
187  __logger->log_warn("WebviewThread", "Could not get microhttpd fdsets");
188  return;
189  }
190  select(max_fd + 1, &read_fd, &write_fd, &except_fd, NULL);
191  MHD_run(__daemon);
192 }
193 
194 } // end namespace fawkes