* :ref:`setting-webserver-allow-from`: Netmasks that are allowed to connect to the webserver (not relevant if :ref:`setting-webserver-address` is set to a UNIX domain socket).
* :ref:`setting-webserver-max-bodysize`: Maximum request/response body size in megabytes
* :ref:`setting-webserver-connection-timeout`: Request/response timeout in seconds
+* :ref:`setting-webserver-max-concurrent-connections`: Maximum number of allowed concurrent connections to the web server.
.. warning::
Maximum request/response body size in megabytes.
+.. _setting-webserver-max-concurrent-connections:
+
+``webserver-max-concurrent-connections``
+----------------------------------------
+.. versionadded:: 5.1.0
+
+- Integer
+- Default: 100
+
+Maximum number of allowed concurrent connections to the web server.
+
.. _setting-webserver-connection-timeout:
``webserver-connection-timeout``
circular_buffer.hh \
comment.hh \
communicator.cc communicator.hh \
+ connection-management.hh \
coverage.cc coverage.hh \
credentials.cc credentials.hh \
dbdnsseckeeper.cc \
axfr-retriever.cc \
base32.cc \
base64.cc base64.hh \
+ connection-management.hh \
credentials.cc credentials.hh \
dns.cc \
dns_random.hh \
::arg().set("webserver-allow-from", "Webserver/API access is only allowed from these subnets") = "127.0.0.1,::1";
::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
::arg().set("webserver-max-bodysize", "Webserver/API maximum request/response body size in megabytes") = "2";
+ ::arg().set("webserver-max-concurrent-connections", "Webserver/API maximum concurrent connections allowed") = "100";
::arg().set("webserver-connection-timeout", "Webserver/API request/response timeout in seconds") = "5";
::arg().setSwitch("webserver-hash-plaintext-credentials", "Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime") = "no";
#include "ixfrdist-stats.hh"
IXFRDistWebServer::IXFRDistWebServer(const ComboAddress& listenAddress, const NetmaskGroup& acl, const string& loglevel) :
- d_ws(std::make_unique<WebServer>(listenAddress.toString(), listenAddress.getPort()))
+ d_ws(std::make_unique<WebServer>(nullptr, listenAddress.toString(), listenAddress.getPort()))
{
d_ws->setACL(acl);
d_ws->setLogLevel(loglevel);
{
public:
AsyncWebServer(FDMultiplexer* fdm, const string& listenaddress, int port) :
- WebServer(listenaddress, port), d_fdm(fdm) {};
+ WebServer(nullptr, listenaddress, port), d_fdm(fdm) {};
void go();
private:
registerBareHandler(url, func, method);
}
-static void* WebServerConnectionThreadStart(const WebServer* webServer, const std::shared_ptr<Socket>& client)
+static void* WebServerConnectionThreadStart(WebServer* webServer, const std::shared_ptr<Socket>& client)
{
setThreadName("rec/webhndlr");
+
+ if (!webServer->registerConnection()) {
+ SLOG(g_log<<Logger::Error<<"Too many concurrent web server connections"<<endl,
+ webServer->d_slog->info(Logr::Error, "Too many concurrent web server connections"));
+ return nullptr;
+ }
+
const std::string msg = "Exception while serving a connection in main webserver thread";
try {
webServer->serveConnection(client);
SLOG(g_log<<Logger::Error<<"Unknown exception while serving a connection in main webserver thread"<<endl,
webServer->d_slog->info(Logr::Error, msg));
}
+
+ webServer->releaseConnection();
return nullptr;
}
return isOK;
}
-void WebServer::serveConnection(const std::shared_ptr<Socket>& client) const {
+void WebServer::serveConnection(const std::shared_ptr<Socket>& client) const
+{
const auto unique = getUniqueID();
const string logprefix = d_logprefix + to_string(unique) + " ";
}
}
-WebServer::WebServer(string listenaddress, int port) :
+WebServer::WebServer(std::shared_ptr<ConcurrentConnectionManager> ccm, string listenaddress, int port) :
+ d_ccm(ccm),
d_listenaddress(std::move(listenaddress)),
d_port(port),
d_server(nullptr),
Socket d_server_socket;
};
+#include "connection-management.hh"
+
class WebServer : public boost::noncopyable
{
public:
- WebServer(string listenaddress, int port);
+ WebServer(std::shared_ptr<ConcurrentConnectionManager> ccm, string listenaddress, int port);
virtual ~WebServer() = default;
void setSLog(Logr::log_t log)
void registerApiHandler(const string& url, const HandlerFunction& handler, const std::string& method = "", bool allowPassword=false);
void registerWebHandler(const string& url, const HandlerFunction& handler, const std::string& method = "");
+ bool registerConnection() {
+ if (!d_ccm) {
+ return true;
+ }
+ return d_ccm->registerConnection();
+ }
+ void releaseConnection() {
+ if (d_ccm) {
+ d_ccm->releaseConnection();
+ }
+ }
+
enum class LogLevel : uint8_t {
None = 0, // No logs from requests at all
Normal = 10, // A "common log format"-like line e.g. '127.0.0.1 "GET /apache_pb.gif HTTP/1.0" 200 2326'
void apiWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp, bool allowPassword);
void webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp);
+ std::shared_ptr<ConcurrentConnectionManager> d_ccm;
string d_listenaddress;
int d_port;
std::shared_ptr<Server> d_server;
{
if (arg().mustDo("webserver") || arg().mustDo("api")) {
- d_ws = std::make_unique<WebServer>(arg()["webserver-address"], arg().asNum("webserver-port"));
+ d_ws = std::make_unique<WebServer>(std::make_shared<ConcurrentConnectionManager>(arg().asNum("webserver-max-concurrent-connections")), arg()["webserver-address"], arg().asNum("webserver-port"));
if (g_slogStructured) {
d_ws->setSLog(g_slog->withName("webserver"));
}