409 lines
14 KiB
C++
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
|