22 #include <core/exception.h>
23 #include <core/threading/mutex.h>
24 #include <core/threading/mutex_locker.h>
25 #include <sys/socket.h>
26 #include <sys/types.h>
27 #include <utils/misc/string_urlescape.h>
28 #include <utils/time/time.h>
29 #include <webview/access_log.h>
30 #include <webview/error_reply.h>
31 #include <webview/page_reply.h>
32 #include <webview/request_dispatcher.h>
33 #include <webview/url_manager.h>
34 #include <webview/user_verifier.h>
39 #include <microhttpd.h>
41 #define UNAUTHORIZED_REPLY \
43 " <head><title>Access denied</title></head>\n" \
45 " <h1>Access denied</h1>\n" \
46 " <p>Authentication is required to access Fawkes Webview</p>\n" \
71 url_manager_ = url_manager;
72 page_header_generator_ = headergen;
73 page_footer_generator_ = footergen;
75 active_requests_mutex_ =
new Mutex();
76 last_request_completion_time_ =
new Time();
78 cors_allow_all_ =
false;
87 delete active_requests_mutex_;
88 delete last_request_completion_time_;
101 #if MHD_VERSION >= 0x00090400
105 user_verifier_ = NULL;
106 if (realm && verifier) {
107 realm_ = strdup(realm);
108 user_verifier_ = verifier;
111 throw Exception(
"libmicrohttpd >= 0.9.4 is required for basic authentication, "
112 "which was not available at compile time.");
134 std::vector<std::string> &&origins,
135 unsigned int max_age)
137 cors_allow_all_ = allow_all;
138 cors_origins_ = std::move(origins);
139 cors_max_age_ = max_age;
151 return rd->log_uri(uri);
167 struct MHD_Connection *connection,
170 const char * version,
171 const char * upload_data,
172 size_t * upload_data_size,
173 void ** session_data)
176 return rd->process_request(
177 connection, url, method, version, upload_data, upload_data_size, session_data);
188 struct MHD_Connection * connection,
190 enum MHD_RequestTerminationCode toe)
194 rd->request_completed(request, toe);
210 ssize_t bytes = dreply->
next_chunk(pos, buf, max);
212 if (bytes > 0 && request)
231 struct MHD_Response *
232 WebRequestDispatcher::prepare_static_response(StaticWebReply *sreply)
234 struct MHD_Response *response;
235 WebPageReply * wpreply =
dynamic_cast<WebPageReply *
>(sreply);
237 wpreply->pack(active_baseurl_, page_header_generator_, page_footer_generator_);
242 if (sreply->body_length() > 0) {
243 response = MHD_create_response_from_buffer(sreply->body_length(),
244 (
void *)sreply->body().c_str(),
245 MHD_RESPMEM_MUST_COPY);
247 response = MHD_create_response_from_buffer(0, (
void *)
"", MHD_RESPMEM_PERSISTENT);
250 WebRequest *request = sreply->get_request();
252 request->set_reply_code(sreply->code());
253 request->increment_reply_size(sreply->body_length());
257 WebReply::HeaderMap::const_iterator i;
258 for (i = headers.begin(); i != headers.end(); ++i) {
259 MHD_add_response_header(response, i->first.c_str(), i->second.c_str());
271 WebRequestDispatcher::queue_dynamic_reply(
struct MHD_Connection *connection,
272 WebRequest * request,
273 DynamicWebReply * dreply)
275 dreply->set_request(request);
276 dreply->pack_caching();
277 request->set_reply_code(dreply->code());
279 struct MHD_Response *response;
280 response = MHD_create_response_from_callback(
284 WebReply::HeaderMap::const_iterator i;
285 for (i = headers.begin(); i != headers.end(); ++i) {
286 MHD_add_response_header(response, i->first.c_str(), i->second.c_str());
289 int ret = MHD_queue_response(connection, dreply->code(), response);
290 MHD_destroy_response(response);
302 WebRequestDispatcher::queue_static_reply(
struct MHD_Connection *connection,
303 WebRequest * request,
304 StaticWebReply * sreply)
306 sreply->set_request(request);
308 struct MHD_Response *response = prepare_static_response(sreply);
310 int rv = MHD_queue_response(connection, sreply->code(), response);
311 MHD_destroy_response(response);
320 WebRequestDispatcher::queue_basic_auth_fail(
struct MHD_Connection *connection, WebRequest *request)
323 #if MHD_VERSION >= 0x00090400
324 sreply.set_request(request);
325 sreply.pack_caching();
327 struct MHD_Response *response = prepare_static_response(&sreply);
329 int rv = MHD_queue_basic_auth_fail_response(connection, realm_, response);
330 MHD_destroy_response(response);
332 sreply.add_header(MHD_HTTP_HEADER_WWW_AUTHENTICATE,
333 (std::string(
"Basic realm=") + realm_).c_str());
335 int rv = queue_static_reply(connection, request, &sreply);
360 post_iterator(
void * cls,
361 enum MHD_ValueKind kind,
363 const char * filename,
364 const char * content_type,
365 const char * transfer_encoding,
370 WebRequest *request =
static_cast<WebRequest *
>(cls);
376 request->set_post_value(key, data + off, size);
386 WebRequestDispatcher::log_uri(
const char *uri)
388 return new WebRequest(uri);
402 WebRequestDispatcher::process_request(
struct MHD_Connection *connection,
405 const char * version,
406 const char * upload_data,
407 size_t * upload_data_size,
408 void ** session_data)
410 WebRequest *request =
static_cast<WebRequest *
>(*session_data);
412 if (!request->is_setup()) {
415 request->setup(url, method, version, connection);
417 active_requests_mutex_->
lock();
418 active_requests_ += 1;
419 active_requests_mutex_->
unlock();
421 if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
422 request->pp_ = MHD_create_post_processor(connection, 1024, &post_iterator, request);
428 #if MHD_VERSION >= 0x00090400
430 char *user, *pass = NULL;
431 user = MHD_basic_auth_get_username_password(connection, &pass);
432 if ((user == NULL) || (pass == NULL) || !user_verifier_->
verify_user(user, pass)) {
433 return queue_basic_auth_fail(connection, request);
435 request->user_ = user;
439 if (0 == strcmp(method, MHD_HTTP_METHOD_OPTIONS)) {
441 reply->set_caching(
true);
442 const std::map<std::string, std::string> &headers{request->headers()};
443 const auto &request_method = headers.find(
"Access-Control-Request-Method");
444 const auto &request_headers = headers.find(
"Access-Control-Request-Headers");
445 if (cors_allow_all_) {
446 reply->add_header(
"Access-Control-Allow-Origin",
"*");
447 if (cors_max_age_ > 0) {
448 reply->add_header(
"Access-Control-Max-Age", std::to_string(cors_max_age_));
450 if (request_method != headers.end()) {
451 reply->add_header(
"Access-Control-Allow-Methods", request_method->second);
453 if (request_headers != headers.end()) {
454 reply->add_header(
"Access-Control-Allow-Headers", request_headers->second);
456 }
else if (!cors_origins_.empty()) {
457 const auto &origin = headers.find(
"Origin");
458 if (origin != headers.end()) {
459 if (std::find(cors_origins_.begin(), cors_origins_.end(), origin->second)
460 != cors_origins_.end()) {
461 reply->add_header(
"Access-Control-Allow-Origin", origin->second);
462 if (cors_max_age_ > 0) {
463 reply->add_header(
"Access-Control-Max-Age", std::to_string(cors_max_age_));
465 if (request_method != headers.end()) {
466 reply->add_header(
"Access-Control-Allow-Methods", request_method->second);
468 if (request_headers != headers.end()) {
469 reply->add_header(
"Access-Control-Allow-Headers", request_headers->second);
478 return queue_static_reply(connection, request, reply);
482 if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
483 if (MHD_post_process(request->pp_, upload_data, *upload_data_size) == MHD_NO) {
484 request->addto_body(upload_data, *upload_data_size);
486 if (0 != *upload_data_size) {
487 *upload_data_size = 0;
490 MHD_destroy_post_processor(request->pp_);
492 }
else if (0 != *upload_data_size) {
493 request->addto_body(upload_data, *upload_data_size);
494 *upload_data_size = 0;
497 request->finish_body();
501 WebReply *reply = url_manager_->process_request(request);
505 if (cors_allow_all_) {
506 reply->
add_header(
"Access-Control-Allow-Origin",
"*");
509 StaticWebReply * sreply =
dynamic_cast<StaticWebReply *
>(reply);
510 DynamicWebReply *dreply =
dynamic_cast<DynamicWebReply *
>(reply);
512 ret = queue_static_reply(connection, request, sreply);
515 ret = queue_dynamic_reply(connection, request, dreply);
518 ret = queue_static_reply(connection, request, &ereply);
523 ret = queue_static_reply(connection, request, &ereply);
526 }
catch (Exception &e) {
528 return queue_static_reply(connection, request, &ereply);
529 }
catch (std::exception &e) {
531 return queue_static_reply(connection, request, &ereply);
536 WebRequestDispatcher::request_completed(WebRequest *request, MHD_RequestTerminationCode term_code)
538 active_requests_mutex_->
lock();
539 if (active_requests_ > 0)
540 active_requests_ -= 1;
541 last_request_completion_time_->
stamp();
542 active_requests_mutex_->
unlock();
544 access_log_->
log(request);
554 return active_requests_;
564 return *last_request_completion_time_;