[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/feekia/libhttpserver/master/src/webserver.cpp [Back]  [Original]

/*
     This file is part of libhttpserver
     Copyright (C) 2011-2019 Sebastiano Merlino

     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
     License as published by the Free Software Foundation; either
     version 2.1 of the License, or (at your option) any later version.

     This library is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     Lesser General Public License for more details.

     You should have received a copy of the GNU Lesser General Public
     License along with this library; if not, write to the Free Software
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
     USA
*/

#include "httpserver/webserver.hpp"

#if defined(_WIN32) && !defined(__CYGWIN__)
#include 
#include 
#define _WINDOWS
#else
#if defined(__CYGWIN__)
#include 
#endif
#include 
#include 
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "httpserver/create_webserver.hpp"
#include "httpserver/details/http_endpoint.hpp"
#include "httpserver/details/modded_request.hpp"
#include "httpserver/http_request.hpp"
#include "httpserver/http_resource.hpp"
#include "httpserver/http_response.hpp"
#include "httpserver/http_utils.hpp"
#include "httpserver/string_response.hpp"

struct MHD_Connection;

#define _REENTRANT 1

#ifndef SOCK_CLOEXEC
#define SOCK_CLOEXEC 02000000
#endif

#if MHD_VERSION < 0x00097002
typedef int MHD_Result;
#endif

using std::string;
using std::pair;
using std::vector;
using std::map;
using std::set;

using httpserver::http::http_utils;
using httpserver::http::ip_representation;
using httpserver::http::base_unescaper;

namespace httpserver {

MHD_Result policy_callback(void *, const struct sockaddr*, socklen_t);
void error_log(void*, const char*, va_list);
void* uri_log(void*, const char*);
void access_log(webserver*, string);
size_t unescaper_func(void*, struct MHD_Connection*, char*);

struct compare_value {
    bool operator() (const std::pair& left, const std::pair& right) const {
        return left.second < right.second;
    }
};

#if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
static void catcher(int) { }
#endif

static void ignore_sigpipe() {
// Mingw doesn't implement SIGPIPE
#if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
    struct sigaction oldsig;
    struct sigaction sig;

    sig.sa_handler = &catcher;
    sigemptyset(&sig.sa_mask);
#ifdef SA_INTERRUPT
    sig.sa_flags = SA_INTERRUPT;  /* SunOS */
#else  // SA_INTERRUPT
    sig.sa_flags = SA_RESTART;
#endif  // SA_INTERRUPTT
    if (0 != sigaction(SIGPIPE, &sig, &oldsig)) {
        fprintf(stderr, "Failed to install SIGPIPE handler: %s\n", strerror(errno));
    }
#endif
}

// WEBSERVER
webserver::webserver(const create_webserver& params):
    port(params._port),
    start_method(params._start_method),
    max_threads(params._max_threads),
    max_connections(params._max_connections),
    memory_limit(params._memory_limit),
    content_size_limit(params._content_size_limit),
    connection_timeout(params._connection_timeout),
    per_IP_connection_limit(params._per_IP_connection_limit),
    log_access(params._log_access),
    log_error(params._log_error),
    validator(params._validator),
    unescaper(params._unescaper),
    bind_address(params._bind_address),
    bind_socket(params._bind_socket),
    max_thread_stack_size(params._max_thread_stack_size),
    use_ssl(params._use_ssl),
    use_ipv6(params._use_ipv6),
    use_dual_stack(params._use_dual_stack),
    debug(params._debug),
    pedantic(params._pedantic),
    https_mem_key(params._https_mem_key),
    https_mem_cert(params._https_mem_cert),
    https_mem_trust(params._https_mem_trust),
    https_priorities(params._https_priorities),
    cred_type(params._cred_type),
    digest_auth_random(params._digest_auth_random),
    nonce_nc_size(params._nonce_nc_size),
    running(false),
    default_policy(params._default_policy),
    basic_auth_enabled(params._basic_auth_enabled),
    digest_auth_enabled(params._digest_auth_enabled),
    regex_checking(params._regex_checking),
    ban_system_enabled(params._ban_system_enabled),
    post_process_enabled(params._post_process_enabled),
    put_processed_data_to_content(params._put_processed_data_to_content),
    file_upload_target(params._file_upload_target),
    file_upload_dir(params._file_upload_dir),
    generate_random_filename_on_upload(params._generate_random_filename_on_upload),
    deferred_enabled(params._deferred_enabled),
    single_resource(params._single_resource),
    tcp_nodelay(params._tcp_nodelay),
    not_found_resource(params._not_found_resource),
    method_not_allowed_resource(params._method_not_allowed_resource),
    internal_error_resource(params._internal_error_resource) {
        ignore_sigpipe();
        pthread_mutex_init(&mutexwait, nullptr);
        pthread_cond_init(&mutexcond, nullptr);
}

webserver::~webserver() {
    stop();
    pthread_mutex_destroy(&mutexwait);
    pthread_cond_destroy(&mutexcond);
}

void webserver::sweet_kill() {
    stop();
}

void webserver::request_completed(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) {
    // These parameters are passed to respect the MHD interface, but are not needed here.
    std::ignore = cls;
    std::ignore = connection;
    std::ignore = toe;

    details::modded_request* mr = static_cast(*con_cls);
    if (mr == nullptr) return;

    delete mr;
    mr = nullptr;
}

bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) {
    if (single_resource && ((resource != "" && resource != "/") || !family)) {
        throw std::invalid_argument("The resource should be '' or '/' and be marked as family when using a single_resource server");
    }

    details::http_endpoint idx(resource, family, true, regex_checking);

    pair result = registered_resources.insert(map::value_type(idx, hrm));

    if (result.second) {
        registered_resources_str.insert(pair(idx.get_url_complete(), result.first->second));
    }

    return result.second;
}

bool webserver::start(bool blocking) {
    struct {
        MHD_OptionItem operator ()(enum MHD_OPTION opt, intptr_t val, void *ptr = nullptr) {
            MHD_OptionItem x = {opt, val, ptr};
            return x;
        }
    } gen;
    vector iov;

    iov.push_back(gen(MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) &request_completed, nullptr));
    iov.push_back(gen(MHD_OPTION_URI_LOG_CALLBACK, (intptr_t) &uri_log, this));
    iov.push_back(gen(MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) &error_log, this));
    iov.push_back(gen(MHD_OPTION_UNESCAPE_CALLBACK, (intptr_t) &unescaper_func, this));
    iov.push_back(gen(MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout));
    if (bind_socket != 0) {
        iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket));
    }

    if (start_method == http_utils::THREAD_PER_CONNECTION && (max_threads != 0 || max_thread_stack_size != 0)) {
        throw std::invalid_argument("Cannot specify maximum number of threads when using a thread per connection");
    }

    if (max_threads != 0) {
        iov.push_back(gen(MHD_OPTION_THREAD_POOL_SIZE, max_threads));
    }

    if (max_connections != 0) {
        iov.push_back(gen(MHD_OPTION_CONNECTION_LIMIT, max_connections));
    }

    if (memory_limit != 0) {
        iov.push_back(gen(MHD_OPTION_CONNECTION_MEMORY_LIMIT, memory_limit));
    }

    if (per_IP_connection_limit != 0) {
        iov.push_back(gen(MHD_OPTION_PER_IP_CONNECTION_LIMIT, per_IP_connection_limit));
    }

    if (max_thread_stack_size != 0) {
        iov.push_back(gen(MHD_OPTION_THREAD_STACK_SIZE, max_thread_stack_size));
    }

    if (nonce_nc_size != 0) {
        iov.push_back(gen(MHD_OPTION_NONCE_NC_SIZE, nonce_nc_size));
    }

    if (use_ssl) {
        // Need for const_cast to respect MHD interface that needs a void*
        iov.push_back(gen(MHD_OPTION_HTTPS_MEM_KEY, 0, reinterpret_cast(const_cast(https_mem_key.c_str()))));
    }

    if (use_ssl) {
        // Need for const_cast to respect MHD interface that needs a void*
        iov.push_back(gen(MHD_OPTION_HTTPS_MEM_CERT, 0, reinterpret_cast(const_cast(https_mem_cert.c_str()))));
    }

    if (https_mem_trust != "" && use_ssl) {
        // Need for const_cast to respect MHD interface that needs a void*
        iov.push_back(gen(MHD_OPTION_HTTPS_MEM_TRUST, 0, reinterpret_cast(const_cast(https_mem_trust.c_str()))));
    }

    if (https_priorities != "" && use_ssl) {
        // Need for const_cast to respect MHD interface that needs a void*
        iov.push_back(gen(MHD_OPTION_HTTPS_PRIORITIES, 0, reinterpret_cast(const_cast(https_priorities.c_str()))));
    }

    if (digest_auth_random != "") {
        // Need for const_cast to respect MHD interface that needs a char*
        iov.push_back(gen(MHD_OPTION_DIGEST_AUTH_RANDOM, digest_auth_random.size(), const_cast(digest_auth_random.c_str())));
    }

