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 
40 using 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  */
54 WebviewThread::WebviewThread(bool enable_tp)
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 
63 WebviewThread::~WebviewThread()
64 {
65 }
66 
67 void
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 
109  logger->log_debug(name(),
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 
258 void
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 
285 void
287 {
288  if (!cfg_use_thread_pool_)
289  webserver_->process();
290 }
291 
292 void
293 WebviewThread::tls_create(const char *tls_key_file, const char *tls_cert_file)
294 {
295  logger->log_info(name(),
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 
320 WebReply *
321 WebviewThread::produce_404()
322 {
323  return new StaticWebReply(WebReply::HTTP_NOT_FOUND, "Not found\n");
324 }
fawkes::WebUrlManager::add_handler
void add_handler(WebRequest::Method method, const std::string &path, Handler handler)
Add a request processor.
Definition: url_manager.cpp:58
fawkes::Thread::set_prepfin_conc_loop
void set_prepfin_conc_loop(bool concurrent=true)
Set concurrent execution of prepare_finalize() and loop().
Definition: thread.cpp:720
fawkes::Configuration::get_bool
virtual bool get_bool(const char *path)=0
fawkes::Logger::log_info
virtual void log_info(const char *component, const char *format,...)=0
WebviewThread::finalize
virtual void finalize()
Finalize the thread.
Definition: webview_thread.cpp:258
fawkes::WebUrlManager::remove_handler
void remove_handler(WebRequest::Method method, const std::string &path)
Remove a request processor.
Definition: url_manager.cpp:88
WebviewUserVerifier
Definition: user_verifier.h:32
fawkes::WebServer
Definition: server.h:42
fawkes::Thread::name
const char * name() const
Definition: thread.h:99
fawkes::WebRequestDispatcher
Definition: request_dispatcher.h:49
fawkes::ServiceBrowser::watch_service
virtual void watch_service(const char *service_type, ServiceBrowseHandler *h)=0
fawkes::NetworkAspect::nnresolver
NetworkNameResolver * nnresolver
Definition: network.h:54
fawkes::HostInfo::short_name
const char * short_name()
Get short hostname (up to first dot).
Definition: hostinfo.cpp:113
WebviewRESTRequestProcessor
Definition: rest_processor.h:36
fawkes::LoggingAspect::logger
Logger * logger
Definition: logging.h:50
fawkes::NetworkService
Definition: service.h:41
fawkes::ServicePublisher::unpublish_service
virtual void unpublish_service(NetworkService *service)=0
fawkes
fawkes::WebviewAspect::webview_rest_api_manager
WebviewRestApiManager * webview_rest_api_manager
Webview REST API manager.
Definition: webview.h:59
fawkes::ServiceBrowser::unwatch_service
virtual void unwatch_service(const char *service_type, ServiceBrowseHandler *h)=0
fawkes::WebServer::setup_tls
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:78
WebviewThread::loop
virtual void loop()
Code to execute in the thread.
Definition: webview_thread.cpp:285
fawkes::Configuration::get_strings
virtual std::vector< std::string > get_strings(const char *path)=0
fawkes::ConfigurableAspect::config
Configuration * config
Definition: configurable.h:50
WebviewThread::init
virtual void init()
Initialize the thread.
Definition: webview_thread.cpp:67
fawkes::WebviewAspect::webview_request_manager
WebRequestManager * webview_request_manager
Webview request manager.
Definition: webview.h:57
fawkes::HostInfo
Definition: hostinfo.h:30
fawkes::WebServer::setup_basic_auth
WebServer & setup_basic_auth(const char *realm, WebUserVerifier *verifier)
Setup basic authentication.
Definition: server.cpp:267
fawkes::Thread
Definition: thread.h:44
fawkes::WebviewAspect::webview_url_manager
WebUrlManager * webview_url_manager
Webview request processor manager.
Definition: webview.h:53
fawkes::WebServer::setup_access_log
WebServer & setup_access_log(const char *filename)
Setup access log.
Definition: server.cpp:278
fawkes::ServicePublisher::publish_service
virtual void publish_service(NetworkService *service)=0
fawkes::Configuration::get_uint
virtual unsigned int get_uint(const char *path)=0
WebviewStaticRequestProcessor
Definition: static_processor.h:36
fawkes::Configuration::get_string
virtual std::string get_string(const char *path)=0
fawkes::StaticWebReply
Definition: reply.h:139
fawkes::WebServer::setup_request_manager
WebServer & setup_request_manager(WebRequestManager *request_manager)
Setup this server as request manager.
Definition: server.cpp:290
fawkes::OutOfMemoryException
Definition: system.h:35
fawkes::WebServer::setup_thread_pool
WebServer & setup_thread_pool(unsigned int num_threads)
Setup thread pool.
Definition: server.cpp:132
fawkes::WebServer::start
void start()
Start daemon and enable processing requests.
Definition: server.cpp:142
fawkes::WebServer::process
void process()
Process requests.
Definition: server.cpp:324
fawkes::NetworkAspect::service_publisher
ServicePublisher * service_publisher
Definition: network.h:55
fawkes::Logger::log_debug
virtual void log_debug(const char *component, const char *format,...)=0
WebviewThread::WebviewThread
WebviewThread(bool enable_tp)
Constructor.
Definition: webview_thread.cpp:53
fawkes::WebReply
Definition: reply.h:37
fawkes::NetworkAspect::service_browser
ServiceBrowser * service_browser
Definition: network.h:56
WebviewServiceBrowseHandler
Definition: service_browse_handler.h:35
fawkes::Exception
Definition: exception.h:39