Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion wish/cpp/src/h2_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,16 @@ void H2Server::AcceptConnCb(evconnlistener* listener,
int /*socklen*/,
void* ctx) {
H2Server* server = static_cast<H2Server*>(ctx);

event_base* base = evconnlistener_get_base(listener);

if (server->max_connections_ > 0 && server->active_sessions_count_ >= server->max_connections_) {
VLOG(1) << "H2Server: Max connections limit reached (" << server->max_connections_ << "). Rejecting connection.";

evutil_closesocket(fd);

return;
}

int one = 1;
int set_rv = setsockopt(fd,
IPPROTO_TCP,
Expand Down Expand Up @@ -145,6 +152,7 @@ void H2Server::AcceptConnCb(evconnlistener* listener,

return;
}
server->active_sessions_count_++;

// Send server connection preface (SETTINGS frame).
nghttp2_settings_entry iv[] = {
Expand Down Expand Up @@ -224,6 +232,9 @@ void H2Server::HandleSessionError(Session* sess) {
if (!sess) {
return;
}
if (sess->server && sess->server->active_sessions_count_ > 0) {
sess->server->active_sessions_count_--;
}
for (auto& [sid, info] : sess->incoming_streams) {
if (info.web_stream) {
info.web_stream->OnError();
Expand Down
7 changes: 7 additions & 0 deletions wish/cpp/src/h2_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

#include "nghttp2_web_stream.h"

constexpr size_t kDefaultMaxH2Connections = 10000;

// H2Server listens for plain (cleartext) HTTP/2 (h2c) connections and
// exposes each incoming web-stream stream to the caller.
class H2Server {
Expand All @@ -43,6 +45,8 @@ class H2Server {
bool Init();
int GetPort() const;
void SetOnStream(StreamCallback cb);
void SetMaxConnections(size_t max_connections) { max_connections_ = max_connections; }
size_t active_connections() const { return active_sessions_count_; }
int Run();

private:
Expand Down Expand Up @@ -129,6 +133,9 @@ class H2Server {
evconnlistener* listener_;

StreamCallback on_stream_;

size_t max_connections_ = kDefaultMaxH2Connections;
size_t active_sessions_count_ = 0;
};

#endif // WISH_CPP_SRC_H2_SERVER_H_
28 changes: 28 additions & 0 deletions wish/cpp/src/h2_server_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,31 @@ TEST_F(H2ServerTest, InvalidConnectionPrefaceCleanlyDeallocatesSession) {
close(client_fd);
event_base_loop(base_, EVLOOP_NONBLOCK);
}

TEST_F(H2ServerTest, MaxConnectionsLimitRejectsExtraConnections) {
H2Server server(base_, 0);
server.SetMaxConnections(1);
ASSERT_TRUE(server.Init());

sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(server.GetPort());
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);

// Connection 1
int fd1 = socket(AF_INET, SOCK_STREAM, 0);
ASSERT_GE(fd1, 0);
ASSERT_EQ(connect(fd1, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0);
event_base_loop(base_, EVLOOP_NONBLOCK);
EXPECT_EQ(server.active_connections(), 1u);

// Connection 2 (Should be rejected)
int fd2 = socket(AF_INET, SOCK_STREAM, 0);
ASSERT_GE(fd2, 0);
ASSERT_EQ(connect(fd2, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0);
event_base_loop(base_, EVLOOP_NONBLOCK);
EXPECT_EQ(server.active_connections(), 1u);

close(fd1);
close(fd2);
}
13 changes: 12 additions & 1 deletion wish/cpp/src/h2_tls_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,16 @@ void H2TlsServer::AcceptConnCb(evconnlistener* listener,
int /*socklen*/,
void* ctx) {
H2TlsServer* server = static_cast<H2TlsServer*>(ctx);

event_base* base = evconnlistener_get_base(listener);

if (server->max_connections_ > 0 && server->active_sessions_count_ >= server->max_connections_) {
VLOG(1) << "H2TlsServer: Max connections limit reached (" << server->max_connections_ << "). Rejecting connection.";

evutil_closesocket(fd);

return;
}

int one = 1;
int set_rv = setsockopt(fd,
IPPROTO_TCP,
Expand Down Expand Up @@ -175,6 +182,7 @@ void H2TlsServer::AcceptConnCb(evconnlistener* listener,

return;
}
server->active_sessions_count_++;

nghttp2_settings_entry iv[] = {
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
Expand Down Expand Up @@ -252,6 +260,9 @@ void H2TlsServer::HandleSessionError(Session* sess) {
if (!sess) {
return;
}
if (sess->server && sess->server->active_sessions_count_ > 0) {
sess->server->active_sessions_count_--;
}
for (auto& [sid, info] : sess->incoming_streams) {
if (info.web_stream) {
info.web_stream->OnError();
Expand Down
7 changes: 7 additions & 0 deletions wish/cpp/src/h2_tls_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
// H2TlsServer listens for TLS-encrypted HTTP/2 connections.
// ALPN "h2" is advertised so standard HTTP/2 clients can connect.
// mTLS is enforced (client certificates are required), matching TlsServer.
constexpr size_t kDefaultMaxH2TlsConnections = 10000;

class H2TlsServer {
public:
using StreamCallback = std::function<void(WebStream*)>;
Expand All @@ -45,6 +47,8 @@ class H2TlsServer {

bool Init();
void SetOnStream(StreamCallback cb);
void SetMaxConnections(size_t max_connections) { max_connections_ = max_connections; }
size_t active_connections() const { return active_sessions_count_; }
int Run();

private:
Expand Down Expand Up @@ -133,6 +137,9 @@ class H2TlsServer {
TlsContext tls_ctx_;

StreamCallback on_stream_;

size_t max_connections_ = kDefaultMaxH2TlsConnections;
size_t active_sessions_count_ = 0;
};

#endif // WISH_CPP_SRC_H2_TLS_SERVER_H_
9 changes: 9 additions & 0 deletions wish/cpp/src/plain_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ void PlainServer::AcceptConnCb(evconnlistener* listener,
event_base* base = evconnlistener_get_base(listener);
PlainServer* server = static_cast<PlainServer*>(ctx);

if (server->max_connections_ > 0 &&
(server->active_handshakes_.size() + server->active_streams_.size()) >= server->max_connections_) {
VLOG(1) << "Max connections limit reached (" << server->max_connections_ << "). Rejecting connection.";

evutil_closesocket(fd);

return;
}

int one = 1;
int set_opt_rv = setsockopt(fd,
IPPROTO_TCP,
Expand Down
6 changes: 6 additions & 0 deletions wish/cpp/src/plain_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
class ServerHandshake;
class BufferEventWebStream;

constexpr size_t kDefaultMaxConnections = 10000;

class PlainServer {
public:
using StreamCallback = std::function<void(WebStream*)>;
Expand All @@ -40,6 +42,8 @@ class PlainServer {

bool Init();
void SetOnStream(StreamCallback cb);
void SetMaxConnections(size_t max_connections) { max_connections_ = max_connections; }
size_t active_connections() const { return active_handshakes_.size() + active_streams_.size(); }
int Run();

private:
Expand All @@ -62,6 +66,8 @@ class PlainServer {

StreamCallback on_stream_;

size_t max_connections_ = kDefaultMaxConnections;

std::vector<std::unique_ptr<ServerHandshake>> active_handshakes_;
std::vector<std::unique_ptr<BufferEventWebStream>> active_streams_;
};
Expand Down
9 changes: 9 additions & 0 deletions wish/cpp/src/tls_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ void TlsServer::AcceptConnCb(evconnlistener* listener,
event_base* base = evconnlistener_get_base(listener);
TlsServer* server = static_cast<TlsServer*>(ctx);

if (server->max_connections_ > 0 &&
(server->active_handshakes_.size() + server->active_streams_.size()) >= server->max_connections_) {
VLOG(1) << "Max TLS connections limit reached (" << server->max_connections_ << "). Rejecting connection.";

evutil_closesocket(fd);

return;
}

int one = 1;
int set_opt_rv = setsockopt(fd,
IPPROTO_TCP,
Expand Down
6 changes: 6 additions & 0 deletions wish/cpp/src/tls_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
class ServerHandshake;
class BufferEventWebStream;

constexpr size_t kDefaultMaxTlsConnections = 10000;

class TlsServer {
public:
using StreamCallback = std::function<void(WebStream*)>;
Expand All @@ -45,6 +47,8 @@ class TlsServer {

bool Init();
void SetOnStream(StreamCallback cb);
void SetMaxConnections(size_t max_connections) { max_connections_ = max_connections; }
size_t active_connections() const { return active_handshakes_.size() + active_streams_.size(); }
int Run();

private:
Expand Down Expand Up @@ -73,6 +77,8 @@ class TlsServer {

StreamCallback on_stream_;

size_t max_connections_ = kDefaultMaxTlsConnections;

std::vector<std::unique_ptr<ServerHandshake>> active_handshakes_;
std::vector<std::unique_ptr<BufferEventWebStream>> active_streams_;
};
Expand Down