#ifdef HAVE_GNUTLS
    if (cred_type != http_utils::NONE) {
        iov.push_back(gen(MHD_OPTION_HTTPS_CRED_TYPE, cred_type));
    }
#endif  // HAVE_GNUTLS

    iov.push_back(gen(MHD_OPTION_END, 0, nullptr));

    int start_conf = start_method;

    if (use_ssl) {
        start_conf |= MHD_USE_SSL;
    }

    if (use_ipv6) {
        start_conf |= MHD_USE_IPv6;
    }

    if (use_dual_stack) {
        start_conf |= MHD_USE_DUAL_STACK;
    }

    if (debug) {
        start_conf |= MHD_USE_DEBUG;
    }
    if (pedantic) {
        start_conf |= MHD_USE_PEDANTIC_CHECKS;
    }

    if (deferred_enabled) {
        start_conf |= MHD_USE_SUSPEND_RESUME;
    }

#ifdef USE_FASTOPEN
    start_conf |= MHD_USE_TCP_FASTOPEN;
#endif

    daemon = nullptr;
    if (bind_address == nullptr) {
        daemon = MHD_start_daemon(start_conf, port, &policy_callback, this,
                &answer_to_connection, this, MHD_OPTION_ARRAY,
                &iov[0], MHD_OPTION_END);
    } else {
        daemon = MHD_start_daemon(start_conf, 1, &policy_callback, this,
                &answer_to_connection, this, MHD_OPTION_ARRAY,
                &iov[0], MHD_OPTION_SOCK_ADDR, bind_address, MHD_OPTION_END);
    }

    if (daemon == nullptr) {
        throw std::invalid_argument("Unable to connect daemon to port: " + std::to_string(port));
    }

    bool value_onclose = false;

    running = true;

    if (blocking) {
        pthread_mutex_lock(&mutexwait);
        while (blocking && running) {
            pthread_cond_wait(&mutexcond, &mutexwait);
        }
        pthread_mutex_unlock(&mutexwait);
        value_onclose = true;
    }
    return value_onclose;
}

