Fawkes API Fawkes Development Version
webview_thread.cpp
1
2/***************************************************************************
3 * webview_thread.cpp - Thread that handles web interface requests
4 *
5 * Created: Mon Oct 13 17:51:31 2008 (I5 Developer's Day)
6 * Copyright 2006-2018 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_thread.h"
23
24#include "rest_processor.h"
25#include "service_browse_handler.h"
26#include "static_processor.h"
27#include "user_verifier.h"
28
29#include <core/exceptions/system.h>
30#include <core/version.h>
31#include <sys/wait.h>
32#include <utils/misc/string_conversions.h>
33#include <utils/system/file.h>
34#include <utils/system/hostinfo.h>
35#include <webview/page_reply.h>
36#include <webview/request_dispatcher.h>
37#include <webview/server.h>
38#include <webview/url_manager.h>
39
40using namespace fawkes;
41
42/** @class WebviewThread "webview_thread.h"
43 * Webview Thread.
44 * This thread runs the HTTP server and handles requests via the
45 * WebRequestDispatcher.
46 * @author Tim Niemueller
47 */
48
49/** Constructor.
50 * @param enable_tp true to enable thread pool setting the thread to
51 * wait-for-wakeup mode, falso to run request processing in this
52 * thread.
53 */
55: Thread("WebviewThread", enable_tp ? Thread::OPMODE_WAITFORWAKEUP : Thread::OPMODE_CONTINUOUS)
56{
57 cfg_use_thread_pool_ = enable_tp;
58
59 if (!enable_tp)
61}
62
63WebviewThread::~WebviewThread()
64{
65}
66
67void
69{
70 cfg_port_ = config->get_uint("/webview/port");
71
72 WebReply::set_caching_default(config->get_bool("/webview/client_side_caching"));
73
74 webview_service_ = NULL;
75 service_browse_handler_ = NULL;
76 dispatcher_ = NULL;
77
78 cfg_use_tls_ = false;
79 try {
80 cfg_use_tls_ = config->get_bool("/webview/tls/enable");
81 } catch (Exception &e) {
82 }
83
84 cfg_use_ipv4_ = config->get_bool("/network/ipv4/enable");
85 cfg_use_ipv6_ = config->get_bool("/network/ipv6/enable");
86
87 if (cfg_use_tls_) {
88 cfg_tls_create_ = false;
89 try {
90 cfg_tls_create_ = config->get_bool("/webview/tls/create");
91 } catch (Exception &e) {
92 }
93
94 cfg_tls_key_ = config->get_string("/webview/tls/key-file");
95 cfg_tls_cert_ = config->get_string("/webview/tls/cert-file");
96
97 try {
98 cfg_tls_cipher_suite_ = config->get_string("/webview/tls/cipher-suite");
99 logger->log_debug(name(), "Using cipher suite %s", cfg_tls_cipher_suite_.c_str());
100 } catch (Exception &e) {
101 }
102
103 if (cfg_tls_key_[0] != '/')
104 cfg_tls_key_ = std::string(CONFDIR "/") + cfg_tls_key_;
105
106 if (cfg_tls_cert_[0] != '/')
107 cfg_tls_cert_ = std::string(CONFDIR "/") + cfg_tls_cert_;
108
110 "Key file: %s Cert file: %s",
111 cfg_tls_key_.c_str(),
112 cfg_tls_cert_.c_str());
113
114 if (!File::exists(cfg_tls_key_.c_str())) {
115 if (File::exists(cfg_tls_cert_.c_str())) {
116 throw Exception("Key file %s does not exist, but certificate file %s "
117 "does",
118 cfg_tls_key_.c_str(),
119 cfg_tls_cert_.c_str());
120 } else if (cfg_tls_create_) {
121 tls_create(cfg_tls_key_.c_str(), cfg_tls_cert_.c_str());
122 } else {
123 throw Exception("Key file %s does not exist", cfg_tls_key_.c_str());
124 }
125 } else if (!File::exists(cfg_tls_cert_.c_str())) {
126 throw Exception("Certificate file %s does not exist, but key file %s "
127 "does",
128 cfg_tls_key_.c_str(),
129 cfg_tls_cert_.c_str());
130 }
131 }
132
133 if (cfg_use_thread_pool_) {
134 cfg_num_threads_ = config->get_uint("/webview/thread-pool/num-threads");
135 }
136
137 cfg_use_basic_auth_ = false;
138 try {
139 cfg_use_basic_auth_ = config->get_bool("/webview/use_basic_auth");
140 } catch (Exception &e) {
141 }
142 cfg_basic_auth_realm_ = "Fawkes Webview";
143 try {
144 cfg_basic_auth_realm_ = config->get_bool("/webview/basic_auth_realm");
145 } catch (Exception &e) {
146 }
147
148 cfg_access_log_ = "";
149 try {
150 cfg_access_log_ = config->get_string("/webview/access_log");
151 } catch (Exception &e) {
152 }
153
154 bool cfg_cors_allow_all = false;
155 try {
156 cfg_cors_allow_all = config->get_bool("/webview/cors/allow/all");
157 } catch (Exception &e) {
158 }
159 std::vector<std::string> cfg_cors_origins;
160 try {
161 cfg_cors_origins = config->get_strings("/webview/cors/allow/origins");
162 } catch (Exception &e) {
163 }
164 unsigned int cfg_cors_max_age = 0;
165 try {
166 cfg_cors_max_age = config->get_uint("/webview/cors/max-age");
167 } catch (Exception &e) {
168 }
169
170 webview_service_ =
171 new NetworkService(nnresolver, "Fawkes Webview on %h", "_http._tcp", cfg_port_);
172 webview_service_->add_txt("fawkesver=%u.%u.%u",
173 FAWKES_VERSION_MAJOR,
174 FAWKES_VERSION_MINOR,
175 FAWKES_VERSION_MICRO);
176 service_browse_handler_ = new WebviewServiceBrowseHandler(logger, webview_service_);
177
178 dispatcher_ = new WebRequestDispatcher(webview_url_manager);
179
180 try {
181 webserver_ = new WebServer(cfg_port_, dispatcher_, logger);
182
183 (*webserver_)
184 .setup_ipv(cfg_use_ipv4_, cfg_use_ipv6_)
185 .setup_cors(cfg_cors_allow_all, std::move(cfg_cors_origins), cfg_cors_max_age);
186
187 if (cfg_use_tls_) {
188 webserver_->setup_tls(cfg_tls_key_.c_str(),
189 cfg_tls_cert_.c_str(),
190 cfg_tls_cipher_suite_.empty() ? NULL : cfg_tls_cipher_suite_.c_str());
191 }
192
193 if (cfg_use_thread_pool_) {
194 webserver_->setup_thread_pool(cfg_num_threads_);
195 }
196
197 if (cfg_use_basic_auth_) {
198 user_verifier_ = new WebviewUserVerifier(config, logger);
199 webserver_->setup_basic_auth(cfg_basic_auth_realm_.c_str(), user_verifier_);
200 }
202
203 if (cfg_access_log_ != "") {
204 logger->log_debug(name(), "Setting up access log %s", cfg_access_log_.c_str());
205 webserver_->setup_access_log(cfg_access_log_.c_str());
206 }
207 } catch (Exception &e) {
208 delete webview_service_;
209 delete service_browse_handler_;
210 delete dispatcher_;
211 throw;
212 }
213 // get all directories for the static processor
214 std::vector<std::string> static_dirs = config->get_strings("/webview/htdocs/dirs");
215 std::string catchall_file;
216 try {
217 catchall_file = config->get_string("/webview/htdocs/catchall-file");
218 } catch (Exception &e) {
219 };
220 std::string mime_file = config->get_string("/webview/htdocs/mime-file");
221 static_dirs = StringConversions::resolve_paths(static_dirs);
222 std::string static_base_url = catchall_file.empty() ? "/static/" : "/";
223 static_processor_ = new WebviewStaticRequestProcessor(
224 webview_url_manager, static_base_url, static_dirs, catchall_file, mime_file, logger);
225 rest_processor_ =
227
228 try {
229 cfg_explicit_404_ = config->get_strings("/webview/explicit-404");
230 for (const auto &u : cfg_explicit_404_) {
231 webview_url_manager->add_handler(WebRequest::METHOD_GET,
232 u,
233 std::bind(&WebviewThread::produce_404, this),
234 10000);
235 }
236 } catch (Exception &e) {
237 } // ignored, no explicit 404
238
239 std::string afs;
240 if (cfg_use_ipv4_ && cfg_use_ipv6_) {
241 afs = "IPv4,IPv6";
242 } else if (cfg_use_ipv4_) {
243 afs = "IPv4";
244 } else if (cfg_use_ipv6_) {
245 afs = "IPv6";
246 }
247 webserver_->start();
248 logger->log_info("WebviewThread",
249 "Listening for HTTP%s connections on port %u (%s)",
250 cfg_use_tls_ ? "S" : "",
251 cfg_port_,
252 afs.c_str());
253
254 service_publisher->publish_service(webview_service_);
255 service_browser->watch_service("_http._tcp", service_browse_handler_);
256}
257
258void
260{
261 try {
262 service_publisher->unpublish_service(webview_service_);
263 } catch (Exception &e) {
264 } // ignored, can happen if avahi-daemon not running
265 try {
266 service_browser->unwatch_service("_http._tcp", service_browse_handler_);
267 } catch (Exception &e) {
268 } // ignored, can happen if avahi-daemon not running
269
270 for (const auto &u : cfg_explicit_404_) {
271 webview_url_manager->remove_handler(WebRequest::METHOD_GET, u);
272 }
273
274 delete webserver_;
275
276 delete webview_service_;
277 delete service_browse_handler_;
278
279 delete dispatcher_;
280 delete static_processor_;
281 delete rest_processor_;
282 dispatcher_ = NULL;
283}
284
285void
287{
288 if (!cfg_use_thread_pool_)
289 webserver_->process();
290}
291
292void
293WebviewThread::tls_create(const char *tls_key_file, const char *tls_cert_file)
294{
296 "Creating TLS key and certificate. "
297 "This may take a while...");
298 HostInfo h;
299
300 char *cmd;
301 if (asprintf(&cmd,
302 "openssl req -new -x509 -batch -nodes -days 365 "
303 "-subj \"/C=XX/L=World/O=Fawkes/CN=%s.local\" "
304 "-out \"%s\" -keyout \"%s\" >/dev/null 2>&1",
305 h.short_name(),
306 tls_cert_file,
307 tls_key_file)
308 == -1) {
309 throw OutOfMemoryException("Webview/TLS: Could not generate OpenSSL string");
310 }
311
312 int status = system(cmd);
313 free(cmd);
314
315 if (WEXITSTATUS(status) != 0) {
316 throw Exception("Failed to auto-generate key/certificate pair");
317 }
318}
319
320WebReply *
321WebviewThread::produce_404()
322{
323 return new StaticWebReply(WebReply::HTTP_NOT_FOUND, "Not found\n");
324}
REST API web processor.
Browse handler to detect other Webview instances on the network.
Static file web processor.
virtual void finalize()
Finalize the thread.
virtual void init()
Initialize the thread.
WebviewThread(bool enable_tp)
Constructor.
virtual void loop()
Code to execute in the thread.
Webview user verification.
Definition: user_verifier.h:34
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
virtual unsigned int get_uint(const char *path)=0
Get value from configuration which is of type unsigned int.
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Host information.
Definition: hostinfo.h:32
const char * short_name()
Get short hostname (up to first dot).
Definition: hostinfo.cpp:109
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
NetworkNameResolver * nnresolver
Network name resolver to lookup IP addresses of hostnames and vice versa.
Definition: network.h:45
ServicePublisher * service_publisher
Service publisher to publish services on the network.
Definition: network.h:46
ServiceBrowser * service_browser
Service browser to browse services on the network.
Definition: network.h:47
Representation of a service announced or found via service discovery (i.e.
Definition: service.h:38
void add_txt(const char *format,...)
Add a TXT record.
Definition: service.cpp:313
System ran out of memory and desired operation could not be fulfilled.
Definition: system.h:32
virtual void watch_service(const char *service_type, ServiceBrowseHandler *h)=0
Add browse handler for specific service.
virtual void unwatch_service(const char *service_type, ServiceBrowseHandler *h)=0
Remove browse handler for specific service.
virtual void unpublish_service(NetworkService *service)=0
Revoke service publication.
virtual void publish_service(NetworkService *service)=0
Publish service.
Static web reply.
Definition: reply.h:136
Thread class encapsulation of pthreads.
Definition: thread.h:46
void set_prepfin_conc_loop(bool concurrent=true)
Set concurrent execution of prepare_finalize() and loop().
Definition: thread.cpp:716
const char * name() const
Get name of thread.
Definition: thread.h:100
Basic web reply.
Definition: reply.h:34
Web request dispatcher.
Encapsulation of the libmicrohttpd webserver.
Definition: server.h:44
WebServer & setup_access_log(const char *filename)
Setup access log.
Definition: server.cpp:276
void process()
Process requests.
Definition: server.cpp:322
WebServer & setup_tls(const char *key_pem_filepath, const char *cert_pem_filepath, const char *cipher_suite=WEBVIEW_DEFAULT_CIPHERS)
Setup Transport Layer Security (encryption),.
Definition: server.cpp:76
WebServer & setup_thread_pool(unsigned int num_threads)
Setup thread pool.
Definition: server.cpp:130
WebServer & setup_basic_auth(const char *realm, WebUserVerifier *verifier)
Setup basic authentication.
Definition: server.cpp:265
void start()
Start daemon and enable processing requests.
Definition: server.cpp:140
WebServer & setup_request_manager(WebRequestManager *request_manager)
Setup this server as request manager.
Definition: server.cpp:288
void remove_handler(WebRequest::Method method, const std::string &path)
Remove a request processor.
Definition: url_manager.cpp:84
void add_handler(WebRequest::Method method, const std::string &path, Handler handler)
Add a request processor.
Definition: url_manager.cpp:54
WebRequestManager * webview_request_manager
Webview request manager.
Definition: webview.h:53
WebviewRestApiManager * webview_rest_api_manager
Webview REST API manager.
Definition: webview.h:55
WebUrlManager * webview_url_manager
Webview request processor manager.
Definition: webview.h:49
Fawkes library namespace.