22#include "microhttpd_compat.h"
24#include <core/exception.h>
25#include <core/threading/mutex.h>
26#include <core/threading/mutex_locker.h>
27#include <sys/socket.h>
29#include <utils/misc/string_urlescape.h>
30#include <utils/time/time.h>
31#include <webview/access_log.h>
32#include <webview/error_reply.h>
33#include <webview/page_reply.h>
34#include <webview/request_dispatcher.h>
35#include <webview/url_manager.h>
36#include <webview/user_verifier.h>
42#define UNAUTHORIZED_REPLY \
44 " <head><title>Access denied</title></head>\n" \
46 " <h1>Access denied</h1>\n" \
47 " <p>Authentication is required to access Fawkes Webview</p>\n" \
72 url_manager_ = url_manager;
73 page_header_generator_ = headergen;
74 page_footer_generator_ = footergen;
76 active_requests_mutex_ =
new Mutex();
77 last_request_completion_time_ =
new Time();
79 cors_allow_all_ =
false;
88 delete active_requests_mutex_;
89 delete last_request_completion_time_;
102#if MHD_VERSION >= 0x00090400
106 user_verifier_ = NULL;
107 if (realm && verifier) {
108 realm_ = strdup(realm);
109 user_verifier_ = verifier;
112 throw Exception(
"libmicrohttpd >= 0.9.4 is required for basic authentication, "
113 "which was not available at compile time.");
135 std::vector<std::string> &&origins,
136 unsigned int max_age)
138 cors_allow_all_ = allow_all;
139 cors_origins_ = std::move(origins);
140 cors_max_age_ = max_age;
152 return rd->log_uri(uri);
168 struct MHD_Connection *connection,
171 const char * version,
172 const char * upload_data,
173 size_t * upload_data_size,
174 void ** session_data)
177 return rd->process_request(
178 connection, url, method, version, upload_data, upload_data_size, session_data);
189 struct MHD_Connection * connection,
191 enum MHD_RequestTerminationCode toe)
195 rd->request_completed(request, toe);
211 ssize_t bytes = dreply->
next_chunk(pos, buf, max);
213 if (bytes > 0 && request)
233WebRequestDispatcher::prepare_static_response(StaticWebReply *sreply)
235 struct MHD_Response *response;
236 WebPageReply * wpreply =
dynamic_cast<WebPageReply *
>(sreply);
238 wpreply->pack(active_baseurl_, page_header_generator_, page_footer_generator_);
243 if (sreply->body_length() > 0) {
244 response = MHD_create_response_from_buffer(sreply->body_length(),
245 (
void *)sreply->body().c_str(),
246 MHD_RESPMEM_MUST_COPY);
248 response = MHD_create_response_from_buffer(0, (
void *)
"", MHD_RESPMEM_PERSISTENT);
251 WebRequest *request = sreply->get_request();
253 request->set_reply_code(sreply->code());
254 request->increment_reply_size(sreply->body_length());
258 WebReply::HeaderMap::const_iterator i;
259 for (i = headers.begin(); i != headers.end(); ++i) {
260 MHD_add_response_header(response, i->first.c_str(), i->second.c_str());
272WebRequestDispatcher::queue_dynamic_reply(
struct MHD_Connection *connection,
273 WebRequest * request,
274 DynamicWebReply * dreply)
276 dreply->set_request(request);
277 dreply->pack_caching();
278 request->set_reply_code(dreply->code());
280 struct MHD_Response *response;
281 response = MHD_create_response_from_callback(
285 WebReply::HeaderMap::const_iterator i;
286 for (i = headers.begin(); i != headers.end(); ++i) {
287 MHD_add_response_header(response, i->first.c_str(), i->second.c_str());
290 MHD_RESULT ret = MHD_queue_response(connection, dreply->code(), response);
291 MHD_destroy_response(response);
303WebRequestDispatcher::queue_static_reply(
struct MHD_Connection *connection,
304 WebRequest * request,
305 StaticWebReply * sreply)
307 sreply->set_request(request);
309 struct MHD_Response *response = prepare_static_response(sreply);
311 MHD_RESULT rv = MHD_queue_response(connection, sreply->code(), response);
312 MHD_destroy_response(response);
321WebRequestDispatcher::queue_basic_auth_fail(
struct MHD_Connection *connection, WebRequest *request)
324#if MHD_VERSION >= 0x00090400
325 sreply.set_request(request);
326 sreply.pack_caching();
328 struct MHD_Response *response = prepare_static_response(&sreply);
331 static_cast<MHD_RESULT
>(MHD_queue_basic_auth_fail_response(connection, realm_, response));
332 MHD_destroy_response(response);
334 sreply.add_header(MHD_HTTP_HEADER_WWW_AUTHENTICATE,
335 (std::string(
"Basic realm=") + realm_).c_str());
337 MHD_RESULT rv = queue_static_reply(connection, request, &sreply);
362post_iterator(
void * cls,
363 enum MHD_ValueKind kind,
365 const char * filename,
366 const char * content_type,
367 const char * transfer_encoding,
372 WebRequest *request =
static_cast<WebRequest *
>(cls);
378 request->set_post_value(key, data + off, size);
388WebRequestDispatcher::log_uri(
const char *uri)
390 return new WebRequest(uri);
404WebRequestDispatcher::process_request(
struct MHD_Connection *connection,
407 const char * version,
408 const char * upload_data,
409 size_t * upload_data_size,
410 void ** session_data)
412 WebRequest *request =
static_cast<WebRequest *
>(*session_data);
414 if (!request->is_setup()) {
417 request->setup(url, method, version, connection);
419 active_requests_mutex_->
lock();
420 active_requests_ += 1;
421 active_requests_mutex_->
unlock();
423 if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
424 request->pp_ = MHD_create_post_processor(connection, 1024, &post_iterator, request);
430#if MHD_VERSION >= 0x00090400
432 char *user, *pass = NULL;
433 user = MHD_basic_auth_get_username_password(connection, &pass);
434 if ((user == NULL) || (pass == NULL) || !user_verifier_->
verify_user(user, pass)) {
435 return queue_basic_auth_fail(connection, request);
437 request->user_ = user;
441 if (0 == strcmp(method, MHD_HTTP_METHOD_OPTIONS)) {
443 reply->set_caching(
true);
444 const std::map<std::string, std::string> &headers{request->headers()};
445 const auto &request_method = headers.find(
"Access-Control-Request-Method");
446 const auto &request_headers = headers.find(
"Access-Control-Request-Headers");
447 if (cors_allow_all_) {
448 reply->add_header(
"Access-Control-Allow-Origin",
"*");
449 if (cors_max_age_ > 0) {
450 reply->add_header(
"Access-Control-Max-Age", std::to_string(cors_max_age_));
452 if (request_method != headers.end()) {
453 reply->add_header(
"Access-Control-Allow-Methods", request_method->second);
455 if (request_headers != headers.end()) {
456 reply->add_header(
"Access-Control-Allow-Headers", request_headers->second);
458 }
else if (!cors_origins_.empty()) {
459 const auto &origin = headers.find(
"Origin");
460 if (origin != headers.end()) {
461 if (std::find(cors_origins_.begin(), cors_origins_.end(), origin->second)
462 != cors_origins_.end()) {
463 reply->add_header(
"Access-Control-Allow-Origin", origin->second);
464 if (cors_max_age_ > 0) {
465 reply->add_header(
"Access-Control-Max-Age", std::to_string(cors_max_age_));
467 if (request_method != headers.end()) {
468 reply->add_header(
"Access-Control-Allow-Methods", request_method->second);
470 if (request_headers != headers.end()) {
471 reply->add_header(
"Access-Control-Allow-Headers", request_headers->second);
480 return queue_static_reply(connection, request, reply);
484 if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
485 if (MHD_post_process(request->pp_, upload_data, *upload_data_size) == MHD_NO) {
486 request->addto_body(upload_data, *upload_data_size);
488 if (0 != *upload_data_size) {
489 *upload_data_size = 0;
492 MHD_destroy_post_processor(request->pp_);
494 }
else if (0 != *upload_data_size) {
495 request->addto_body(upload_data, *upload_data_size);
496 *upload_data_size = 0;
499 request->finish_body();
503 WebReply * reply = url_manager_->process_request(request);
507 if (cors_allow_all_) {
508 reply->add_header(
"Access-Control-Allow-Origin",
"*");
511 StaticWebReply * sreply =
dynamic_cast<StaticWebReply *
>(reply);
512 DynamicWebReply *dreply =
dynamic_cast<DynamicWebReply *
>(reply);
514 ret = queue_static_reply(connection, request, sreply);
517 ret = queue_dynamic_reply(connection, request, dreply);
520 ret = queue_static_reply(connection, request, &ereply);
525 ret = queue_static_reply(connection, request, &ereply);
528 }
catch (Exception &e) {
530 return queue_static_reply(connection, request, &ereply);
531 }
catch (std::exception &e) {
533 return queue_static_reply(connection, request, &ereply);
538WebRequestDispatcher::request_completed(WebRequest *request, MHD_RequestTerminationCode term_code)
540 active_requests_mutex_->
lock();
541 if (active_requests_ > 0)
542 active_requests_ -= 1;
543 last_request_completion_time_->
stamp();
544 active_requests_mutex_->
unlock();
546 access_log_->
log(request);
556 return active_requests_;
566 return *last_request_completion_time_;
virtual size_t next_chunk(size_t pos, char *buffer, size_t buf_max_size)=0
Get data of next chunk.
Base class for exceptions in Fawkes.
Mutex mutual exclusion lock.
void lock()
Lock this mutex.
void unlock()
Unlock the mutex.
A class for handling time.
Time & stamp()
Set this time to the current time.
std::map< std::string, std::string > HeaderMap
Map of headers.
@ HTTP_UNAUTHORIZED
UNAUTHORIZED.
@ HTTP_INTERNAL_SERVER_ERROR
INTERNAL_SERVER_ERROR.
@ HTTP_FORBIDDEN
FORBIDDEN.
@ HTTP_NOT_FOUND
NOT_FOUND.
WebRequest * get_request() const
Get associated request.
void pack_caching()
Called just before the reply is sent.
unsigned int active_requests() const
Get number of active requests.
void setup_cors(bool allow_all, std::vector< std::string > &&origins, unsigned int max_age)
Setup cross-origin resource sharing.
void setup_basic_auth(const char *realm, WebUserVerifier *verifier)
Setup basic authentication.
static void * uri_log_cb(void *cls, const char *uri)
Callback for new requests.
~WebRequestDispatcher()
Destructor.
static void request_completed_cb(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe)
Process request completion.
static MHD_RESULT 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.
void setup_access_log(const char *filename)
Setup access log.
Time last_request_completion_time() const
Get time when last request was completed.
WebRequestDispatcher(WebUrlManager *url_manager, WebPageHeaderGenerator *headergen=0, WebPageFooterGenerator *footergen=0)
Constructor.
Web request meta data carrier.
void increment_reply_size(size_t increment_by)
Increment reply bytes counter.
Interface for user verification.
virtual bool verify_user(const char *user, const char *password) noexcept=0
Verify a user.
Webview access_log writer.
void log(const WebRequest *request)
Log a request.
Fawkes library namespace.
static void dynamic_reply_free_cb(void *reply)
Callback to free dynamic web reply.
static ssize_t dynamic_reply_data_cb(void *reply, uint64_t pos, char *buf, size_t max)
Callback based chunk-wise data.