bool webserver::is_running() {
    return running;
}

bool webserver::stop() {
    if (!running) return false;

    pthread_mutex_lock(&mutexwait);
    running = false;
    pthread_cond_signal(&mutexcond);
    pthread_mutex_unlock(&mutexwait);

    MHD_stop_daemon(daemon);

    shutdown(bind_socket, 2);

    return true;
}

void webserver::unregister_resource(const string& resource) {
    // family does not matter - it just checks the url_normalized anyhow
    details::http_endpoint he(resource, false, true, regex_checking);
    registered_resources.erase(he);
    registered_resources.erase(he.get_url_complete());
    registered_resources_str.erase(he.get_url_complete());
}

void webserver::ban_ip(const string& ip) {
    ip_representation t_ip(ip);
    set::iterator it = bans.find(t_ip);
    if (it != bans.end() && (t_ip.weight() < (*it).weight())) {
        bans.erase(it);
        bans.insert(t_ip);
    } else {
        bans.insert(t_ip);
    }
}

void webserver::allow_ip(const string& ip) {
    ip_representation t_ip(ip);
    set::iterator it = allowances.find(t_ip);
    if (it != allowances.end() && (t_ip.weight() < (*it).weight())) {
        allowances.erase(it);
        allowances.insert(t_ip);
    } else {
        allowances.insert(t_ip);
    }
}

void webserver::unban_ip(const string& ip) {
    bans.erase(ip_representation(ip));
}

void webserver::disallow_ip(const string& ip) {
    allowances.erase(ip_representation(ip));
}

MHD_Result policy_callback(void *cls, const struct sockaddr* addr, socklen_t addrlen) {
    // Parameter needed to respect MHD interface, but not needed here.
    std::ignore = addrlen;

    if (!(static_cast(cls))->ban_system_enabled) return MHD_YES;

    if ((((static_cast(cls))->default_policy == http_utils::ACCEPT) &&
                ((static_cast(cls))->bans.count(ip_representation(addr))) &&
                (!(static_cast(cls))->allowances.count(ip_representation(addr)))) ||
            (((static_cast(cls))->default_policy == http_utils::REJECT) &&
             ((!(static_cast(cls))->allowances.count(ip_representation(addr))) ||
              ((static_cast(cls))->bans.count(ip_representation(addr)))))) {
        return MHD_NO;
    }

    return MHD_YES;
}

void* uri_log(void* cls, const char* uri) {
    // Parameter needed to respect MHD interface, but not needed here.
    std::ignore = cls;

    struct details::modded_request* mr = new details::modded_request();
    mr->complete_uri = new string(uri);
    mr->second = false;
    return reinterpret_cast(mr);
}

void error_log(void* cls, const char* fmt, va_list ap) {
    // Parameter needed to respect MHD interface, but not needed here.
    std::ignore = ap;

    webserver* dws = static_cast(cls);
    if (dws->log_error != nullptr) dws->log_error(fmt);
}

void access_log(webserver* dws, string uri) {
    if (dws->log_access != nullptr) dws->log_access(uri);
}

size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) {
    // Parameter needed to respect MHD interface, but not needed here.
    std::ignore = cls;
    std::ignore = c;

    // THIS IS USED TO AVOID AN UNESCAPING OF URL BEFORE THE ANSWER.
    // IT IS DUE TO A BOGUS ON libmicrohttpd (V0.99) THAT PRODUCING A
    // STRING CONTAINING '\0' AFTER AN UNESCAPING, IS UNABLE TO PARSE
    // ARGS WITH get_connection_values FUNC OR lookup FUNC.
    return std::string(s).size();
}

