Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HttpAppFramework / HttpClientPtr ] can't change/set (ssl key, cert, & configuration) after/on plugin #2047

Closed
prothegee opened this issue May 29, 2024 · 2 comments

Comments

@prothegee
Copy link

[HttpAppFramework / HttpClientPtr ] can't change/set (ssl key, cert, & configuration) after/on plugin

May related to:

  • webserver reverse proxy
  • HttpFramework::setSSLFiles
  • HttpFramework::setSSLConfigCommands
  • HttpClient::setCertPath
  • HttpClient::addSSLConfigs

I was try on a server using ssl/tls and use drogon framework as webserver,

but the catch is, the first key to handshake cert (I think), key & sslconf already binded and can't change after it's run (even re-configured on pre routing reverse proxy plugin)


I was expecting

app().setSSLFiles(sslConf.cert, sslConf.key);
app().setSSLConfigCommands(sslConf.configurations);

pClient->setCertPath(sslConf.cert, sslConf.key);
pClient->addSSLConfigs(sslConf.configurations);

able to set cert, key and sslconf manualy and use correct one.


But the certficate alway belong to config file under

drogon::app().loadConfigFile("../../../configs/json/drogon_framework.json");


summary & question

long story short video

is there's a existing related feature/function/subject that can change/set cert, key, & sslconf

after HttpAppFramework run?



related file/config

