326 lines
No EOL
15 KiB
C++
326 lines
No EOL
15 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour //
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#include "tests/config.h"
|
|
#include "tests/common/helpers.h"
|
|
#include "tests/fixture/memoryCheckFixture.h"
|
|
#include "testsUnit/http/ps4/mockHttpPs4Impl.h"
|
|
#include "testsUnit/mock/mockHttpRequest.h"
|
|
#include <ubiservices/core/configs/configpriv.h>
|
|
#include <ubiservices/core/http/httpEngineConfig.h>
|
|
#include <ubiservices/core/http/httpEntityReader.h>
|
|
#include <ubiservices/core/http/ps4/httpEnginePs4.h>
|
|
#include <ubiservices/core/http/ps4/httpRequestPs4.h>
|
|
#include <ubiservices/services/http/httpConfig.h>
|
|
|
|
#include <libhttp.h>
|
|
namespace US_NS {;
|
|
US_DEFINE_TEST(TestHttpEnginePS4);
|
|
};
|
|
|
|
#if !defined(US_DLL)
|
|
|
|
namespace US_NS {;
|
|
|
|
typedef LeakChecker HTTP_ENGINE_PS4_MOCK;
|
|
|
|
namespace TestHttpEnginePs4_BF
|
|
{
|
|
void checkKeepAlive(const String serializedHeaders, bool isKeepAliveExpected)
|
|
{
|
|
using namespace testing;
|
|
|
|
MockHttpPs4Impl* ps4Impl = US_NEW(MockHttpPs4Impl);
|
|
|
|
const SceInt32 sceHttpId = 3u;
|
|
MockHttpPs4Impl_BF::setEngineExpectations(*ps4Impl, sceHttpId);
|
|
|
|
{
|
|
HttpEnginePs4 enginePs4(ps4Impl);
|
|
EXPECT_TRUE(enginePs4.initialize(HttpEngineConfig()));
|
|
|
|
const SceInt32 sceConnectionId = 5u;
|
|
EXPECT_CALL(*ps4Impl, sceHttpCreateConnectionWithURL(_,_,_)).WillOnce(Return(sceConnectionId));
|
|
HttpRequestContext requestContext = MockHttpRequest_BF::createDummyContext();
|
|
const SceInt32 sceRequestId = 6u;
|
|
MockHttpPs4Impl_BF::setRequestInitExpectations(*ps4Impl, sceConnectionId, sceRequestId);
|
|
HttpRequestInternal* internalRequest = enginePs4.createRequest(requestContext);
|
|
|
|
HttpRequestPs4* request = reinterpret_cast<HttpRequestPs4*>(internalRequest);
|
|
EXPECT_CALL(*ps4Impl, sceHttpSendRequest(sceRequestId,_,_)).WillRepeatedly(Return(SCE_OK));
|
|
request->executeStep(); // stepSendRequest
|
|
|
|
EXPECT_CALL(*ps4Impl, sceHttpGetResponseContentLength(sceRequestId,_,_)).WillOnce(DoAll(
|
|
SetArgPointee<1>(SCE_HTTP_CONTENTLEN_EXIST), SetArgPointee<2>(0), Return(SCE_OK)));
|
|
request->executeStep(); // stepResponseContentLength
|
|
|
|
EXPECT_CALL(*ps4Impl, sceHttpGetAllResponseHeaders(sceRequestId,_,_)).WillOnce(DoAll(
|
|
SetArgPointee<1>(const_cast<char*>(serializedHeaders.getUtf8())), SetArgPointee<2>(serializedHeaders.getLength() + 1), Return(SCE_OK)));
|
|
EXPECT_CALL(*ps4Impl, sceHttpGetStatusCode(_,_)).WillOnce(Return(SCE_OK));
|
|
|
|
// The request should have been deleted in the previous step (all data have been downloaded)
|
|
// Ensure here that the request is not deleted after the last call to stepDownloadData
|
|
MockHttpPs4Impl_BF::setRequestReleaseExpectations(*ps4Impl, sceRequestId);
|
|
request->executeStep(); // stepGetAllResponseHeaders
|
|
request->executeStep(); // stepParseResponseHeaders
|
|
|
|
// Check that the connection is not using keep alive
|
|
EXPECT_EQ(isKeepAliveExpected, request->isConnectionAlive()) << "keepAlive status is wrong for headers : \"" << serializedHeaders << "\"";
|
|
|
|
// If keep alive is disabled, the connection shall be closed as soon as the request is released
|
|
// Otherwise, in this test, the connection is closed only when the engine is shut down (close all connections)
|
|
// So the call to sceHttpDeleteConnection is checked before and after the release of the request
|
|
if (isKeepAliveExpected)
|
|
{
|
|
EXPECT_CALL(*ps4Impl, sceHttpDeleteConnection(sceConnectionId)).Times(0);
|
|
}
|
|
else
|
|
{
|
|
EXPECT_CALL(*ps4Impl, sceHttpDeleteConnection(sceConnectionId)).WillOnce(Return(SCE_OK));
|
|
}
|
|
// Request is not delete automatically by the engine implementation but by HttpEngine components
|
|
enginePs4.releaseRequest(internalRequest);
|
|
|
|
if (isKeepAliveExpected)
|
|
{
|
|
EXPECT_CALL(*ps4Impl, sceHttpDeleteConnection(sceConnectionId)).WillOnce(Return(SCE_OK));
|
|
}
|
|
else
|
|
{
|
|
EXPECT_CALL(*ps4Impl, sceHttpDeleteConnection(sceConnectionId)).Times(0);
|
|
}
|
|
|
|
enginePs4.shutdown();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate request creation when sceHttpCreateConnectionWithURL succeeds
|
|
TEST_F(HTTP_ENGINE_PS4_MOCK, LOCAL_CreateRequestSuccess)
|
|
{
|
|
using namespace testing;
|
|
|
|
MockHttpPs4Impl* ps4Impl = US_NEW(MockHttpPs4Impl);
|
|
MockHttpPs4Impl_BF::setEngineExpectations(*ps4Impl);
|
|
|
|
{
|
|
HttpEnginePs4 enginePs4(ps4Impl);
|
|
EXPECT_TRUE(enginePs4.initialize(HttpEngineConfig()));
|
|
|
|
const SceInt32 connectionId = 1;
|
|
EXPECT_CALL(*ps4Impl, sceHttpCreateConnectionWithURL(_,_,_)).WillOnce(Return(connectionId));
|
|
|
|
MockHttpPs4Impl_BF::setRequestExpectations(*ps4Impl, connectionId);
|
|
HttpRequestContext requestContext = MockHttpRequest_BF::createDummyContext();
|
|
HttpRequestInternal* internalRequest = enginePs4.createRequest(requestContext);
|
|
|
|
// Request is not delete automatically by the engine implementation but by HttpEngine components
|
|
enginePs4.releaseRequest(internalRequest);
|
|
|
|
EXPECT_CALL(*ps4Impl, sceHttpDeleteConnection(connectionId)).WillOnce(Return(SCE_OK));
|
|
enginePs4.shutdown();
|
|
}
|
|
}
|
|
|
|
// Validate request creation when connection creation fails
|
|
TEST_F(HTTP_ENGINE_PS4_MOCK, LOCAL_CreateConnectionError)
|
|
{
|
|
using namespace testing;
|
|
|
|
MockHttpPs4Impl* ps4Impl = US_NEW(MockHttpPs4Impl);
|
|
MockHttpPs4Impl_BF::setEngineExpectations(*ps4Impl);
|
|
|
|
{
|
|
HttpEnginePs4 enginePs4(ps4Impl);
|
|
EXPECT_TRUE(enginePs4.initialize(HttpEngineConfig()));
|
|
|
|
EXPECT_CALL(*ps4Impl, sceHttpCreateConnectionWithURL(_,_,_)).WillOnce(Return(SCE_HTTP_ERROR_INSUFFICIENT_STACKSIZE));
|
|
HttpRequestContext requestContext = MockHttpRequest_BF::createDummyContext();
|
|
HttpRequestInternal* internalRequest = enginePs4.createRequest(requestContext);
|
|
|
|
// Make sure that the request is not created
|
|
EXPECT_EQ(nullptr, internalRequest);
|
|
|
|
enginePs4.shutdown();
|
|
}
|
|
}
|
|
|
|
|
|
// Validate request creation when the SCE Http API is initialized by the user
|
|
TEST_F(HTTP_ENGINE_PS4_MOCK, LOCAL_CreateRequestApiInitByUser)
|
|
{
|
|
using namespace testing;
|
|
|
|
const SceInt32 userHttpId = 8; // Arbitrary value (> 0)
|
|
HttpEngineConfigPs4 engineConfig;
|
|
engineConfig.setLibHttpContextId(userHttpId);
|
|
|
|
MockHttpPs4Impl* ps4Impl = US_NEW(MockHttpPs4Impl);
|
|
MockHttpPs4Impl_BF::setEngineExpectationsUserHttpId(*ps4Impl, userHttpId);
|
|
|
|
{
|
|
HttpEnginePs4 enginePs4(ps4Impl);
|
|
EXPECT_TRUE(enginePs4.initialize(engineConfig));
|
|
|
|
const SceInt32 connectionId = 1; // Arbitrary value (> 0)
|
|
EXPECT_CALL(*ps4Impl, sceHttpCreateConnectionWithURL(_,_,_)).WillOnce(Return(connectionId));
|
|
|
|
MockHttpPs4Impl_BF::setRequestExpectations(*ps4Impl, connectionId);
|
|
HttpRequestContext requestContext = MockHttpRequest_BF::createDummyContext();
|
|
HttpRequestInternal* internalRequest = enginePs4.createRequest(requestContext);
|
|
|
|
// Request is not delete automatically by the engine implementation but by HttpEngine components
|
|
enginePs4.releaseRequest(internalRequest);
|
|
|
|
EXPECT_CALL(*ps4Impl, sceHttpDeleteConnection(connectionId)).WillOnce(Return(SCE_OK));
|
|
enginePs4.shutdown();
|
|
}
|
|
}
|
|
|
|
// Validate parameters sent to the initialize of a request
|
|
TEST_F(HTTP_ENGINE_PS4_MOCK, LOCAL_CreateRequestInitializeParameters)
|
|
{
|
|
using namespace testing;
|
|
|
|
// Parameters that should be sent by the engine in the initialize method
|
|
const SceInt32 sceHttpId = 8; // Arbitrary value (> 0)
|
|
const SceInt32 sceConnectionId = 5; // Arbitrary value (> 0)
|
|
const uint32 maxBufferSize = 10u;
|
|
const bool validateCerts = false;
|
|
|
|
|
|
HttpEngineConfigPs4 engineConfig;
|
|
engineConfig.setTransferBufferSize(maxBufferSize);
|
|
engineConfig.setValidateCerts(validateCerts);
|
|
|
|
MockHttpPs4Impl* ps4Impl = US_NEW(MockHttpPs4Impl);
|
|
MockHttpPs4Impl_BF::setEngineExpectations(*ps4Impl, sceHttpId);
|
|
|
|
{
|
|
HttpEnginePs4 enginePs4(ps4Impl);
|
|
EXPECT_TRUE(enginePs4.initialize(engineConfig));
|
|
|
|
EXPECT_CALL(*ps4Impl, sceHttpCreateConnectionWithURL(_,_,_)).WillOnce(Return(sceConnectionId));
|
|
// Force an url that should be validated
|
|
const String requestBody = "You wouldn't have won if we'd beaten you";
|
|
HttpStringEntity* requestEntity = US_NEW(HttpStringEntity, requestBody);
|
|
HttpBinaryEntity* responseEntity = US_NEW(HttpBinaryEntity);
|
|
HttpRequestContext requestContext = MockHttpRequest_BF::createDummyContext(HttpMethod::HTTP_POST, requestEntity, responseEntity);
|
|
const String url = "https://ubiservices.ubi.com/";
|
|
requestContext.getRequestData().m_url = url;
|
|
|
|
// Request expectations
|
|
const SceInt32 sceRequestId = 6;
|
|
{
|
|
// Check the validateCerts parameter (url should be validated, but the validateCerts tells to avoid checking)
|
|
Expectation httpsDisable = EXPECT_CALL(*ps4Impl, sceHttpsEnableOption(sceConnectionId,SCE_HTTPS_FLAG_SERVER_VERIFY)).WillOnce(Return(SCE_OK));
|
|
// Check the connectionId parameter
|
|
Expectation createRequest = EXPECT_CALL(*ps4Impl, sceHttpCreateRequestWithURL2(sceConnectionId, _, StrEq(url.getUtf8()), _)).After(httpsDisable).WillOnce(Return(sceRequestId));
|
|
Expectation setNonBlock = EXPECT_CALL(*ps4Impl, sceHttpSetNonblock(sceRequestId,SCE_TRUE)).After(createRequest).WillOnce(Return(SCE_OK));
|
|
SceHttpEpollHandle eh = reinterpret_cast<SceHttpEpollHandle>(1); // arbitrary value to ensure eh will not be null
|
|
// Check the httpId parameter
|
|
Expectation createEpoll = EXPECT_CALL(*ps4Impl, sceHttpCreateEpoll(sceHttpId,_)).After(setNonBlock).WillOnce(DoAll(SetArgPointee<1>(eh), Return(SCE_OK)));
|
|
EXPECT_CALL(*ps4Impl, sceHttpSetEpoll(sceRequestId,_,nullptr)).After(createEpoll).WillOnce(Return(SCE_OK));
|
|
|
|
}
|
|
|
|
HttpRequestInternal* internalRequest = enginePs4.createRequest(requestContext);
|
|
HttpRequestPs4* request = reinterpret_cast<HttpRequestPs4*>(internalRequest);
|
|
|
|
// Check maxBufferSize on upload
|
|
{
|
|
const uint32 requestContentLength = requestBody.getLength(); // Shall be above the maximum buffer size
|
|
uint32 bytesLeft = requestContentLength;
|
|
EXPECT_CALL(*ps4Impl, sceHttpSendRequest(sceRequestId,_,Le(maxBufferSize))).WillRepeatedly(Return(SCE_OK));
|
|
while (bytesLeft > 0)
|
|
{
|
|
request->executeStep(); // stepSendRequest
|
|
bytesLeft -= maxBufferSize;
|
|
}
|
|
}
|
|
|
|
// Get response headers
|
|
const uint32 responseContentLength = 30u; // Arbitrary value, but shall be above the maximum buffer size
|
|
{
|
|
EXPECT_CALL(*ps4Impl, sceHttpGetResponseContentLength(sceRequestId,_,_)).WillOnce(DoAll(
|
|
SetArgPointee<1>(SCE_HTTP_CONTENTLEN_EXIST), SetArgPointee<2>(responseContentLength), Return(SCE_OK)));
|
|
request->executeStep(); // stepResponseContentLength
|
|
|
|
StringStream responseHeadersStream;
|
|
responseHeadersStream << "HTTP/1.1 200 OK\r\nContent-Length: " << responseContentLength << "\r\n\r\n";
|
|
const String responseHeaders = responseHeadersStream.getContent();
|
|
EXPECT_CALL(*ps4Impl, sceHttpGetAllResponseHeaders(sceRequestId,_,_)).WillOnce(DoAll(
|
|
SetArgPointee<1>(const_cast<char*>(responseHeaders.getUtf8())), SetArgPointee<2>(responseHeaders.getLength() + 1), Return(SCE_OK)));
|
|
EXPECT_CALL(*ps4Impl, sceHttpGetStatusCode(sceRequestId,_)).WillOnce(Return(SCE_OK));
|
|
request->executeStep(); // stepResponseHeaders
|
|
request->executeStep(); // stepParseHeaders
|
|
}
|
|
|
|
// Check maxBufferSize on download
|
|
{
|
|
uint32 bytesLeft = responseContentLength;
|
|
MockHttpPs4Impl_BF::setRequestReleaseExpectations(*ps4Impl, sceRequestId);
|
|
while (bytesLeft > 0)
|
|
{
|
|
EXPECT_CALL(*ps4Impl, sceHttpReadData(sceRequestId,_,Le(maxBufferSize))).WillOnce(
|
|
Return(bytesLeft > maxBufferSize?maxBufferSize:bytesLeft));
|
|
request->executeStep(); // stepDownloadData
|
|
bytesLeft -= maxBufferSize;
|
|
}
|
|
}
|
|
|
|
// Request is not delete automatically by the engine implementation but by HttpEngine components
|
|
enginePs4.releaseRequest(internalRequest);
|
|
|
|
EXPECT_CALL(*ps4Impl, sceHttpDeleteConnection(sceConnectionId)).WillOnce(Return(SCE_OK));
|
|
enginePs4.shutdown();
|
|
}
|
|
}
|
|
|
|
/// @test Checks that the connection is closed when the server does not support keep-alive and kept open otherwise
|
|
TEST_F(HTTP_ENGINE_PS4_MOCK, LOCAL_KeepAlive)
|
|
{
|
|
{
|
|
// Http 1.0 without the keep-alive header
|
|
// => Connection should not use keep alive since Http 1.0 is not using keep-alive by default
|
|
const String serializedHeaders = "HTTP/1.0 200 OK\r\n\r\n";
|
|
TestHttpEnginePs4_BF::checkKeepAlive(serializedHeaders, false);
|
|
}
|
|
|
|
{
|
|
// Http 1.1 with the header connection close => Connection should not use keep-alive
|
|
const String serializedHeaders = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
|
|
TestHttpEnginePs4_BF::checkKeepAlive(serializedHeaders, false);
|
|
}
|
|
|
|
{
|
|
// Http 1.0 with keep alive header => Connection should use keep alive
|
|
const String serializedHeaders = "HTTP/1.0 200 OK\r\nConnection: keep-alive\r\n\r\n";
|
|
TestHttpEnginePs4_BF::checkKeepAlive(serializedHeaders, true);
|
|
}
|
|
|
|
{
|
|
// Http 1.1 => Connection uses keep alive by default
|
|
const String serializedHeaders = "HTTP/1.1 200 OK\r\n\r\n";
|
|
TestHttpEnginePs4_BF::checkKeepAlive(serializedHeaders, true);
|
|
}
|
|
|
|
{
|
|
// Http 1.1 with redundant keep-alive header => Connection should use keep alive
|
|
const String serializedHeaders = "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\n\r\n";
|
|
TestHttpEnginePs4_BF::checkKeepAlive(serializedHeaders, true);
|
|
}
|
|
|
|
{
|
|
// Http 1.0 with the header connection close
|
|
// => Connection should not use keep-alive since "close" header is ignored (not defined in Http 1.0)
|
|
// and default behavior in Http 1.0 is to disable the keep-alive
|
|
const String serializedHeaders = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
|
|
TestHttpEnginePs4_BF::checkKeepAlive(serializedHeaders, false);
|
|
}
|
|
}
|
|
|
|
|
|
};
|
|
|
|
#endif //#if !defined(US_DLL)
|