MHD_Result webserver::post_iterator(void *cls, enum MHD_ValueKind kind,
        const char *key, const char *filename, const char *content_type,
        const char *transfer_encoding, const char *data, uint64_t off, size_t size) {
    // Parameter needed to respect MHD interface, but not needed here.
    std::ignore = kind;
    std::ignore = off;

    struct details::modded_request* mr = (struct details::modded_request*) cls;

    try {
        if (filename == nullptr || mr->ws->file_upload_target != FILE_UPLOAD_DISK_ONLY) {
            mr->dhr->set_arg(key, mr->dhr->get_arg(key) + std::string(data, size));
        }

        if (filename && *filename != '\0' && mr->ws->file_upload_target != FILE_UPLOAD_MEMORY_ONLY) {
            // either get the existing file info struct or create a new one in the file map
            http::file_info& file = mr->dhr->get_or_create_file_info(key, filename);
            // if the file_system_file_name is not filled yet, this is a new entry and the name has to be set
            // (either random or copy of the original filename)
            if (file.get_file_system_file_name().empty()) {
                if (mr->ws->generate_random_filename_on_upload) {
                    file.set_file_system_file_name(http_utils::generate_random_upload_filename(mr->ws->file_upload_dir));
                } else {
                    file.set_file_system_file_name(mr->ws->file_upload_dir + "/" + std::string(filename));
                }
                // to not append to an already existing file, delete an already existing file
                unlink(file.get_file_system_file_name().c_str());
                if (content_type != nullptr) {
                    file.set_content_type(content_type);
                }
                if (transfer_encoding != nullptr) {
                    file.set_transfer_encoding(transfer_encoding);
                }
            }

            // if multiple files are uploaded, a different filename or a different key indicates
            // the start of a new file, so close the previous one
            if (mr->upload_filename.empty() ||
                mr->upload_key.empty() ||
                0 != strcmp(filename, mr->upload_filename.c_str()) ||
                0 != strcmp(key, mr->upload_key.c_str())) {
                if (mr->upload_ostrm != nullptr) {
                    mr->upload_ostrm->close();
                }
            }

            if (mr->upload_ostrm == nullptr || !mr->upload_ostrm->is_open()) {
                mr->upload_key = key;
                mr->upload_filename = filename;
                delete mr->upload_ostrm;
                mr->upload_ostrm = new std::ofstream();
                mr->upload_ostrm->open(file.get_file_system_file_name(), std::ios::binary | std::ios::app);
            }

            if (size > 0) {
                mr->upload_ostrm->write(data, size);
            }

            // update the file size in the map
            file.grow_file_size(size);
        }
        return MHD_YES;
    } catch(const http::generateFilenameException& e) {
        return MHD_NO;
    }
}

void webserver::upgrade_handler(void *cls, struct MHD_Connection* connection, void **con_cls, int upgrade_socket) {
    std::ignore = cls;
    std::ignore = connection;
    std::ignore = con_cls;
    std::ignore = upgrade_socket;
}

const std::shared_ptr webserver::not_found_page(details::modded_request* mr) const {
    if (not_found_resource != nullptr) {
        return not_found_resource(*mr->dhr);
    } else {
        return std::shared_ptr(new string_response(NOT_FOUND_ERROR, http_utils::http_not_found));
    }
}

const std::shared_ptr webserver::method_not_allowed_page(details::modded_request* mr) const {
    if (method_not_allowed_resource != nullptr) {
        return method_not_allowed_resource(*mr->dhr);
    } else {
        return std::shared_ptr(new string_response(METHOD_ERROR, http_utils::http_method_not_allowed));
    }
}

const std::shared_ptr webserver::internal_error_page(details::modded_request* mr, bool force_our) const {
    if (internal_error_resource != nullptr && !force_our) {
        return internal_error_resource(*mr->dhr);
    } else {
        return std::shared_ptr(new string_response(GENERIC_ERROR, http_utils::http_internal_server_error, "text/plain"));
    }
}

MHD_Result webserver::requests_answer_first_step(MHD_Connection* connection, struct details::modded_request* mr) {
    mr->second = true;
    mr->dhr = new http_request(connection, unescaper);

    if (!mr->has_body) {
        return MHD_YES;
    }

    mr->dhr->set_content_size_limit(content_size_limit);
    const char *encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, http_utils::http_header_content_type);

    if (post_process_enabled &&
        (nullptr != encoding &&
            ((0 == strncasecmp(http_utils::http_post_encoding_form_urlencoded, encoding, strlen(http_utils::http_post_encoding_form_urlencoded))) ||
             (0 == strncasecmp(http_utils::http_post_encoding_multipart_formdata, encoding, strlen(http_utils::http_post_encoding_multipart_formdata)))))) {
        const size_t post_memory_limit(32 * 1024);  // Same as #MHD_POOL_SIZE_DEFAULT
        mr->pp = MHD_create_post_processor(connection, post_memory_limit, &post_iterator, mr);
    } else {
        mr->pp = nullptr;
    }
    return MHD_YES;
}

MHD_Result webserver::requests_answer_second_step(MHD_Connection* connection, const char* method,
        const char* version, const char* upload_data,
        size_t* upload_data_size, struct details::modded_request* mr) {
    if (0 == *upload_data_size) return complete_request(connection, mr, version, method);

    if (mr->has_body) {
#ifdef DEBUG
        std::cout grow_content(upload_data, *upload_data_size);
        }

        if (mr->pp != nullptr) {
            mr->ws = this;
            MHD_post_process(mr->pp, upload_data, *upload_data_size);
            if (mr->upload_ostrm != nullptr && mr->upload_ostrm->is_open()) {
                mr->upload_ostrm->close();
            }
        }
    }

    *upload_data_size = 0;
    return MHD_YES;
}

MHD_Result webserver::finalize_answer(MHD_Connection* connection, struct details::modded_request* mr, const char* method) {
    int to_ret = MHD_NO;

    map::iterator fe;

    http_resource* hrm;

    bool found = false;
    struct MHD_Response* raw_response;
    if (!single_resource) {
        const char* st_url = mr->standardized_url->c_str();
        fe = registered_resources_str.find(st_url);
        if (fe == registered_resources_str.end()) {
            if (regex_checking) {
                map::iterator found_endpoint;

                details::http_endpoint endpoint(st_url, false, false, false);

                map::iterator it;

                size_t len = 0;
                size_t tot_len = 0;
                for (it = registered_resources.begin(); it != registered_resources.end(); ++it) {
                    size_t endpoint_pieces_len = (*it).first.get_url_pieces().size();
                    size_t endpoint_tot_len = (*it).first.get_url_complete().size();
                    if (!found || endpoint_pieces_len > len || (endpoint_pieces_len == len && endpoint_tot_len > tot_len)) {
                        if ((*it).first.match(endpoint)) {
                            found = true;
                            len = endpoint_pieces_len;
                            tot_len = endpoint_tot_len;
                            found_endpoint = it;
                        }
                    }
                }

                if (found) {
                    vector url_pars = found_endpoint->first.get_url_pars();

                    vector url_pieces = endpoint.get_url_pieces();
                    vector chunks = found_endpoint->first.get_chunk_positions();
                    for (unsigned int i = 0; i < url_pars.size(); i++) {
                        mr->dhr->set_arg(url_pars[i], url_pieces[chunks[i]]);
                    }

                    hrm = found_endpoint->second;
                }
            }
        } else {
            hrm = fe->second;
            found = true;
        }
    } else {
        hrm = registered_resources.begin()->second;
        found = true;
    }

    if (found) {
        try {
            if (hrm->is_allowed(method)) {
                mr->dhrs = ((hrm)->*(mr->callback))(*mr->dhr);  // copy in memory (move in case)
                if (mr->dhrs.get() == nullptr || mr->dhrs->get_response_code() == -1) {
                    mr->dhrs = internal_error_page(mr);
                }
            } else {
                mr->dhrs = method_not_allowed_page(mr);

                vector allowed_methods = hrm->get_allowed_methods();
                if (allowed_methods.size() > 0) {
                    string header_value = allowed_methods[0];
                    for (auto it = allowed_methods.cbegin() + 1; it != allowed_methods.cend(); ++it) {
                        header_value += ", " + (*it);
                    }
                    mr->dhrs->with_header(http_utils::http_header_allow, header_value);
                }
            }
        } catch(const std::exception& e) {
            mr->dhrs = internal_error_page(mr);
        } catch(...) {
            mr->dhrs = internal_error_page(mr);
        }
    } else {
        mr->dhrs = not_found_page(mr);
    }

    try {
        try {
            raw_response = mr->dhrs->get_raw_response();
            if (raw_response == nullptr) {
                mr->dhrs = internal_error_page(mr);
                raw_response = mr->dhrs->get_raw_response();
            }
        } catch(const std::invalid_argument& iae) {
            mr->dhrs = not_found_page(mr);
            raw_response = mr->dhrs->get_raw_response();
        } catch(const std::exception& e) {
            mr->dhrs = internal_error_page(mr);
            raw_response = mr->dhrs->get_raw_response();
        } catch(...) {
            mr->dhrs = internal_error_page(mr);
            raw_response = mr->dhrs->get_raw_response();
        }
    } catch(...) {  // catches errors in internal error page
        mr->dhrs = internal_error_page(mr, true);
        raw_response = mr->dhrs->get_raw_response();
    }
    mr->dhrs->decorate_response(raw_response);
    to_ret = mr->dhrs->enqueue_response(connection, raw_response);
    MHD_destroy_response(raw_response);
    return (MHD_Result) to_ret;
}

