JD2022-TU1/main/unittests/core/HttpServer/HttpServer.cpp

409 lines
14 KiB
C++

#include "precompiled_unittests_core.h"
#include "core/HttpServer/HttpServer.h"
// windows only at the moment
#ifdef ITF_WINDOWS
#include "mongoose/custom/mongoose.h"
#include "HttpServer/WebSocket/WebSocketConnectionMock.h"
#include "HttpServer/WebSocket/WebSocketConnectionFactoryMock.h"
#include "HttpServer/HttpConnectionMock.h"
using ::testing::Return;
using ::testing::Invoke;
using ::testing::An;
using ::testing::SaveArg;
using ::testing::StrEq;
using ::testing::ElementsAreArray;
using ::testing::_;
namespace ITF
{
namespace
{
void fillOneHeaderField(const HttpConnection&, WebSocketConnection::HeaderFields& _headerFields)
{
_headerFields.push_back("Sec-WebSocket-Protocol: myProtocol");
}
} // namespace
class HttpServerUT : public HttpServer
{
public:
explicit HttpServerUT(HttpConnection* _mockHttpConn = nullptr)
: m_mockHttpConn(_mockHttpConn) {}
using HttpServer::onWsHandshake;
using HttpServer::onWsHandshakeReply;
using HttpServer::onWsConnect;
using HttpServer::onRequest;
using HttpServer::onClose;
using HttpServer::onPoll;
HttpConnection& getHttpConnection() { return m_mockHttpConn ? *m_mockHttpConn : HttpServer::getHttpConnection(); }
private:
HttpConnection* m_mockHttpConn;
};
//--------------------------
// onWsHandshake tests
//--------------------------
// test how onWsHandshake behaves when NO WsConnectionFactory is registered
TEST(HttpServer, test_onWsHandshake_NoWsConnectionFactory)
{
mg_connection conn;
conn.is_websocket = 1;
conn.status_code = 0;
int expectedStatusCode = 404;
int expectedRetVal = MG_FALSE;
HttpServerUT httpServer;
int retval = httpServer.onWsHandshake(&conn);
EXPECT_EQ(retval, expectedRetVal);
EXPECT_EQ(expectedStatusCode, conn.status_code);
}
// test how onWsHandshake behaves when ONE WsConnectionFactory is registered (accept)
TEST(HttpServer, test_onWsHandshake_WsConnectionFactoryAccepts)
{
mg_connection conn;
conn.is_websocket = 1;
conn.status_code = 0;
conn.connection_param = nullptr;
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, acquire()).Times(1);
::testing::StrictMock<WebSocketConnectionFactoryMock> wsConnectionFactory;
EXPECT_CALL(wsConnectionFactory, acceptConnection(_)).Times(1).WillRepeatedly(Return(&wsConnection));
int expectedStatusCode = 0;
int expectedRetVal = MG_TRUE;
HttpServerUT httpServer;
httpServer.registerWSConnectionFactory(&wsConnectionFactory);
int retval = httpServer.onWsHandshake(&conn);
EXPECT_EQ(retval, expectedRetVal);
EXPECT_EQ(expectedStatusCode, conn.status_code);
EXPECT_EQ(&wsConnection, reinterpret_cast<WebSocketConnection*>(conn.connection_param));
}
// test how onWsHandshake behaves when ONE WsConnectionFactory is registered (reject)
TEST(HttpServer, test_onWsHandshake_WsConnectionFactoryRejects)
{
mg_connection conn;
conn.is_websocket = 1;
conn.status_code = 0;
conn.connection_param = nullptr;
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
::testing::StrictMock<WebSocketConnectionFactoryMock> wsConnectionFactory;
EXPECT_CALL(wsConnectionFactory, acceptConnection(_)).Times(1).WillRepeatedly(Return(nullptr));
int expectedStatusCode = 404;
int expectedRetVal = MG_FALSE;
HttpServerUT httpServer;
httpServer.registerWSConnectionFactory(&wsConnectionFactory);
int retval = httpServer.onWsHandshake(&conn);
EXPECT_EQ(retval, expectedRetVal);
EXPECT_EQ(expectedStatusCode, conn.status_code);
}
// test how onWsHandshake behaves when TWO WsConnectionFactories are registered (second accepts)
TEST(HttpServer, test_onWsHandshake_SecondWsConnectionFactoryAccepts)
{
mg_connection conn;
conn.is_websocket = 1;
conn.status_code = 0;
conn.connection_param = nullptr;
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, acquire()).Times(1);
::testing::StrictMock<WebSocketConnectionFactoryMock> wsConnectionFactory1;
EXPECT_CALL(wsConnectionFactory1, acceptConnection(_)).Times(1).WillRepeatedly(Return(nullptr));
::testing::StrictMock<WebSocketConnectionFactoryMock> wsConnectionFactory2;
EXPECT_CALL(wsConnectionFactory2, acceptConnection(_)).Times(1).WillRepeatedly(Return(&wsConnection));
int expectedStatusCode = 0;
int expectedRetVal = MG_TRUE;
HttpServerUT httpServer;
httpServer.registerWSConnectionFactory(&wsConnectionFactory1);
httpServer.registerWSConnectionFactory(&wsConnectionFactory2);
int retval = httpServer.onWsHandshake(&conn);
EXPECT_EQ(retval, expectedRetVal);
EXPECT_EQ(expectedStatusCode, conn.status_code);
EXPECT_EQ(&wsConnection, reinterpret_cast<WebSocketConnection*>(conn.connection_param));
}
// test how onWsHandshake behaves when TWO WsConnectionFactories are registered (both reject)
TEST(HttpServer, test_onWsHandshake_AllWsConnectionFactoriesRejects)
{
mg_connection conn;
conn.is_websocket = 1;
conn.status_code = 0;
conn.connection_param = nullptr;
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
::testing::StrictMock<WebSocketConnectionFactoryMock> wsConnectionFactory1;
EXPECT_CALL(wsConnectionFactory1, acceptConnection(_)).Times(1).WillRepeatedly(Return(nullptr));
::testing::StrictMock<WebSocketConnectionFactoryMock> wsConnectionFactory2;
EXPECT_CALL(wsConnectionFactory2, acceptConnection(_)).Times(1).WillRepeatedly(Return(nullptr));
int expectedStatusCode = 404;
int expectedRetVal = MG_FALSE;
HttpServerUT httpServer;
httpServer.registerWSConnectionFactory(&wsConnectionFactory1);
httpServer.registerWSConnectionFactory(&wsConnectionFactory2);
int retval = httpServer.onWsHandshake(&conn);
EXPECT_EQ(retval, expectedRetVal);
EXPECT_EQ(expectedStatusCode, conn.status_code);
}
//--------------------------
// onWsHandshakeReply tests
//--------------------------
// test how onWsHanshake behaves when no header fields are added
TEST(HttpServer, test_onWsHandshakeReply_NoHeadersFieldsAdded)
{
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onWsHandshakeReply(_, _)).Times(1);
mg_connection conn;
conn.is_websocket = 1;
conn.status_code = 0;
conn.connection_param = &wsConnection;
HttpServerUT httpServer;
httpServer.onWsHandshakeReply(&conn);
}
// test how onWsHanshake behaves when a header field is added
TEST(HttpServer, test_onWsHandshakeReply_AHeaderFieldAdded)
{
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onWsHandshakeReply(_, _)).Times(1).WillRepeatedly(Invoke(fillOneHeaderField));
::testing::StrictMock<HttpConnectionMock> httpConnection;
EXPECT_CALL(httpConnection, vprintf(_, _)).Times(1);
mg_connection conn;
conn.is_websocket = 1;
conn.status_code = 0;
conn.connection_param = &wsConnection;
HttpServerUT httpServer(&httpConnection);
httpServer.onWsHandshakeReply(&conn);
}
//--------------------------
// onWsConnect tests
//--------------------------
// test how onWsConnect behaves
TEST(HttpServer, test_onConnect)
{
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onOpen()).Times(1);
mg_connection conn;
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
HttpServerUT httpServer;
httpServer.onWsConnect(&conn);
}
//--------------------------
// onRequest tests
//--------------------------
// test how onRequest behaves when it receives web socket frame containing 'text' data
// expected: one call to wsConnection.onMessage() should be made and the Text message
// used as a parameter of the call must match data in mg_connection
TEST(HttpServer, test_onRequestReceivesWsFrameWithText)
{
WebSocketConnection::WebSocketTextMessageSink textMsg;
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onMessage(An<WebSocketConnection::WebSocketTextMessageSink&>())).Times(1).WillOnce(SaveArg<0>(&textMsg));
char* msgText = "Hello world";
mg_connection conn;
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
conn.wsbits = 0x80 | WEBSOCKET_OPCODE_TEXT;
conn.content = msgText;
conn.content_len = strlen(msgText);
HttpServerUT httpServer;
httpServer.onRequest(&conn);
ASSERT_NE(nullptr, textMsg.get());
EXPECT_THAT(textMsg->getContent(), StrEq(msgText));
}
// test how onRequest behaves when it receives web socket frame containing 'binary' data
// expected: one call to wsConnection.onMessage() should be made and the Binary message
// used as a parameter of the call must match data in mg_connection
TEST(HttpServer, test_onRequestReceivesWsFrameWithBinary)
{
WebSocketConnection::WebSocketBinaryMessageSink binMsg;
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onMessage(An<WebSocketConnection::WebSocketBinaryMessageSink&>())).Times(1).WillOnce(SaveArg<0>(&binMsg));
u8 msgBin[] = { 0x11, 0x22, 0x56, 0xf3, 0x4a, 0xbf };
mg_connection conn;
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
conn.wsbits = 0x80 | WEBSOCKET_OPCODE_BINARY;
conn.content = reinterpret_cast<char*>(msgBin);
conn.content_len = ITF_ARRAY_SIZE(msgBin);
HttpServerUT httpServer;
httpServer.onRequest(&conn);
ASSERT_NE(nullptr, binMsg.get());
EXPECT_EQ(conn.content_len, binMsg->getContentLen());
EXPECT_EQ(0, memcmp(conn.content, binMsg->getContent(), conn.content_len));
}
// test how onRequest behaves when it receives web socket frame containing 'close' request
// expected: one call to wsConnection.onClose() should be made and the close message
// used as a parameter of the call must match data in mg_connection
TEST(HttpServer, test_onRequestReceivesWsFrameWithCloseRequest)
{
u8 msgClose[] = { 0x03, 0xE8, 'C', 'L', 'O', 'S', 'E', '_', 'N', 'O', 'R', 'M', 'A', 'L' };
const u16 code = 1000;
const char* reason = "CLOSE_NORMAL";
WebSocketConnection::WebSocketCloseMessageSink closeMsg;
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onMessage(An<WebSocketConnection::WebSocketCloseMessageSink&>())).Times(1).WillOnce(SaveArg<0>(&closeMsg));
mg_connection conn;
conn.content = reinterpret_cast<char*>(msgClose);
conn.content_len = ITF_ARRAY_SIZE(msgClose);
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
conn.wsbits = 0x80 | WEBSOCKET_OPCODE_CONNECTION_CLOSE;
HttpServerUT httpServer;
httpServer.onRequest(&conn);
ASSERT_NE(nullptr, closeMsg.get());
EXPECT_EQ(code, closeMsg->getCode());
EXPECT_THAT(closeMsg->getReason().cStr(), StrEq(reason));
}
// test how onRequest behaves when it receives web socket frame containing 'Pong' request
// expected: a call to wsConnection onPong should be made
TEST(HttpServer, test_onRequestReceivesWsFrameWithPongRequest)
{
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onPong(_)).Times(1);
mg_connection conn;
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
conn.wsbits = 0x80 | WEBSOCKET_OPCODE_PONG;
conn.content = nullptr;
conn.content_len = 0;
HttpServerUT httpServer;
httpServer.onRequest(&conn);
}
// test how onRequest behaves when it receives web socket frame containing 'Ping' request
// expected: a call to wsConnection onPing should be made
TEST(HttpServer, test_onRequestReceivesWsFrameWithPingRequest)
{
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onPing(_)).Times(1);
mg_connection conn;
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
conn.wsbits = 0x80 | WEBSOCKET_OPCODE_PING;
conn.content = nullptr;
conn.content_len = 0;
HttpServerUT httpServer;
httpServer.onRequest(&conn);
}
// test how onRequest behaves when it receives web socket frame containing 'Continuation' request
// expected: no calls to wsConnection should be made
TEST(HttpServer, test_onRequestReceivesWsFrameWithContinuationRequest)
{
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
mg_connection conn;
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
conn.wsbits = 0x00 | WEBSOCKET_OPCODE_CONTINUATION;
conn.content = nullptr;
conn.content_len = 0;
HttpServerUT httpServer;
httpServer.onRequest(&conn);
}
//--------------------------
// onClose tests
//--------------------------
// test how HttpServer behaves when onClose event is raised
TEST(HttpServer, test_onClose_WsConnection)
{
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onClose()).Times(1);
EXPECT_CALL(wsConnection, release()).Times(1);
mg_connection conn;
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
conn.content = nullptr;
conn.content_len = 0;
HttpServerUT httpServer;
httpServer.onClose(&conn);
}
//--------------------------
// onPoll tests
//--------------------------
// test how HttpServer behaves when onPoll event is raised for web socket connection
TEST(HttpServer, test_onPoll_WsConnection)
{
::testing::StrictMock<WebSocketConnectionMock> wsConnection;
EXPECT_CALL(wsConnection, onPoll(_)).Times(1);
mg_connection conn;
conn.is_websocket = 1;
conn.connection_param = &wsConnection;
conn.content = nullptr;
conn.content_len = 0;
HttpServerUT httpServer;
httpServer.onPoll(&conn);
}
} // namespace ITF
#endif // ITF_WINDOWS