mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-30 03:34:26 +03:00
Mar 21st 2024 update
This commit is contained in:
@@ -38,6 +38,10 @@ USE_DEBUG_FLAG(D_CONNECTION);
|
||||
static const HTTPResponse sending_timeout(HTTPStatusCode::HTTP_UNKNOWN, "Failed to send all data in time");
|
||||
static const HTTPResponse receving_timeout(HTTPStatusCode::HTTP_UNKNOWN, "Failed to receive all data in time");
|
||||
static const HTTPResponse parsing_error(HTTPStatusCode::HTTP_UNKNOWN, "Failed to parse the HTTP response");
|
||||
static const HTTPResponse close_error(
|
||||
HTTPStatusCode::HTTP_UNKNOWN,
|
||||
"The previous request failed to receive a response. Closing the connection"
|
||||
);
|
||||
|
||||
const string &
|
||||
MessageConnectionKey::getHostName() const
|
||||
@@ -125,6 +129,12 @@ public:
|
||||
return key;
|
||||
}
|
||||
|
||||
bool
|
||||
shouldCloseConnection() const
|
||||
{
|
||||
return should_close_connection;
|
||||
}
|
||||
|
||||
bool
|
||||
isOverProxy() const
|
||||
{
|
||||
@@ -152,12 +162,12 @@ public:
|
||||
|
||||
if (establishConnection().ok()) {
|
||||
dbgDebug(D_MESSAGING) << "Reestablish connection";
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgWarning(D_MESSAGING) << "Reestablish connection failed";
|
||||
active = genError(curr_time + chrono::seconds(300));
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
@@ -200,6 +210,7 @@ public:
|
||||
<< key.getPort()
|
||||
<< (isOverProxy() ? ", Over proxy: " + settings.getProxyHost() + ":" + to_string(key.getPort()) : "");
|
||||
active = Maybe<void, chrono::seconds>();
|
||||
should_close_connection = false;
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
@@ -443,25 +454,40 @@ private:
|
||||
|
||||
I_MainLoop *i_mainloop = Singleton::Consume<I_MainLoop>::by<Messaging>();
|
||||
I_TimeGet *i_time = Singleton::Consume<I_TimeGet>::by<Messaging>();
|
||||
auto conn_end_time = i_time->getMonotonicTime() + getConnectionTimeout();
|
||||
|
||||
auto bio_connect = BIO_do_connect(bio.get());
|
||||
uint attempts_count = 0;
|
||||
while (i_time->getMonotonicTime() < conn_end_time) {
|
||||
attempts_count++;
|
||||
if (BIO_do_connect(bio.get()) > 0) {
|
||||
if (isUnsecure() || isOverProxy()) return Maybe<void>();
|
||||
return performHandshakeAndVerifyCert(i_time, i_mainloop);
|
||||
}
|
||||
|
||||
auto conn_end_time = i_time->getMonotonicTime() + getConnectionTimeout();
|
||||
while (i_time->getMonotonicTime() < conn_end_time && bio_connect <= 0) {
|
||||
if (!BIO_should_retry(bio.get())) {
|
||||
auto curr_time = chrono::duration_cast<chrono::seconds>(i_time->getMonotonicTime());
|
||||
active = genError(curr_time + chrono::seconds(60));
|
||||
string bio_error = ERR_error_string(ERR_get_error(), nullptr);
|
||||
return genError("Failed to connect (BIO won't retry!): " + bio_error);
|
||||
return genError(
|
||||
"Failed to connect to: " +
|
||||
full_address +
|
||||
", error: " +
|
||||
bio_error +
|
||||
". Connection suspended for 60 seconds");
|
||||
}
|
||||
|
||||
if ((attempts_count % 10) == 0) i_mainloop->yield(true);
|
||||
attempts_count++;
|
||||
if (!isBioSocketReady()) {
|
||||
i_mainloop->yield((attempts_count % 10) == 0);
|
||||
continue;
|
||||
}
|
||||
bio_connect = BIO_do_connect(bio.get());
|
||||
}
|
||||
|
||||
return genError("Failed to establish new connection to " + full_address + " after reaching timeout.");
|
||||
if (bio_connect > 0) {
|
||||
if (isUnsecure() || isOverProxy()) return Maybe<void>();
|
||||
return performHandshakeAndVerifyCert(i_time, i_mainloop);
|
||||
}
|
||||
auto curr_time = chrono::duration_cast<chrono::seconds>(i_time->getMonotonicTime());
|
||||
active = genError(curr_time + chrono::seconds(60));
|
||||
return genError(
|
||||
"Failed to establish new connection to: " +
|
||||
full_address +
|
||||
" after reaching timeout." +
|
||||
" Connection suspended for 60 seconds");
|
||||
}
|
||||
|
||||
Maybe<uint, HTTPResponse>
|
||||
@@ -544,6 +570,7 @@ private:
|
||||
Maybe<HTTPResponse, HTTPResponse>
|
||||
sendAndReceiveData(const string &request, bool is_connect)
|
||||
{
|
||||
dbgFlow(D_CONNECTION) << "Sending and receiving data";
|
||||
I_MainLoop *i_mainloop = Singleton::Consume<I_MainLoop>::by<Messaging>();
|
||||
while (lock) {
|
||||
i_mainloop->yield(true);
|
||||
@@ -551,6 +578,11 @@ private:
|
||||
lock = true;
|
||||
auto unlock = make_scope_exit([&] () { lock = false; });
|
||||
|
||||
if (should_close_connection) {
|
||||
dbgWarning(D_CONNECTION) << close_error.getBody();
|
||||
return genError(close_error);
|
||||
}
|
||||
|
||||
I_TimeGet *i_time = Singleton::Consume<I_TimeGet>::by<Messaging>();
|
||||
auto sending_end_time = i_time->getMonotonicTime() + getConnectionTimeout();
|
||||
size_t data_left_to_send = request.length();
|
||||
@@ -567,9 +599,15 @@ private:
|
||||
HTTPResponseParser http_parser;
|
||||
dbgTrace(D_CONNECTION) << "Sent the message, now waiting for response";
|
||||
while (!http_parser.hasReachedError()) {
|
||||
if (i_time->getMonotonicTime() > receiving_end_time) return genError(receving_timeout);
|
||||
if (i_time->getMonotonicTime() > receiving_end_time) {
|
||||
should_close_connection = true;
|
||||
return genError(receving_timeout);
|
||||
};
|
||||
auto receieved = receiveData();
|
||||
if (!receieved.ok()) return receieved.passErr();
|
||||
if (!receieved.ok()) {
|
||||
should_close_connection = true;
|
||||
return receieved.passErr();
|
||||
}
|
||||
auto response = http_parser.parseData(*receieved, is_connect);
|
||||
if (response.ok()) {
|
||||
dbgTrace(D_MESSAGING) << printOut(response.unpack().toString());
|
||||
@@ -611,6 +649,7 @@ private:
|
||||
uint failed_attempts = 0;
|
||||
|
||||
bool lock = false;
|
||||
bool should_close_connection = false;
|
||||
};
|
||||
|
||||
Connection::Connection(const MessageConnectionKey &key, const MessageMetadata &metadata)
|
||||
@@ -663,6 +702,12 @@ Connection::getConnKey() const
|
||||
return pimpl->getConnKey();
|
||||
}
|
||||
|
||||
bool
|
||||
Connection::shouldCloseConnection() const
|
||||
{
|
||||
return pimpl->shouldCloseConnection();
|
||||
}
|
||||
|
||||
bool
|
||||
Connection::isOverProxy() const
|
||||
{
|
||||
|
@@ -53,6 +53,10 @@ public:
|
||||
{
|
||||
auto conn = persistent_connections.find(MessageConnectionKey(host_name, port, category));
|
||||
if (conn == persistent_connections.end()) return genError("No persistent connection found");
|
||||
if (conn->second.shouldCloseConnection()) {
|
||||
persistent_connections.erase(conn);
|
||||
return genError("The connection needs to reestablish");
|
||||
}
|
||||
return conn->second;
|
||||
}
|
||||
|
||||
@@ -93,6 +97,7 @@ private:
|
||||
if (!external_certificate.empty()) conn.setExternalCertificate(external_certificate);
|
||||
|
||||
auto connected = conn.establishConnection();
|
||||
persistent_connections.emplace(conn_key, conn);
|
||||
|
||||
if (!connected.ok()) {
|
||||
string connection_err = "Failed to establish connection. Error: " + connected.getErr();
|
||||
@@ -101,7 +106,6 @@ private:
|
||||
}
|
||||
|
||||
dbgTrace(D_CONNECTION) << "Connection establish succssesfuly";
|
||||
persistent_connections.emplace(conn_key, conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
@@ -23,83 +23,13 @@
|
||||
#include "mock/mock_encryptor.h"
|
||||
#include "rest.h"
|
||||
#include "rest_server.h"
|
||||
#include "dummy_socket.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
USE_DEBUG_FLAG(D_CONNECTION);
|
||||
|
||||
class DummySocket : Singleton::Consume<I_MainLoop>
|
||||
{
|
||||
public:
|
||||
~DummySocket()
|
||||
{
|
||||
if (server_fd != -1) close(server_fd);
|
||||
if (connection_fd != -1) close(connection_fd);
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
dbgAssert(server_fd >= 0) << "Failed to open a socket";
|
||||
int socket_enable = 1;
|
||||
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &socket_enable, sizeof(int));
|
||||
|
||||
struct sockaddr_in addr;
|
||||
bzero(&addr, sizeof(addr));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.sin_port = htons(8080);
|
||||
bind(server_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
|
||||
listen(server_fd, 100);
|
||||
}
|
||||
|
||||
void
|
||||
acceptSocket()
|
||||
{
|
||||
if (connection_fd == -1) connection_fd = accept(server_fd, nullptr, nullptr);
|
||||
}
|
||||
|
||||
string
|
||||
readFromSocket()
|
||||
{
|
||||
acceptSocket();
|
||||
|
||||
string res;
|
||||
char buffer[1024];
|
||||
while (int bytesRead = readRaw(buffer, sizeof(buffer))) {
|
||||
res += string(buffer, bytesRead);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
writeToSocket(const string &msg)
|
||||
{
|
||||
acceptSocket();
|
||||
EXPECT_EQ(write(connection_fd, msg.data(), msg.size()), msg.size());
|
||||
}
|
||||
|
||||
private:
|
||||
int
|
||||
readRaw(char *buf, uint len)
|
||||
{
|
||||
struct pollfd s_poll;
|
||||
s_poll.fd = connection_fd;
|
||||
s_poll.events = POLLIN;
|
||||
s_poll.revents = 0;
|
||||
|
||||
if (poll(&s_poll, 1, 0) <= 0 || (s_poll.revents & POLLIN) == 0) return 0;
|
||||
|
||||
return read(connection_fd, buf, len);
|
||||
}
|
||||
|
||||
int server_fd = -1;
|
||||
int connection_fd = -1;
|
||||
};
|
||||
|
||||
static ostream &
|
||||
operator<<(ostream &os, const BufferedMessage &)
|
||||
{
|
||||
@@ -179,7 +109,6 @@ TEST_F(TestConnectionComp, testEstablishNewConnection)
|
||||
conn_metadata.setExternalCertificate("external cert");
|
||||
|
||||
auto maybe_connection = i_conn->establishConnection(conn_metadata, MessageCategory::LOG);
|
||||
cout << "[OREN] read: " << endl;
|
||||
ASSERT_TRUE(maybe_connection.ok());
|
||||
auto get_conn = maybe_connection.unpack();
|
||||
EXPECT_EQ(get_conn.getConnKey().getHostName(), "127.0.0.1");
|
||||
@@ -228,6 +157,43 @@ TEST_F(TestConnectionComp, testSendRequest)
|
||||
EXPECT_EQ(dummy_socket.readFromSocket(), expected_msg);
|
||||
}
|
||||
|
||||
TEST_F(TestConnectionComp, testCloseConnectionBeforeResponse)
|
||||
{
|
||||
// Create a connection
|
||||
Flags<MessageConnectionConfig> conn_flags;
|
||||
conn_flags.setFlag(MessageConnectionConfig::UNSECURE_CONN);
|
||||
MessageMetadata conn_metadata("127.0.0.1", 8080, conn_flags);
|
||||
|
||||
// Insert the connection to the map
|
||||
auto maybe_connection = i_conn->establishConnection(conn_metadata, MessageCategory::LOG);
|
||||
ASSERT_TRUE(maybe_connection.ok());
|
||||
|
||||
// Get the connection from the map - Should be successful
|
||||
auto maybe_get_connection = i_conn->getPersistentConnection("127.0.0.1", 8080, MessageCategory::LOG);
|
||||
ASSERT_TRUE(maybe_get_connection.ok());
|
||||
auto conn = maybe_get_connection.unpack();
|
||||
|
||||
auto req = HTTPRequest::prepareRequest(conn, HTTPMethod::POST, "/test", conn_metadata.getHeaders(), "test-body");
|
||||
ASSERT_TRUE(req.ok());
|
||||
|
||||
// force the connection to be closed
|
||||
ON_CALL(mock_mainloop, yield(false)).WillByDefault(InvokeWithoutArgs([&] () { return; }));
|
||||
|
||||
EXPECT_CALL(mock_timer, getMonotonicTime())
|
||||
.WillRepeatedly(Invoke([] () {static int j = 0; return chrono::microseconds(++j * 1000 * 1000);}));
|
||||
|
||||
auto maybe_response = i_conn->sendRequest(conn, *req);
|
||||
ASSERT_TRUE(!maybe_response.ok());
|
||||
ASSERT_EQ(
|
||||
maybe_response.getErr().toString(),
|
||||
"[Status-code]: -1 - HTTP_UNKNOWN, [Body]: Failed to receive all data in time"
|
||||
);
|
||||
|
||||
auto maybe_get_closed_connection = i_conn->getPersistentConnection("127.0.0.1", 8080, MessageCategory::LOG);
|
||||
ASSERT_TRUE(!maybe_get_closed_connection.ok());
|
||||
ASSERT_EQ(maybe_get_closed_connection.getErr(), "The connection needs to reestablish");
|
||||
}
|
||||
|
||||
TEST_F(TestConnectionComp, testSendRequestReplyChunked)
|
||||
{
|
||||
Flags<MessageConnectionConfig> conn_flags;
|
||||
|
Reference in New Issue
Block a user