MHD_Result webserver::complete_request(MHD_Connection* connection, struct details::modded_request* mr, const char* version, const char* method) {
    mr->ws = this;

    mr->dhr->set_path(mr->standardized_url->c_str());
    mr->dhr->set_method(method);
    mr->dhr->set_version(version);

    return finalize_answer(connection, mr, method);
}

MHD_Result webserver::answer_to_connection(void* cls, MHD_Connection* connection, const char* url, const char* method,
        const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls) {
    struct details::modded_request* mr = static_cast(*con_cls);

    if (mr->second != false) {
        return static_cast(cls)->requests_answer_second_step(connection, method, version, upload_data, upload_data_size, mr);
    }

    const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CONNECTION_FD);

    if (static_cast(cls)->tcp_nodelay) {
        int yes = 1;
        setsockopt(conninfo->connect_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), sizeof(int));
    }

    std::string t_url = url;

    base_unescaper(&t_url, static_cast(cls)->unescaper);
    mr->standardized_url = new string(http_utils::standardize_url(t_url));

    mr->has_body = false;

    access_log(static_cast(cls), *(mr->complete_uri) + " METHOD: " + method);

    if (0 == strcasecmp(method, http_utils::http_method_get)) {
        mr->callback = &http_resource::render_GET;
    } else if (0 == strcmp(method, http_utils::http_method_post)) {
        mr->callback = &http_resource::render_POST;
        mr->has_body = true;
    } else if (0 == strcasecmp(method, http_utils::http_method_put)) {
        mr->callback = &http_resource::render_PUT;
        mr->has_body = true;
    } else if (0 == strcasecmp(method, http_utils::http_method_delete)) {
        mr->callback = &http_resource::render_DELETE;
        mr->has_body = true;
    } else if (0 == strcasecmp(method, http_utils::http_method_patch)) {
        mr->callback = &http_resource::render_PATCH;
        mr->has_body = true;
    } else if (0 == strcasecmp(method, http_utils::http_method_head)) {
        mr->callback = &http_resource::render_HEAD;
    } else if (0 ==strcasecmp(method, http_utils::http_method_connect)) {
        mr->callback = &http_resource::render_CONNECT;
    } else if (0 == strcasecmp(method, http_utils::http_method_trace)) {
        mr->callback = &http_resource::render_TRACE;
    } else if (0 ==strcasecmp(method, http_utils::http_method_options)) {
        mr->callback = &http_resource::render_OPTIONS;
    }

    return static_cast(cls)->requests_answer_first_step(connection, mr);
}

}  // namespace httpserver

Web Proxy Viewer  |  New URL  |  Original Page