config file
{
    "custom_config": {
        "name": "drogon_framework"
    },
    "listeners": [
        {
            "address": "0.0.0.0",
            "port": 80,
            "https": false,
            "cert": "",
            "key": "",
            "ssl_conf": []
        },
        {
            "address": "0.0.0.0",
            "port": 443,
            "https": true,
            "cert": "/etc/letsencrypt/live/prothegee.com/cert.pem",
            "key": "/etc/letsencrypt/live/prothegee.com/privkey.pem",
            "ssl_conf": [
                ["MinProtocol", "TLSv1.2"],
                ["MaxProtocol", "TLSv1.3"],
                ["AllowNoDHEKEX", "off"],
                ["-SessionTicket", "off"],
                ["ServerPreference", "on"],
                ["ChainCAFile", "/etc/letsencrypt/live/prothegee.com/fullchain.pem"],
                ["VerifyCAFile", "/etc/letsencrypt/live/prothegee.com/fullchain.pem"],
                ["RequestCAFile", "/etc/letsencrypt/live/prothegee.com/fullchain.pem"],
                ["DHParameters", "/root/projects/prothegee/com.prothegee.stack.v1.private/ssl/_prothegee.com.dhparams.pem"],
                ["Options", "-strict, ServerPreference, -SessionTicket, Compression, AllowNoDHEKEX"],
                ["Curves", "P-256:P-384:P-521:X25519:X448:ffdhe2048:ffdhe3072:ffdhe4096:ffdhe6144:ffdhe8192"],
                ["CipherString", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"],
                ["SignatureAlgorithms", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"]
            ]
        }
    ],
    "mime": {
        "text/markdown": "md",
        "text/gemini": [
            "gmi", "gemini"
        ],
        "text/css": [
            "css", "css.map"
        ],
        "text/javascript": [
            "js", "mjs", "js.map", "framework.js", "loader.js",
            "audio.worklet.js",
            "data", "pck",
            "wasm", "side.wasm",
            "worker.js"
        ]
    },
    "app": {
        "number_of_threads": 1,
        "document_root": "/root/projects/prothegee/com.prothegee.stack.v1/public/_",
        "enable_session": false,
        "session_timeout": 604800,
        "session_same_site" : "Strict",
        "session_cookie_key": "_drogon_framework-session",
        "max_connections": 150000,
        "max_connections_per_ip": 0,
        "client_max_body_size": "5M",
        "client_max_memory_body_size": "5M",
        "client_max_websocket_message_size": "5M",
        "log": {
            "use_spdlog": false,
            "log_path": "/root/projects/prothegee/com.prothegee.stack.v1/logs/info",
            "logfile_base_name": "drogon_framework.info",
            "log_size_limit": 100000000,
            "max_files": 30,
            "log_level": "INFO"
        },
        "run_as_daemon": true,
        "relaunch_on_error": true,
        "upload_path": "../media",
        "use_sendfile": false,
        "use_gzip": true,
        "use_brotli": true,
        "static_files_cache_time": -1,
        "idle_connection_timeout": 600,
        "enable_server_header": true,
        "server_header_field": "Drogon Framework",
        "enable_date_header": false,
        "keepalive_requests": 0,
        "br_static": true,
        "reuse_port": false,
        "file_types": [
            "gif",
            "png",
            "jpg",
            "map",
            "js",
            "framework.js",
            "loader.js",
            "bundle.js",
            "min.js",
            "js.map",
            "js.flow",
            "module.js",
            "cjs",
            "mjs",
            "css",
            "css.map",
            "min.css",
            "min.css.map",
            "rtl.css.map",
            "rtl.min.css.map",
            "scss",
            "html",
            "ico",
            "swf",
            "svg",
            "xap",
            "apk",
            "cur",
            "xml",
            "ttf",
            "woff",
            "woff2",
            "text",
            "txt",
            "json",
            "audio.worklet.js",
            "data",
            "pck",
            "wasm",
            "side.wasm",
            "worker.js"
        ]
    },
    "plugins": [
        {
            "name": "drogon::plugin::AccessLogger",
            "dependencies": [],
            "config": {
                "log_path": "/root/projects/prothegee/com.prothegee.stack.v1/logs/access",
                "log_format": "$request_date $method $url [$body_bytes_received] ($remote_addr - $local_addr) $status $body_bytes_sent $processing_time",
                "log_file": "drogon_framework.access.log",
                "log_size_limit": 100000000,
                "use_local_time": true,
                "log_index": 0
            }
        },
        {
            "name": "drogon_framework::ReverseProxy",
            "dependencies": [],
            "config": {
                "backends": [
                    {
                        "host": "prothegee.com",
                        "redirect": false,
                        "redirect_to": "",
                        "header_add": true,
                        "header_add_list": [
                            ["x-frame-options", "sameorigin"]
                        ],
                        "document_root": "/root/projects/prothegee/com.prothegee.stack.v1/public/www",
                        "origin_whitelist_check": true,
                        "origin_whitelist_check_data": "https://prothegee.com https://api.prothegee.com https://www.prothegee.com https://media.prothegee.com",
                        "internal_proxies": [
                            "http://127.0.0.1:9101",
                            "http://127.0.0.1:9102",
                            "http://127.0.0.1:9103"
                        ],
                        "ssl": true,
                        "ssl_conf": {
                            "cert": "/etc/letsencrypt/live/prothegee.com/cert.pem",
                            "key": "/etc/letsencrypt/live/prothegee.com/privkey.pem",
                            "use_old_tls": false,
                            "configurations": [
                                ["MinProtocol", "TLSv1.2"],
                                ["MaxProtocol", "TLSv1.3"],
                                ["AllowNoDHEKEX", "off"],
                                ["-SessionTicket", "off"],
                                ["ServerPreference", "on"],
                                ["ChainCAFile", "/etc/letsencrypt/live/prothegee.com/fullchain.pem"],
                                ["VerifyCAFile", "/etc/letsencrypt/live/prothegee.com/fullchain.pem"],
                                ["RequestCAFile", "/etc/letsencrypt/live/prothegee.com/fullchain.pem"],
                                ["DHParameters", "/root/projects/prothegee/com.prothegee.stack.v1.private/ssl/_prothegee.com.dhparams.pem"],
                                ["Options", "-strict, ServerPreference, -SessionTicket, Compression, AllowNoDHEKEX"],
                                ["Curves", "P-256:P-384:P-521:X25519:X448:ffdhe2048:ffdhe3072:ffdhe4096:ffdhe6144:ffdhe8192"],
                                ["CipherString", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"],
                                ["SignatureAlgorithms", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"]
                            ]
                        }
                    },
                    {
                        "host": "www.prothegee.com",
                        "redirect": true,
                        "redirect_to": "https://prothegee.com",
                        "header_add": true,
                        "header_add_list": [
                            ["x-frame-options", "sameorigin"]
                        ],
                        "document_root": "/root/projects/prothegee/com.prothegee.stack.v1/public/www",
                        "origin_whitelist_check": true,
                        "origin_whitelist_check_data": "https://prothegee.com https://api.prothegee.com https://www.prothegee.com https://media.prothegee.com",
                        "internal_proxies": [
                            "http://127.0.0.1:9101",
                            "http://127.0.0.1:9102",
                            "http://127.0.0.1:9103"
                        ],
                        "ssl": true,
                        "ssl_conf": {
                            "cert": "/etc/letsencrypt/live/www.prothegee.com/cert.pem",
                            "key": "/etc/letsencrypt/live/www.prothegee.com/privkey.pem",
                            "use_old_tls": false,
                            "configurations": [
                                ["MinProtocol", "TLSv1.2"],
                                ["MaxProtocol", "TLSv1.3"],
                                ["AllowNoDHEKEX", "off"],
                                ["-SessionTicket", "off"],
                                ["ServerPreference", "on"],
                                ["ChainCAFile", "/etc/letsencrypt/live/www.prothegee.com/fullchain.pem"],
                                ["VerifyCAFile", "/etc/letsencrypt/live/www.prothegee.com/fullchain.pem"],
                                ["RequestCAFile", "/etc/letsencrypt/live/www.prothegee.com/fullchain.pem"],
                                ["DHParameters", "/root/projects/prothegee/com.prothegee.stack.v1.private/ssl/_prothegee.com.dhparams.pem"],
                                ["Options", "-strict, ServerPreference, -SessionTicket, Compression, AllowNoDHEKEX"],
                                ["Curves", "P-256:P-384:P-521:X25519:X448:ffdhe2048:ffdhe3072:ffdhe4096:ffdhe6144:ffdhe8192"],
                                ["CipherString", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"],
                                ["SignatureAlgorithms", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"]
                            ]
                        }
                    },
                    {
                        "host": "api.prothegee.com",
                        "redirect": false,
                        "redirect_to": "",
                        "header_add": true,
                        "header_add_list": [
                            ["x-frame-options", "sameorigin"]
                        ],
                        "document_root": "/root/projects/prothegee/com.prothegee.stack.v1/public/api",
                        "origin_whitelist_check": false,
                        "origin_whitelist_check_data": "https://prothegee.com https://api.prothegee.com https://www.prothegee.com https://media.prothegee.com",
                        "internal_proxies": [
                            "http://127.0.0.1:9104",
                            "http://127.0.0.1:9105",
                            "http://127.0.0.1:9106"
                        ],
                        "ssl": true,
                        "ssl_conf": {
                            "cert": "/etc/letsencrypt/live/api.prothegee.com/cert.pem",
                            "key": "/etc/letsencrypt/live/api.prothegee.com/privkey.pem",
                            "use_old_tls": false,
                            "configurations": [
                                ["MinProtocol", "TLSv1.2"],
                                ["MaxProtocol", "TLSv1.3"],
                                ["AllowNoDHEKEX", "off"],
                                ["-SessionTicket", "off"],
                                ["ServerPreference", "on"],
                                ["ChainCAFile", "/etc/letsencrypt/live/api.prothegee.com/fullchain.pem"],
                                ["VerifyCAFile", "/etc/letsencrypt/live/api.prothegee.com/fullchain.pem"],
                                ["RequestCAFile", "/etc/letsencrypt/live/api.prothegee.com/fullchain.pem"],
                                ["DHParameters", "/root/projects/prothegee/com.prothegee.stack.v1.private/ssl/_prothegee.com.dhparams.pem"],
                                ["Options", "-strict, ServerPreference, -SessionTicket, Compression, AllowNoDHEKEX"],
                                ["Curves", "P-256:P-384:P-521:X25519:X448:ffdhe2048:ffdhe3072:ffdhe4096:ffdhe6144:ffdhe8192"],
                                ["CipherString", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"],
                                ["SignatureAlgorithms", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"]
                            ]
                        }
                    },
                    {
                        "host": "media.prothegee.com",
                        "redirect": false,
                        "redirect_to": "",
                        "header_add": true,
                        "header_add_list": [
                            ["x-frame-options", "sameorigin"]
                        ],
                        "document_root": "/root/projects/prothegee/com.prothegee.stack.v1/public/api",
                        "origin_whitelist_check": false,
                        "origin_whitelist_check_data": "https://prothegee.com https://api.prothegee.com https://www.prothegee.com https://media.prothegee.com",
                        "internal_proxies": [
                            "http://127.0.0.1:9107",
                            "http://127.0.0.1:9108",
                            "http://127.0.0.1:9109"
                        ],
                        "ssl": true,
                        "ssl_conf": {
                            "cert": "/etc/letsencrypt/live/media.prothegee.com/cert.pem",
                            "key": "/etc/letsencrypt/live/media.prothegee.com/privkey.pem",
                            "use_old_tls": false,
                            "configurations": [
                                ["MinProtocol", "TLSv1.2"],
                                ["MaxProtocol", "TLSv1.3"],
                                ["AllowNoDHEKEX", "off"],
                                ["-SessionTicket", "off"],
                                ["ServerPreference", "on"],
                                ["ChainCAFile", "/etc/letsencrypt/live/media.prothegee.com/fullchain.pem"],
                                ["VerifyCAFile", "/etc/letsencrypt/live/media.prothegee.com/fullchain.pem"],
                                ["RequestCAFile", "/etc/letsencrypt/live/media.prothegee.com/fullchain.pem"],
                                ["DHParameters", "/root/projects/prothegee/com.prothegee.stack.v1.private/ssl/_prothegee.com.dhparams.pem"],
                                ["Options", "-strict, ServerPreference, -SessionTicket, Compression, AllowNoDHEKEX"],
                                ["Curves", "P-256:P-384:P-521:X25519:X448:ffdhe2048:ffdhe3072:ffdhe4096:ffdhe6144:ffdhe8192"],
                                ["CipherString", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"],
                                ["SignatureAlgorithms", "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"]
                            ]
                        }
                    }
                ],
                "pipelining": 16,
                "connection_factor": 1,
                "default_document_root": "/root/projects/prothegee/com.prothegee.stack.v1/public/_",
                "same_client_to_same_backend": false
            }
        }
    ]
}

reverse_proxy.h
#ifndef DROGON_FRAMEWORK_REVERSE_PROXY_H
#define DROGON_FRAMEWORK_REVERSE_PROXY_H
#include <drogon/drogon.h>

#include <drogon_framework/base/config.h>

namespace drogon_framework
{

using namespace drogon;


/**
 * @brief type for backend ssl storage
 * 
 */
struct TBackendSSL
{
    std::string key;
    std::string cert;
    bool use_old_tls;
    std::vector<std::pair<std::string, std::string>> configurations;
};

/**
 * @brief type for backend storage
 * 
 */
struct TBackendStorage
{
    bool ssl;
    bool redirect;
    bool header_add;
    bool origin_whitelist_check;
    std::string host;
    TBackendSSL ssl_conf;
    std::string redirect_to;
    std::string document_root;
    std::string origin_whitelist_check_data;
    std::vector<std::string> internal_proxies;
    std::vector<std::pair<std::string, std::string>> header_add_list_pair;
};


/**
 * @brief drogon_framework reverse proxy plugin
 * 
 */
class ReverseProxy final : public Plugin<ReverseProxy>
{
private:
    bool m_useSSL = false;
    bool m_sameClientToSameBackend = false;

    size_t m_backendIdx = 0;
    size_t m_pipelining = 16;
    size_t m_connectionFactor = 1;

    std::vector<TBackendStorage> m_backends;

    std::string m_defaultDocumentRoot = "";

    IOThreadStorage<size_t> m_clientIndex = 0;
    IOThreadStorage<std::vector<HttpClientPtr>> m_clients;

protected:
    void initAndStart(const Json::Value &config) override;
    void shutdown() override;

    /**
     * @brief pre routing request to array backend
     * 
     * @param pReq 
     * @param aCallback 
     * @param acCallback 
     */
    void PreRouting(const HttpRequestPtr &pReq, AdviceCallback &&aCallback, AdviceChainCallback &&acCallback);

public:
    ReverseProxy(/* args */);
    ~ReverseProxy();
};

} // namespace drogon_framework

#endif // DROGON_FRAMEWORK_REVERSE_PROXY_H

reverse_proxy.cc
#include "reverse_proxy.h"

namespace drogon_framework
{

void ReverseProxy::initAndStart(const Json::Value &config)
{
    #if PROJECT_IS_DEBUG
    std::cout << "drogon_framework reverse proxy init and start\n";
    #endif

    // backends storage
    if (config.isMember("backends") && config["backends"].isArray())
    {
        for (auto &backends : config["backends"])
        {
            TBackendStorage backend;

            backend.host = backends["host"].asString();

            backend.redirect = backends["redirect"].asBool();

            backend.redirect_to = backends["redirect_to"].asString();

            backend.origin_whitelist_check = backends["origin_whitelist_check"].asBool();

            backend.origin_whitelist_check_data = backends["origin_whitelist_check_data"].asString();

            backend.header_add = backends["header_add"].asBool();

            if (backend.header_add)
            {
                if (backends["header_add_list"].isArray() && backends["header_add_list"].size() <= 0)
                {
                    app().getLoop()->runAfter(0, [&]()
                    {
                        std::cerr << "ERROR: you are trying to add more header response, but header add list is not supplied or 0\n";
                        app().quit();
                    });
                }

                for (auto &header : backends["header_add_list"])
                {
                    if (header.isArray() && header.size() == 2 && header[0].isString() && header[1].isString())
                    {
                        std::string key = header[0].asString();
                        std::string value = header[1].asString();

                        backend.header_add_list_pair.emplace_back(key, value);
                    }
                    else
                    {
                        app().getLoop()->runAfter(0, [&]()
                        {
                            std::cerr << "ERROR: can't use header list, these array can't assign as pair 2 string\n";
                            app().quit();
                        });
                    }
                }
            }

            backend.document_root = backends["document_root"].asString();

            // check backend proxies
            if (backends["internal_proxies"].size() <= 0)
            {
                app().getLoop()->runAfter(0, [&]()
                {
                    std::cerr << "ERROR: internal backend proxies should be provide at least one\n";
                    app().quit();
                });
            }

            for (auto &proxy : backends["internal_proxies"])
            {
                backend.internal_proxies.emplace_back(proxy.asString());
            }

            backend.ssl = backends["ssl"].asBool();

            if (backend.ssl)
            {
                TBackendSSL backendSSL;

                backendSSL.key = backends["ssl_conf"]["key"].asString();
                backendSSL.cert = backends["ssl_conf"]["cert"].asString();
                backendSSL.use_old_tls = backends["ssl_conf"]["use_old_tls"].asBool();

                if (backendSSL.key.length() <= 0)
                {
                    app().getLoop()->runAfter(0, [&]()
                    {
                        std::cerr << "ERROR: you're configuring using ssl, but key is not supplied\n";
                        app().quit();
                    });
                }

                if (backendSSL.cert.length() <= 0)
                {
                    app().getLoop()->runAfter(0, [&]()
                    {
                        std::cerr << "ERROR: you're configuring using ssl, but cert is not supplied\n";
                        app().quit();
                    });
                }

                const auto SSL_CONF_CONFIG = backends["ssl_conf"]["configurations"];

                if (SSL_CONF_CONFIG.isArray() && SSL_CONF_CONFIG.size() >= 1)
                {
                    for (auto &ssl_cfg : SSL_CONF_CONFIG)
                    {
                        if (ssl_cfg.isArray() && ssl_cfg.size() == 2 && ssl_cfg[0].isString() && ssl_cfg[1].isString())
                        {
                            std::string key = ssl_cfg[0].asString();
                            std::string value = ssl_cfg[1].asString();

                            backendSSL.configurations.emplace_back(key, value);
                        }
                        else
                        {
                            app().getLoop()->runAfter(0, [&]()
                            {
                                std::cerr << "ERROR: can't use config, these array can't assign as pair 2 string\n";
                                app().quit();
                            });
                        }
                    }

                    backend.ssl_conf = backendSSL;
                }
                else
                {
                    app().getLoop()->runAfter(0, [&]()
                    {
                        std::cerr << "ERROR: ssl_conf configurations is not an array or empty\nREADMORE: https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html\n";
                        app().quit();
                    });
                }
            }

            m_backends.emplace_back(backend);
        }

        // final check
        if (m_backends.empty())
        {
            app().getLoop()->runAfter(0, [&]()
            {
                std::cerr << "ERROR: backends configuration is empty\n";
                app().quit();
            });
        }
    }
    else
    {
        app().getLoop()->runAfter(0, [&]()
        {
            std::cerr << "ERROR: configuration\n";
            app().quit();
        });
    }

    // pipelining
    m_pipelining = config.get("pipelining", 0).asInt();

    // connection factor
    m_connectionFactor = config.get("connection_factor", 0).asInt();

    if (m_connectionFactor == 0 || m_connectionFactor > 100)
    {
        app().getLoop()->runAfter(0, [&]()
        {
            std::cerr << "ERROR: invalid number of connection factor\n";
            app().quit();
        });
    }

    // same client to same backend
    m_sameClientToSameBackend = config.get("same_client_to_same_backend", false).asBool();

    // default document root
    m_defaultDocumentRoot = config["default_document_root"].asString();

    // clients config
    m_clients.init([this](std::vector<HttpClientPtr> &clients, size_t ioLoopIndex)
    {
        clients.resize(m_backends.size() * m_connectionFactor);
    });

    // client index config
    m_clientIndex.init([this](size_t &index, size_t ioLoopIndex)
    {
        index = ioLoopIndex;
    });

    // pre routing register
    app().registerPreRoutingAdvice([this](const HttpRequestPtr &pReq, AdviceCallback &&aCallback, AdviceChainCallback &&acCallback)
    {
        PreRouting(pReq, std::move(aCallback), std::move(acCallback));
    });
}

void ReverseProxy::shutdown()
{
    #if PROJECT_IS_DEBUG
    std::cout << "drogon_framework reverse proxy shutdown\n";
    #endif
}

void ReverseProxy::PreRouting(const HttpRequestPtr &pReq, AdviceCallback &&aCallback, AdviceChainCallback &&acCallback)
{
    size_t index = 0, index_be = 0;
    size_t count = -1;
    auto host = pReq->getHeader("host");

    auto &clientsVector = *m_clients;

    bool redirect = false;
    std::string redirectUrl = "";

    bool requestFound = false;

    // check for correct request
    for (const auto &backend : m_backends)
    {
        count++;

        if (backend.host.rfind(host, 0) == 0)
        {
            index_be = count;
            requestFound = true;
            break;
        }
    }

    m_backendIdx = index_be;

    // same client same backend
    if (m_sameClientToSameBackend)
    {
        auto ipHash = std::hash<uint32_t>{}(pReq->getPeerAddr().ipNetEndian()) % clientsVector.size();
        index = (ipHash + (++(*m_clientIndex))) % clientsVector.size();
    }
    else if (!m_sameClientToSameBackend)
    {
        index = ++(*m_clientIndex) % clientsVector.size();
    }

    // http client pointer
    auto &pClient = clientsVector[index];

    if (!pClient && requestFound)
    {
        const auto &backend = m_backends[index_be];
        const auto &address = backend.internal_proxies[index % backend.internal_proxies.size()];

        pClient = HttpClient::newHttpClient(
            address,
            trantor::EventLoop::getEventLoopOfCurrentThread(),
            backend.ssl_conf.use_old_tls,
            backend.ssl,
            Version::kHttp2
        );
    }
    else
    {
        // NOTE:
        // this section is to
        // expecting something not right
        // to prevent subdomain and same path confiction
        // if this else section remove, there's something wrong with the request

        const auto &backend = m_backends[index_be];
        const auto &address = backend.internal_proxies[index % backend.internal_proxies.size()];

        pClient = HttpClient::newHttpClient(
            address,
            trantor::EventLoop::getEventLoopOfCurrentThread(),
            backend.ssl_conf.use_old_tls,
            backend.ssl,
            Version::kHttp2
        );
    }

    // ssl assignment config
    if (m_backends[index_be].ssl)
    {
        const auto &backend = m_backends[index_be];
        const auto &sslConf = m_backends[index_be].ssl_conf;

        app().addListener("0.0.0.0", 80, true, sslConf.cert, sslConf.key, false, sslConf.configurations);
        app().addListener("0.0.0.0", 443, true, sslConf.cert, sslConf.key, false, sslConf.configurations);

        app().setDocumentRoot(backend.document_root);

        app().setSSLFiles(sslConf.cert, sslConf.key);
        app().setSSLConfigCommands(sslConf.configurations);

        pClient->setCertPath(sslConf.cert, sslConf.key);
        pClient->addSSLConfigs(sslConf.configurations);
    }

    pReq->setPassThrough(true);

    if (requestFound)
    {
        pClient->setPipeliningDepth(m_pipelining);

        redirect = m_backends[index_be].redirect;
        redirectUrl = m_backends[index_be].redirect_to;

        if (redirect)
        {
            auto pRequestRedirect = HttpResponse::newHttpResponse();

            aCallback(pRequestRedirect->newRedirectionResponse(redirectUrl, k307TemporaryRedirect));
            return;
        }

        // valid backend object helper for response
        auto validBackend = m_backends[index_be];

        pClient->sendRequest(pReq, [aCallback = std::move(aCallback), validBackend = std::move(validBackend)](ReqResult result, const HttpResponsePtr &pResp)
        {
            pResp->setPassThrough(true);

            switch (result)
            {
                case ReqResult::Ok:
                {
                    // add header if supplied
                    if (validBackend.header_add && validBackend.header_add_list_pair.size() >= 1)
                    {
                        for (auto &pair : validBackend.header_add_list_pair)
                        {
                            pResp->addHeader(pair.first, pair.second);
                        }
                    }
                    // give warn or error?

                    // add server check origin if supplied
                    // can be add manually from each backend before their response callback
                    if (validBackend.origin_whitelist_check)
                    {
                        pResp->addHeader("access-control-allow-origin", validBackend.origin_whitelist_check_data);
                    }

                    aCallback(pResp);
                }
                break;

                default:
                {
                    // just to make it sure and show the message when happen
                    pResp->setBody("Internal Server Error By Default");
                    pResp->setStatusCode(k500InternalServerError);

                    aCallback(pResp);
                }
                break;
            }
        });
    }
    else
    {
        app().setDocumentRoot(m_defaultDocumentRoot);

        auto errorResp = HttpResponse::newHttpViewResponse("drogon_framework_505");

        errorResp->setStatusCode(k500InternalServerError);

        aCallback(errorResp);
    }
}

ReverseProxy::ReverseProxy()
{
}

ReverseProxy::~ReverseProxy()
{
}

} // namespace drogon_framework

drogon version

Version: 1.9.4
Git commit: fb08813e5cc602964257a3340c5814cccd9a5249
Compilation:
  Compiler: c++
  Compiler ID: GNU
  Compilation flags: -O3 -DNDEBUG -std=c++20 -I/usr/include/jsoncpp -I/usr/local/include
Libraries:
  postgresql: yes  (pipeline mode: yes)
  mariadb: no
  sqlite3: yes
  ssl/tls backend: OpenSSL
  brotli: yes
  hiredis: no
  c-ares: yes
  yaml-cpp: no


end of [HttpAppFramework / HttpClientPtr ] change ssl configuration after/on plugin
@marty1885
Copy link
Member

I've watched your videos. In short you cannot. Drogon initializes everything and lock them down to read only when app().run() is called.

@prothegee
Copy link
Author

allright, I guess that enough.

clossing this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants