1232 lines
49 KiB
C++
1232 lines
49 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour //
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#include "tests/config.h"
|
|
#include "tests/common/cancelTest.h"
|
|
#include "tests/common/helpers.h"
|
|
#include "tests/common/restStatusCode.h"
|
|
#include "tests/fixture/authenticationFixture.h" // AsyncHelper
|
|
#include "tests/fixture/loginMockFixture.h"
|
|
#include "tests/fixture/memoryCheckFixture.h"
|
|
#include "tests/fixture/paramFixture.h"
|
|
#include "tests/mock/mockLogDevice.h"
|
|
#include "tests/mock/mockResultHelper.h"
|
|
#include "testsUnit/helpers/asyncResultBaseProxy.h"
|
|
#include "testsUnit/mock/mockHttpRequest.h"
|
|
#include <ubiservices/facadeInternal.h>
|
|
#include <ubiservices/version.h>
|
|
#include <ubiservices/localizationHelper.h>
|
|
#include <ubiservices/core/chrono/clockSystem.h>
|
|
#include <ubiservices/core/chrono/chronometer.h>
|
|
#include <ubiservices/core/configs/environmentHelper.h>
|
|
#include <ubiservices/core/configs/platformName.h>
|
|
#include <ubiservices/core/helpers/instancesManager.h>
|
|
#include <ubiservices/core/http/httpHeadersHelper.h>
|
|
#include <ubiservices/core/http/httpEngine.h>
|
|
#include <ubiservices/core/http/httpRequestContext.h>
|
|
#include <ubiservices/core/http/httpStringEntity.h>
|
|
#include <ubiservices/core/JSON/jsonWriter.h>
|
|
#include <ubiservices/core/network/URLInfo.h>
|
|
#include <ubiservices/core/types/string.h>
|
|
#include <ubiservices/services/parameters/parametersClientProxy.h>
|
|
#include <ubiservices/services/parameters/parametersInfo.h>
|
|
#include <ubiservices/services/parameters/parametersSettingsHttpGame.h>
|
|
#include <ubiservices/services/http/httpClient.h>
|
|
#include <ubiservices/services/http/httpProxyConfig.h>
|
|
#include <ubiservices/services/http/request/httpRequestIncludes.h>
|
|
#include <ubiservices/services/parameters/gatewayResources.h>
|
|
|
|
|
|
// #if !defined(US_RETAIL)
|
|
// extern bool US_DLL_API s_forceNonSecure;
|
|
// #endif
|
|
|
|
namespace US_NS {;
|
|
US_DEFINE_TEST(TestHttp);
|
|
|
|
template<uint32 facadesCount = 1, bool executeLogin = true, bool skipEalMemForLibcurl = false>
|
|
class AuthenticationFixtureHttp : public AuthenticationFixture<facadesCount, executeLogin>
|
|
{
|
|
public:
|
|
AuthenticationFixtureHttp<facadesCount, executeLogin, skipEalMemForLibcurl>() : AuthenticationFixture<facadesCount, executeLogin>()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
void SetUp() override
|
|
{
|
|
LeakChecker::m_skipEalMemForLibcurl = skipEalMemForLibcurl;
|
|
AuthenticationFixture<facadesCount, executeLogin>::SetUp();
|
|
}
|
|
};
|
|
|
|
typedef LeakChecker CORE_HTTP;
|
|
typedef LeakCheckerHttpClient CORE_HTTP_CLIENT;
|
|
typedef LeakCheckerHttpClientSkipLibCurlEalMem CORE_HTTP_CLIENT_NOEALMEM;
|
|
typedef LeakCheckerNoInit CORE_HTTP_NO_INIT;
|
|
typedef AuthenticationFixtureHttp<1, true, true> CORE_HTTP_CANCEL_LOGIN;
|
|
typedef AuthenticationFixtureHttp<2, false> CORE_HTTP_PROXY_X2;
|
|
|
|
typedef ParamFixture<LeakCheckerHttpClientSkipLibCurlEalMem, TestConfigCancel> CORE_HTTP_CANCEL;
|
|
US_DEFINE_LONG_TEST_P(CORE_HTTP_CANCEL, TestConfigCancel(100u), TestConfigCancel(20u));
|
|
|
|
typedef LeakCheckerNoInitWithParam<TestConfigCancel>::type CORE_HTTP_CANCEL_NO_INIT;
|
|
US_DEFINE_LONG_TEST_P(CORE_HTTP_CANCEL_NO_INIT, TestConfigCancel(200u), TestConfigCancel(20u));
|
|
|
|
TEST_F(CORE_HTTP, LOCAL_CheckProxyConfig)
|
|
{
|
|
const HttpProxyConfig defaultProxy;
|
|
EXPECT_STREQ("", defaultProxy.m_host.getUtf8());
|
|
EXPECT_EQ(0u, defaultProxy.m_port);
|
|
EXPECT_STREQ("", defaultProxy.m_username.getUtf8());
|
|
EXPECT_STREQ("", defaultProxy.m_password.getUtf8());
|
|
|
|
const String host("proxy.ubi.com");
|
|
const uint32 PORT = 9090;
|
|
const String username("player2");
|
|
const String password("Im 3l33t!");
|
|
|
|
const HttpProxyConfig proxyNoAuth(host, PORT);
|
|
EXPECT_EQ(host, proxyNoAuth.m_host);
|
|
EXPECT_EQ(PORT, proxyNoAuth.m_port);
|
|
EXPECT_STREQ("", proxyNoAuth.m_username.getUtf8());
|
|
EXPECT_STREQ("", proxyNoAuth.m_password.getUtf8());
|
|
|
|
const HttpProxyConfig proxyAuth(host, PORT, username, password);
|
|
EXPECT_EQ(host, proxyAuth.m_host);
|
|
EXPECT_EQ(PORT, proxyAuth.m_port);
|
|
EXPECT_EQ(username, proxyAuth.m_username);
|
|
EXPECT_EQ(password, proxyAuth.m_password);
|
|
|
|
const HttpProxyConfig copy(proxyAuth);
|
|
EXPECT_EQ(proxyAuth.m_host, copy.m_host);
|
|
EXPECT_EQ(proxyAuth.m_port, copy.m_port);
|
|
EXPECT_EQ(proxyAuth.m_username, copy.m_username);
|
|
EXPECT_EQ(proxyAuth.m_password, copy.m_password);
|
|
|
|
HttpProxyConfig copy2;
|
|
copy2 = copy;
|
|
EXPECT_EQ(copy.m_host, copy2.m_host);
|
|
EXPECT_EQ(copy.m_port, copy2.m_port);
|
|
EXPECT_EQ(copy.m_username, copy2.m_username);
|
|
EXPECT_EQ(copy.m_password, copy2.m_password);
|
|
}
|
|
|
|
TEST_F(CORE_HTTP, LOCAL_HttpHeader)
|
|
{
|
|
SAVE_ALLOC_COUNT(testCount);
|
|
{
|
|
HttpHeader headersSource;
|
|
|
|
headersSource["field1"] = "value1";
|
|
headersSource["field2"] = "value2";
|
|
|
|
EXPECT_TRUE(headersSource.hasHeader("field1"));
|
|
EXPECT_TRUE(headersSource.hasHeader("field2"));
|
|
|
|
EXPECT_EQ(2u, headersSource.getHeaderCount());
|
|
|
|
const String lines1 = "field1: value1";
|
|
const String lines2 = "field2: value2";
|
|
|
|
HttpHeader headersTarget;
|
|
|
|
EXPECT_STREQ("field1", headersTarget.addHeaderLine(lines1).getUtf8());
|
|
EXPECT_STREQ("field2", headersTarget.addHeaderLine(lines2).getUtf8());
|
|
|
|
EXPECT_EQ(lines1.getLength() + lines2.getLength(), headersTarget.getSize());
|
|
|
|
EXPECT_TRUE(headersTarget.hasHeader("field1"));
|
|
EXPECT_TRUE(headersTarget.hasHeader("field2"));
|
|
|
|
EXPECT_EQ(lines1, headersTarget.getLine("field1"));
|
|
EXPECT_EQ(headersSource.getLine("field1"), headersTarget.getLine("field1"));
|
|
EXPECT_EQ(lines2, headersTarget.getLine("field2"));
|
|
EXPECT_EQ(headersSource.getLine("field2"), headersTarget.getLine("field2"));
|
|
|
|
EXPECT_EQ(headersSource, headersTarget);
|
|
}
|
|
{
|
|
HttpHeader header;
|
|
EXPECT_EQ(-1, header.getContentLength());
|
|
header["Content-Length"] = "5";
|
|
EXPECT_EQ(5u, header.getContentLength());
|
|
}
|
|
{
|
|
HttpHeader header;
|
|
EXPECT_EQ(-1, header.getContentLength());
|
|
header["content-Length"] = "5";
|
|
EXPECT_EQ(5u, header.getContentLength());
|
|
}
|
|
{
|
|
HttpHeader caseInsentiveHeader;
|
|
const String value1 = "value1";
|
|
|
|
caseInsentiveHeader["field1"] = value1;
|
|
EXPECT_EQ(value1, caseInsentiveHeader.getValue("FIELD1"));
|
|
EXPECT_EQ(1u, caseInsentiveHeader.getHeaderCount());
|
|
|
|
caseInsentiveHeader["fiElD1"] = "value1";
|
|
EXPECT_EQ(value1, caseInsentiveHeader.getValue("field1"));
|
|
EXPECT_EQ(1u, caseInsentiveHeader.getHeaderCount());
|
|
|
|
EXPECT_TRUE(caseInsentiveHeader.hasHeader("field1"));
|
|
EXPECT_TRUE(caseInsentiveHeader.hasHeader("fiElD1"));
|
|
|
|
caseInsentiveHeader.addHeader("field2", "value2");
|
|
EXPECT_EQ(2u, caseInsentiveHeader.getHeaderCount());
|
|
caseInsentiveHeader.addHeader("FiELd2", "value2");
|
|
EXPECT_EQ(2u, caseInsentiveHeader.getHeaderCount());
|
|
|
|
EXPECT_TRUE(caseInsentiveHeader.hasHeader("field2"));
|
|
EXPECT_TRUE(caseInsentiveHeader.hasHeader("FiELd2"));
|
|
|
|
caseInsentiveHeader.removeHeader("fiElD1");
|
|
EXPECT_EQ(1u, caseInsentiveHeader.getHeaderCount());
|
|
caseInsentiveHeader.removeHeader("FiELd2");
|
|
EXPECT_EQ(0u, caseInsentiveHeader.getHeaderCount());
|
|
|
|
EXPECT_FALSE(caseInsentiveHeader.hasHeader("field2"));
|
|
EXPECT_FALSE(caseInsentiveHeader.hasHeader("FiELd2"));
|
|
}
|
|
{
|
|
HttpHeader headersSource;
|
|
EXPECT_EQ(0u, headersSource.getHeaderCount());
|
|
headersSource["field1"] = "value1";
|
|
EXPECT_EQ(1u, headersSource.getHeaderCount());
|
|
headersSource.addHeader("field1", "value1"); // redundant adding
|
|
EXPECT_EQ(1u, headersSource.getHeaderCount());
|
|
headersSource.addHeader("field2", "value2");
|
|
EXPECT_EQ(2u, headersSource.getHeaderCount());
|
|
headersSource.removeHeader("NotFound");
|
|
EXPECT_EQ(2u, headersSource.getHeaderCount());
|
|
headersSource.removeHeader("field1");
|
|
headersSource.removeHeader("field2");
|
|
EXPECT_EQ(0, headersSource.getHeaderCount());
|
|
}
|
|
|
|
EXPECT_ALLOC_COUNT_PC_X1_EQ(114u, 114, testCount);
|
|
}
|
|
|
|
#if defined(US_TARGET_WINDOWS)
|
|
#pragma warning(disable:4068) // unknown pragma
|
|
#pragma BullseyeCoverage save off
|
|
#pragma warning(default:4068)
|
|
#endif
|
|
|
|
/// @test Retrieves the user-agent sent from UbiServices and checks it is present and correct
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_CheckUserAgent)
|
|
{
|
|
Facade facade;
|
|
|
|
const HttpGet request(TestHelpers_BF::getHttpTestUrl("get"), TestHelpers_BF::getHttpBoxHeader());
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(request);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
const Json bodyReader(result.getResult().getBodyAsString());
|
|
ASSERT_TRUE(bodyReader.isValid());
|
|
|
|
const Json headersReader = bodyReader["headers"];
|
|
ASSERT_TRUE(headersReader.isValid());
|
|
|
|
const Json userAgentReader = headersReader["User-Agent"];
|
|
ASSERT_TRUE(userAgentReader.isValid());
|
|
ASSERT_TRUE(userAgentReader.isTypeString());
|
|
|
|
const String userAgent = userAgentReader.getValueString();
|
|
#if defined(US_SDK_S2S)
|
|
#if defined(US_UNITY3D)
|
|
const StringUrl expectedUserAgent = String::formatText("UbiServices_SDK_Unity_Server_" US_SDK_VERSION_CONST_CHAR "_%s", US_PLATFORM_NAME);
|
|
#else
|
|
const StringUrl expectedUserAgent = String::formatText("UbiServices_SDK_Server_" US_SDK_VERSION_CONST_CHAR "_%s", US_PLATFORM_NAME);
|
|
#endif
|
|
#elif defined(US_SDK_LIGHT_MOBILE)
|
|
#if defined(US_UNITY3D)
|
|
const StringUrl expectedUserAgent = String::formatText("UbiServices_SDK_Unity_Light_Mobile_" US_SDK_VERSION_CONST_CHAR "_%s", US_PLATFORM_NAME);
|
|
#else
|
|
const StringUrl expectedUserAgent = String::formatText("UbiServices_SDK_Light_Mobile_" US_SDK_VERSION_CONST_CHAR "_%s", US_PLATFORM_NAME);
|
|
#endif
|
|
#else //Full SDK
|
|
#if defined(US_UNITY3D)
|
|
const StringUrl expectedUserAgent = String::formatText("UbiServices_SDK_Unity_" US_SDK_VERSION_CONST_CHAR "_%s", US_PLATFORM_NAME);
|
|
#else
|
|
const StringUrl expectedUserAgent = String::formatText("UbiServices_SDK_" US_SDK_VERSION_CONST_CHAR "_%s", US_PLATFORM_NAME);
|
|
#endif
|
|
#endif
|
|
|
|
// Check if the string is found in the retrieved user-agent
|
|
// Avoid checking that the full string are equal as libraries may add some prefix or suffix (e.g. PS4)
|
|
EXPECT_NE(String::npos, userAgent.findSubstringCase(expectedUserAgent));
|
|
PRINT_LINE("User Agent: %s", userAgent.getAnsi().c_str());
|
|
}
|
|
|
|
/// Executes multiple (PARALLEL_COUNT) HTTP requests in parallel
|
|
/// (repeating the exercise EXECUTION_COUNT times)
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_ParalleleRequests)
|
|
{
|
|
const uint32 EXECUTION_COUNT = 10;
|
|
const uint32 PARALLEL_COUNT = 6;
|
|
|
|
Facade facade;
|
|
|
|
String data;
|
|
for(uint32 i = 0; i < 1000; i++)
|
|
{
|
|
data += "Test";
|
|
}
|
|
|
|
// Force UAT environment. Since there is no login, prod would be used every time
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
|
|
HttpHeader headers = TestHelpers_BF::getHttpBoxHeader();
|
|
headers["DummyData"] = data.getAnsi(); // Sending a large request
|
|
|
|
for (uint32 count = 0; count < EXECUTION_COUNT; count++)
|
|
{
|
|
Vector<AsyncResult<HttpResponse> > responses(PARALLEL_COUNT);
|
|
|
|
for(Vector<AsyncResult<HttpResponse> >::iterator it = responses.begin(); it != responses.end(); ++it)
|
|
{
|
|
*it = facade.getHttpClient().sendRequest(HttpGet(url, headers));
|
|
|
|
// FIXME, this does not validate the test. The requests are not truly made in parallel.
|
|
#if defined(US_TARGET_XONE)
|
|
Helper::sleep(250); // Arbitrary delay attempting to improve success (some requests time out...)
|
|
#endif
|
|
}
|
|
|
|
for(Vector<AsyncResult<HttpResponse> >::iterator it = responses.begin(); it != responses.end(); ++it)
|
|
{
|
|
EXPECT_TRUE((*it).wait());
|
|
US_EXPECT_SUCCESS((*it));
|
|
EXPECT_TRUE(!(*it).getResult().getBodyAsString().isEmpty());
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Same test as above, but for SSL requests
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_ParalleleRequestsSecure)
|
|
{
|
|
const uint32 EXECUTION_COUNT = 10;
|
|
const uint32 PARALLEL_COUNT = 6;
|
|
|
|
Facade facade;
|
|
|
|
String data;
|
|
for(uint32 i = 0; i < 1000; i++)
|
|
{
|
|
data += "Test";
|
|
}
|
|
|
|
HttpHeader headers = TestHelpers_BF::getHttpBoxHeader();
|
|
headers["DummyData"] = data.getAnsi(); //Sending a large request
|
|
const HttpGet request(TestHelpers_BF::getHttpTestUrl("get", String(), false, false), headers);
|
|
|
|
for (uint32 count = 0; count < EXECUTION_COUNT; count++)
|
|
{
|
|
{
|
|
Vector<AsyncResult<HttpResponse> > responses(PARALLEL_COUNT);
|
|
|
|
for(Vector<AsyncResult<HttpResponse> >::iterator it = responses.begin(); it != responses.end(); ++it)
|
|
{
|
|
*it = facade.getHttpClient().sendRequest(request);
|
|
}
|
|
|
|
for(Vector<AsyncResult<HttpResponse> >::iterator it = responses.begin(); it != responses.end(); ++it)
|
|
{
|
|
EXPECT_TRUE((*it).wait());
|
|
US_EXPECT_SUCCESS((*it));
|
|
EXPECT_TRUE(it->getResult().isSuccessStatusCode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if !defined(US_DLL)
|
|
/* Test the prefix of the the user agent */
|
|
TEST_F(CORE_HTTP_CLIENT, HTTP_GetUserAgentPrefix)
|
|
{
|
|
const String agent = HttpHelper::getUserAgent();
|
|
|
|
#if defined(US_SDK_S2S)
|
|
#if defined(US_UNITY3D)
|
|
EXPECT_TRUE(agent.findSubstringCase("UbiServices_SDK_Unity_Server_") == 0);
|
|
#else
|
|
EXPECT_TRUE(agent.findSubstringCase("UbiServices_SDK_Server_") == 0);
|
|
#endif //defined(US_UNITY3D)
|
|
#elif defined(US_SDK_LIGHT_MOBILE)
|
|
#if defined(US_UNITY3D)
|
|
EXPECT_TRUE(agent.findSubstringCase("UbiServices_SDK_Unity_Light_Mobile_") == 0);
|
|
#else
|
|
EXPECT_TRUE(agent.findSubstringCase("UbiServices_SDK_Light_Mobile_") == 0);
|
|
#endif
|
|
#else //Full Client SDK
|
|
#if defined(US_UNITY3D)
|
|
EXPECT_TRUE(agent.findSubstringCase("UbiServices_SDK_Unity_") == 0);
|
|
#else
|
|
EXPECT_TRUE(agent.findSubstringCase("UbiServices_SDK_") == 0);
|
|
#endif //defined(US_UNITY3D)
|
|
#endif //defined(US_SDK_S2S)
|
|
|
|
}
|
|
#endif
|
|
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_TrafficStatsBasicUpload)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpClient& client = facade.getHttpClient();
|
|
|
|
{
|
|
client.resetTrafficStats();
|
|
|
|
const HttpStatistics stats = client.getTrafficStats();
|
|
EXPECT_EQ(0u, stats.getTotalStatistics(HttpTrafficWay::Upload, HttpTrafficType::Http));
|
|
EXPECT_EQ(0u, stats.getTotalStatistics(HttpTrafficWay::Download, HttpTrafficType::Http));
|
|
EXPECT_EQ(0u, stats.getNbRequest(HttpTrafficType::Http));
|
|
}
|
|
|
|
const StringUrl testUrl = TestHelpers_BF::getHttpTestUrl("put");
|
|
const String testBody = "Look at me sexy body! - Fat Bastard";
|
|
HttpHeader testHeader = TestHelpers_BF::getHttpContentHeader();
|
|
testHeader["dummyKey"] = "dummyValue";
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(facade.getHttpClient().sendRequest(HttpPut(testUrl, testHeader, testBody))));
|
|
|
|
{
|
|
const HttpStatistics stats = client.getTrafficStats();
|
|
EXPECT_EQ(1u, stats.getNbRequest(HttpTrafficType::Http));
|
|
|
|
const uint32 testUploadUrl = stats.getStatistic(HttpTrafficWay::Upload, HttpTrafficType::Http, HttpTrafficStatistic::UrlSize);
|
|
const uint32 testUploadHeader = stats.getStatistic(HttpTrafficWay::Upload, HttpTrafficType::Http, HttpTrafficStatistic::HeaderSize);
|
|
const uint32 testUploadBody = stats.getStatistic(HttpTrafficWay::Upload, HttpTrafficType::Http, HttpTrafficStatistic::BodySize);
|
|
|
|
EXPECT_EQ(testUrl.getLength(), testUploadUrl);
|
|
EXPECT_EQ(testHeader.getSize(), testUploadHeader);
|
|
EXPECT_EQ(testBody.getLength(), testUploadBody);
|
|
EXPECT_EQ(testUploadUrl + testUploadHeader + testUploadBody, stats.getTotalStatistics(HttpTrafficWay::Upload, HttpTrafficType::Http));
|
|
}
|
|
|
|
{
|
|
client.resetTrafficStats();
|
|
|
|
const HttpStatistics stats = client.getTrafficStats();
|
|
EXPECT_EQ(0u, stats.getTotalStatistics(HttpTrafficWay::Upload, HttpTrafficType::Http));
|
|
EXPECT_EQ(0u, stats.getTotalStatistics(HttpTrafficWay::Download, HttpTrafficType::Http));
|
|
EXPECT_EQ(0u, stats.getNbRequest(HttpTrafficType::Http));
|
|
}
|
|
}
|
|
|
|
#if !defined(US_DLL)
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_TrafficStatsBasicDownload)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpClient& client = facade.getHttpClient();
|
|
client.resetTrafficStats();
|
|
|
|
const String receivedData("DUMMY_DATA.");
|
|
const size_t uReceivedDataSize = receivedData.getLength();
|
|
const String sReceivedDataSize = String::formatText("%d", uReceivedDataSize).getUtf8();
|
|
|
|
// by using the httpbox "bytes/11" url, the httpbox will send back 11 random bytes
|
|
const StringUrl testUrl = TestHelpers_BF::getHttpTestUrl(String(("bytes/") + sReceivedDataSize));
|
|
|
|
const AsyncResult<HttpResponse> requestAsync = facade.getHttpClient().sendRequest(HttpGet(testUrl, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(requestAsync.wait());
|
|
US_EXPECT_SUCCESS(requestAsync);
|
|
|
|
JsonWriter expectedResponseBody;
|
|
{
|
|
//innacurate but only the body size is important
|
|
expectedResponseBody["data"] = receivedData;
|
|
}
|
|
|
|
HttpHeader expectedResponseHeader;
|
|
{
|
|
//some of the headers values might be innacurate but only the header content size bothers.
|
|
expectedResponseHeader["Cache-Control"] = "no-cache";
|
|
expectedResponseHeader["Connection"] = "keep-alive";
|
|
expectedResponseHeader["Pragma"] = "no-cache";
|
|
expectedResponseHeader["Content-Type"] = "application/octet-stream";
|
|
expectedResponseHeader["Expires"] = "-1";
|
|
expectedResponseHeader["Server"] = "UbiServices Gateway /1.5.0";
|
|
expectedResponseHeader["Strict-Transport-Security"] = "max-age=63072000";
|
|
expectedResponseHeader["Ubi-Forwarded-By"] = "ue1-d-us-public-nginx-0cc428d17f577b72d msr-dev-us nginx02";
|
|
expectedResponseHeader["Ubi-transactionId"] = "e6a31c3e-ab29-4049-aa17-747e80c8d327";
|
|
expectedResponseHeader["X-AspNet-Version"] = "4.0.30319";
|
|
expectedResponseHeader["X-Powered-By"] = "ASP.NET";
|
|
expectedResponseHeader["X-Content-Type-Options"] = "nosniff";
|
|
expectedResponseHeader["X-Frame-Options"] = "DENY";
|
|
expectedResponseHeader["Date"] = "Fri, 31 Oct 2014 13:07:57 GMT";
|
|
expectedResponseHeader["Content-Length"] = sReceivedDataSize;
|
|
expectedResponseHeader["ETag"] = "\"786ce1d467b1f7d99e942ad95787b1082c990fc0\"";
|
|
|
|
EXPECT_EQ(expectedResponseHeader.getSize(), requestAsync.getResult().getAllHeader().getSize());
|
|
}
|
|
|
|
{
|
|
const HttpStatistics stats = client.getTrafficStats();
|
|
|
|
EXPECT_EQ(1u, stats.getNbRequest(HttpTrafficType::Http));
|
|
|
|
const uint32 testDownloadHeader = stats.getStatistic(HttpTrafficWay::Download, HttpTrafficType::Http, HttpTrafficStatistic::HeaderSize);
|
|
const uint32 testDownloadBody = stats.getStatistic(HttpTrafficWay::Download, HttpTrafficType::Http, HttpTrafficStatistic::BodySize);
|
|
const uint32 testDownloadedBytes = stats.getTotalStatistics(HttpTrafficWay::Download, HttpTrafficType::Http);
|
|
|
|
EXPECT_EQ(testDownloadHeader, expectedResponseHeader.getSize());
|
|
EXPECT_EQ(testDownloadBody, uReceivedDataSize);
|
|
EXPECT_EQ(testDownloadBody + testDownloadHeader, testDownloadedBytes);
|
|
}
|
|
|
|
client.resetTrafficStats();
|
|
}
|
|
#endif
|
|
|
|
/// @test Verifies that HTTP requests have all the same behavior when a HTTP timeout occurs
|
|
#if !defined(US_DLL)
|
|
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_HttpErrorCode)
|
|
{
|
|
using namespace testing;
|
|
|
|
struct Helper
|
|
{
|
|
static AssertionResult sendRequest(HttpClient& client, const HttpRequest& request, uint32 statusCode)
|
|
{
|
|
HttpRetryConfig retryConfig = HttpRetryConfig(0); /* Disable retry feature */
|
|
const AsyncResult<HttpResponse> result = client.sendRequest(request, retryConfig);
|
|
if (!result.wait())
|
|
{
|
|
return ::testing::AssertionFailure() << "Wait failed";
|
|
}
|
|
if (result.hasFailed())
|
|
{
|
|
return ::testing::AssertionFailure() << "Async result failed";
|
|
}
|
|
if (statusCode != result.getResult().getStatusCode())
|
|
{
|
|
return ::testing::AssertionFailure() << "Response status code is " << result.getResult().getStatusCode() << " , expected " << statusCode;
|
|
}
|
|
|
|
return AssertionSuccess();
|
|
}
|
|
|
|
static AssertionResult performHttpRequests(HttpClient& client, const HttpHeader& header, RestStatusCode::Enum statusCode)
|
|
{
|
|
{ // GET
|
|
const StringUrl url = TestHelpers_BF::getHttpStatusCodeTestUrl(statusCode);
|
|
const AssertionResult result = sendRequest(client, HttpGet(url, header), statusCode);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
{ // HEAD
|
|
const StringUrl url = TestHelpers_BF::getHttpStatusCodeTestUrl(statusCode);
|
|
const AssertionResult result = sendRequest(client, HttpHead(url, header), statusCode);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
{ // POST
|
|
const StringUrl url = TestHelpers_BF::getHttpStatusCodeTestUrl(statusCode);
|
|
const AssertionResult result = sendRequest(client, HttpPost(url, header, "body"), statusCode);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
{ // PUT
|
|
const StringUrl url = TestHelpers_BF::getHttpStatusCodeTestUrl(statusCode);
|
|
const AssertionResult result = sendRequest(client, HttpPut(url, header, "body"), statusCode);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
{ // DELETE
|
|
const StringUrl url = TestHelpers_BF::getHttpStatusCodeTestUrl(statusCode);
|
|
const AssertionResult result = sendRequest(client, HttpDelete(url, header, "body"), statusCode);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return AssertionSuccess();
|
|
}
|
|
};
|
|
|
|
Facade facade;
|
|
|
|
// JsonWriter writer;
|
|
// writer["delete some thing"] = 0;
|
|
// // On Xbox One, when we send data with a POST, PUT or DELETE and we receive an error from the server
|
|
// // like a 404. The HttpCallback::OnError method get called and in that case we do not have access
|
|
// // to the header and status code. It seems that since the data was not sent, the OnError method get called.
|
|
// // But if we have nothing to send, it works as expected.
|
|
// #if defined(US_TARGET_XONE)
|
|
// const String body;
|
|
// #else
|
|
// const String body = writer.renderContent();
|
|
// #endif
|
|
|
|
HttpClient& client = facade.getHttpClient();
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
EXPECT_TRUE(Helper::performHttpRequests(client, header, RestStatusCode::OK_202));
|
|
EXPECT_TRUE(Helper::performHttpRequests(client, header, RestStatusCode::Error400));
|
|
EXPECT_TRUE(Helper::performHttpRequests(client, header, RestStatusCode::Error403));
|
|
EXPECT_TRUE(Helper::performHttpRequests(client, header, RestStatusCode::Error500));
|
|
EXPECT_TRUE(Helper::performHttpRequests(client, header, RestStatusCode::Error503));
|
|
}
|
|
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_PerformHttpTimeOut)
|
|
{
|
|
const ClockSteady::TTimeNano TIMEOUT = 1000; // 1s
|
|
HttpRetryConfig retryConfig = HttpRetryConfig(0, HttpRetryConfig::RetryParam(), HttpRetryConfig::TimeoutParam(TIMEOUT));
|
|
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", String::formatText("delay=%i", 3000));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()), retryConfig);
|
|
EXPECT_TRUE(result.wait());
|
|
EXPECT_TRUE(result.hasFailed());
|
|
US_EXPECT_FAILURE(result, Errors::HTTP_TIMEOUT);
|
|
}
|
|
|
|
/// @test The download streaming should timeout properly when waiting for client to download next chunk
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_PerformHttpGETStreamTimeoutBeforeNextQuery)
|
|
{
|
|
// Reducing the timeout threshold since the default 60 sec is a bit long for the test.
|
|
const ClockSteady::TTimeNano TIMEOUT = 5000; // 5s
|
|
HttpRetryConfig retryConfig = HttpRetryConfig(0, HttpRetryConfig::RetryParam(), HttpRetryConfig::TimeoutParam(TIMEOUT));
|
|
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
const uint32 DUMMY_SIZE = 1000u;
|
|
|
|
HttpStreamContext context;
|
|
const HttpStreamPost request(url, DUMMY_SIZE, TestHelpers_BF::getHttpBoxHeader(), context);
|
|
AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(request, retryConfig);
|
|
|
|
// Do the streaming
|
|
while (result.isProcessing())
|
|
{
|
|
// Check notifications
|
|
HttpStreamNotification::Code notification = context.popNotification();
|
|
while (notification != HttpStreamNotification::None)
|
|
{
|
|
switch (notification)
|
|
{
|
|
case HttpStreamNotification::BufferToPush: //Do nothing!
|
|
case HttpStreamNotification::BufferToPop: //Do nothing!
|
|
case HttpStreamNotification::ContentLengthReady:
|
|
case HttpStreamNotification::None:
|
|
default:
|
|
break;
|
|
}
|
|
notification = context.popNotification();
|
|
}
|
|
Helper::sleep(50);
|
|
}
|
|
|
|
US_EXPECT_FAILURE(result, Errors::HTTP_TIMEOUT);
|
|
}
|
|
|
|
// NEED CHECK AS THE STATUS CODE CONF DISAPEARED
|
|
//// Test that the requests are automatically retried on configured codes : 500, 502, 503 and 504.
|
|
//typedef ParamFixture<CORE_HTTP_CLIENT, uint32> CORE_HTTP_RETRY_STATUS_CODE;
|
|
//INSTANTIATE_TEST_CASE_P(StatusCode, CORE_HTTP_RETRY_STATUS_CODE, ::testing::Values(500, 502, 503, 504));
|
|
//TEST_P(CORE_HTTP_RETRY_STATUS_CODE, HTTPBOX_PerformHttpRetryStatusCodes)
|
|
//{
|
|
// //HttpEngineConfig& httpEngineConfig = InstancesManager::getInstance()->getHttpEngine().getConfiguration();
|
|
// //httpEngineConfig.setMaxRequestRetry(1);
|
|
// //httpEngineConfig.setRetryInterval(1000);
|
|
// HttpRetryConfig retryConfig = HttpRetryConfig(1, HttpRetryConfig::RetryParam(1000));
|
|
//
|
|
// Facade facade;
|
|
//
|
|
// const StringUrl url = TestHelpers_BF::getHttpStatusCodeTestUrl(this->getParam());
|
|
//
|
|
// Set<uint32> httpCode;
|
|
// httpCode.insert(this->getParam());
|
|
//
|
|
// const AsyncResultBase result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader(), HttpRetryConfig(httpCode)));
|
|
// US_EXPECT_PROPERTY_WAIT(result, AsyncResultBase::isDelayed, true, 1500);
|
|
// US_EXPECT_PROPERTY_WAIT(result, AsyncResultBase::getRetryCount, static_cast<uint32>(1), 5000);
|
|
//
|
|
// EXPECT_TRUE(result.wait());
|
|
// EXPECT_TRUE(result.hasSucceeded()) << "The request should be considered successful even if the http code returned is not a 200.";
|
|
//
|
|
// // Retry count is reset to 0 once the async is complete
|
|
// EXPECT_EQ(0u, result.getRetryCount());
|
|
//}
|
|
|
|
/// @test Tests that retry count is 0 on some status codes
|
|
typedef ParamFixture<CORE_HTTP_CLIENT, RestStatusCode::Enum> CORE_HTTP_NO_RETRY_STATUS_CODE;
|
|
INSTANTIATE_TEST_CASE_P(StatusCode, CORE_HTTP_NO_RETRY_STATUS_CODE, ::testing::Values(RestStatusCode::OK, RestStatusCode::Error403, RestStatusCode::Error501, RestStatusCode::Error508));
|
|
TEST_P(CORE_HTTP_NO_RETRY_STATUS_CODE, HTTPBOX_PerformHttpNoRetryStatusCodes)
|
|
{
|
|
HttpRetryConfig retryConfig = HttpRetryConfig(1);
|
|
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpStatusCodeTestUrl(this->getParam());
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()), retryConfig);
|
|
EXPECT_TRUE(result.wait());
|
|
EXPECT_TRUE(result.hasSucceeded());
|
|
EXPECT_EQ(0u, AsyncResultBaseProxy::getRetryCount(result));
|
|
}
|
|
|
|
#endif // DLL
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Cancel tests
|
|
|
|
/// @test Cancels HTTP requests with various delays.
|
|
TEST_P(CORE_HTTP_CANCEL, HTTPBOX_CancelGet)
|
|
{
|
|
{
|
|
TestCanceler<> canceler(this->getParam());
|
|
while (!canceler.isComplete())
|
|
{
|
|
Facade facade;
|
|
HttpClient& client = facade.getHttpClient();
|
|
|
|
// getCount vs. getCompleteCountMax could bring issue since count may be > completeCountMax.
|
|
// getCompleteCount vs. getCompleteCountMax should not have any issue, but the progression bar may not progress during several steps
|
|
//PRINT_PROGRESS((canceler.m_testConfig.getCompleteCount() + 1), (canceler.m_testConfig.getCompleteCountMax() + 1), "Processing Time Interval: %d ms", canceler.m_testConfig.getTimeInterval());
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("get"));
|
|
EXPECT_TRUE(canceler.cancel(client.sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()))));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// @test Cancels HTTP requests with various delays.
|
|
/// (Normal cancel flow over secure since SSL does some more process)
|
|
TEST_P(CORE_HTTP_CANCEL, US_CancelGetSecure)
|
|
{
|
|
TestCanceler<> canceler(this->getParam());
|
|
while (!canceler.isComplete())
|
|
{
|
|
Facade facade;
|
|
HttpClient& client = facade.getHttpClient();
|
|
|
|
const String urlPath("https://uat-public-ubiservices.ubi.com/v1/applications/{applicationId}/configuration");
|
|
const StringUrl url(urlPath.replace("{applicationId}", NS_COMMON::Options::getApplicationId().c_str()));
|
|
EXPECT_TRUE(canceler.cancel(client.sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()))));
|
|
}
|
|
}
|
|
|
|
/// @test Cancels HTTP requests with various delays.
|
|
TEST_P(CORE_HTTP_CANCEL, HTTPBOX_CancelHead)
|
|
{
|
|
TestCanceler<> canceler(this->getParam());
|
|
while (!canceler.isComplete())
|
|
{
|
|
Facade facade;
|
|
HttpClient& client = facade.getHttpClient();
|
|
EXPECT_TRUE(canceler.cancel(client.sendRequest(HttpHead(TestHelpers_BF::getHttpTestUrl("head"), TestHelpers_BF::getHttpBoxHeader()))));
|
|
}
|
|
}
|
|
|
|
/// @test Cancels HTTP requests with various delays.
|
|
TEST_P(CORE_HTTP_CANCEL, HTTPBOX_CancelPost)
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("post"));
|
|
|
|
TestCanceler<> canceler(this->getParam());
|
|
while (!canceler.isComplete())
|
|
{
|
|
Facade facade;
|
|
HttpClient& client = facade.getHttpClient();
|
|
EXPECT_TRUE(canceler.cancel(client.sendRequest(HttpPost(url, TestHelpers_BF::getHttpContentHeader(), "In Soviet Russia, queries post you."))));
|
|
}
|
|
}
|
|
|
|
/// @test Cancels HTTP requests with various delays.
|
|
/// @note Disabled on XB1/2013-08 QFE2 until further investigation
|
|
/// (XHTTPRequest::Open failed: -2146697208 [0x800C0008])
|
|
/// (HttpCallback::OnError failed: 0x800C000E [INET_E_SECURITY_PROBLEM])
|
|
#if defined(US_TARGET_XONE) && (_XDK_VER >= 9837) // Fails on SDK August QFE2
|
|
TEST_P(CORE_HTTP_CANCEL, DISABLED_HTTPBOX_CancelPut) // TODO: REVIEW: Disabled for XONE
|
|
#else
|
|
TEST_P(CORE_HTTP_CANCEL, HTTPBOX_CancelPut)
|
|
#endif
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("put"));
|
|
|
|
TestCanceler<> canceler(this->getParam());
|
|
while (!canceler.isComplete())
|
|
{
|
|
Facade facade;
|
|
HttpClient& client = facade.getHttpClient();
|
|
EXPECT_TRUE(canceler.cancel(client.sendRequest(HttpPut(url, TestHelpers_BF::getHttpContentHeader(), "In Soviet Russia, queries delete you."))));
|
|
}
|
|
}
|
|
|
|
/// @test Cancels HTTP requests with various delays.
|
|
TEST_P(CORE_HTTP_CANCEL, HTTPBOX_CancelDelete)
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("delete"));
|
|
|
|
TestCanceler<> canceler(this->getParam());
|
|
while (!canceler.isComplete())
|
|
{
|
|
Facade facade;
|
|
HttpClient& client = facade.getHttpClient();
|
|
EXPECT_TRUE(canceler.cancel(client.sendRequest(HttpDelete(url, TestHelpers_BF::getHttpBoxHeader(), "In Soviet Russia, queries delete you."))));
|
|
}
|
|
}
|
|
|
|
/// @test Cancels HTTP requests with uninit
|
|
TEST_P(CORE_HTTP_CANCEL_NO_INIT, HTTPBOX_RequestInProgressUninit)
|
|
{
|
|
initializeSdk();
|
|
|
|
class UninitRequestGet : public CancelRequest
|
|
{
|
|
public:
|
|
UninitRequestGet() {}
|
|
virtual ~UninitRequestGet() {}
|
|
|
|
virtual AsyncResultBase launchRequest(Facade& facade)
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("get"));
|
|
return facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()));
|
|
}
|
|
};
|
|
|
|
{
|
|
TestCanceler<UninitNoLoginHttp> canceler(this->getParam());
|
|
|
|
UninitRequestGet getRequest;
|
|
while (!canceler.isComplete())
|
|
{
|
|
EXPECT_TRUE(canceler.cancel(getRequest));
|
|
}
|
|
}
|
|
|
|
uninitDefaultSDK();
|
|
}
|
|
|
|
/// This case targets a problematic URL. The server is reachable but the URL
|
|
/// cannot be resolved and hangs (invalid port).
|
|
TEST_P(CORE_HTTP_CANCEL, HTTPBOX_RequestTimeoutHostResolvedUrlHangs)
|
|
{
|
|
TestConfigCancel& config = this->getParam();
|
|
config.setMaxCount(5);
|
|
|
|
TestCanceler<> canceler(config);
|
|
while (!canceler.isComplete())
|
|
{
|
|
Facade facade;
|
|
HttpClient& client = facade.getHttpClient();
|
|
EXPECT_TRUE(canceler.cancel(client.sendRequest(HttpGet("http://msr-d-us-iis01.ubisoft.onbe:8001", TestHelpers_BF::getHttpBoxHeader()))));
|
|
}
|
|
}
|
|
|
|
/// @test Cancels HTTP requests with various delays.
|
|
/// This case targets a slow server. The server is reachable but URL takes time to answer.
|
|
TEST_P(CORE_HTTP_CANCEL, HTTPBOX_RequestTimeoutHostResolvedUrlDelayAnswer)
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("get", "delay=5"));
|
|
|
|
TestConfigCancel& config = this->getParam();
|
|
config.setMaxCount(5);
|
|
|
|
TestCanceler<> canceler(config);
|
|
while (!canceler.isComplete())
|
|
{
|
|
Facade facade;
|
|
HttpClient& client = facade.getHttpClient();
|
|
EXPECT_TRUE(canceler.cancel(client.sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()))));
|
|
}
|
|
}
|
|
|
|
/// @test Sends a long(delayed) request and then, send an immediate request
|
|
/// The first request shall still be in progress when the second is finished
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_NonBlockingRequests)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpClient& client = facade.getHttpClient();
|
|
|
|
const StringUrl urlDelayed(TestHelpers_BF::getHttpTestUrl("get", "delay=10"));
|
|
const StringUrl urlImmediate(TestHelpers_BF::getHttpTestUrl("get"));
|
|
|
|
AsyncResult<HttpResponse> resultDelayed = client.sendRequest(HttpGet(urlDelayed, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(resultDelayed.isProcessing());
|
|
|
|
US_TEST_NS::Helper::sleep(500); // Ensure the second request starts after the first one
|
|
|
|
AsyncResult<HttpResponse> resultImmediate = client.sendRequest(HttpGet(urlImmediate, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(resultDelayed.isProcessing());
|
|
|
|
EXPECT_TRUE(resultImmediate.wait());
|
|
EXPECT_TRUE(resultDelayed.isProcessing());
|
|
US_EXPECT_SUCCESS(resultImmediate);
|
|
EXPECT_TRUE(!resultImmediate.getResult().getBodyAsString().isEmpty());
|
|
|
|
EXPECT_TRUE(resultDelayed.isProcessing());
|
|
resultDelayed.cancel();
|
|
|
|
EXPECT_TRUE(resultDelayed.wait());
|
|
EXPECT_FALSE(resultDelayed.hasSucceeded());
|
|
}
|
|
|
|
/// @test Ensures that cancels are not blocking each other when several requests are pending
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_NonBlockingCancels)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpClient& client = facade.getHttpClient();
|
|
|
|
const StringUrl url1(TestHelpers_BF::getHttpTestUrl("get", "delay=3"));
|
|
const StringUrl url2(TestHelpers_BF::getHttpTestUrl("get", "delay=5"));
|
|
|
|
HighResolutionChrono chrono;
|
|
chrono.resetState();
|
|
AsyncResult<HttpResponse> result1 = client.sendRequest(HttpGet(url1, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(result1.isProcessing());
|
|
US_TEST_NS::Helper::sleep(500); // Ensure the second request starts after the first one
|
|
|
|
AsyncResult<HttpResponse> result2 = client.sendRequest(HttpGet(url2, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(result1.isProcessing());
|
|
US_TEST_NS::Helper::sleep(500); // Ensure the second request has started
|
|
|
|
result1.cancel();
|
|
|
|
// The cancel shall not wait for the first request to finish
|
|
EXPECT_LT(chrono.getElapsed(), ClockSteady::getNanoFromMilli(3000));
|
|
|
|
// The cancel shall not wait for the second request to finish
|
|
EXPECT_TRUE(result1.wait());
|
|
EXPECT_FALSE(result1.hasSucceeded());
|
|
EXPECT_LT(chrono.getElapsed(), ClockSteady::getNanoFromMilli(5000));
|
|
|
|
// Cancel the next requests to clean the test
|
|
result2.cancel();
|
|
EXPECT_TRUE(result2.wait());
|
|
}
|
|
|
|
#if !defined(US_DLL)
|
|
/// @test Validates that sending a request creation should not block the main thread.
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_NonBlockingMainThreadRequests)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpClient& client = facade.getHttpClient();
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("get"));
|
|
|
|
const HttpGet request(url, TestHelpers_BF::getHttpBoxHeader());
|
|
const uint32 MAX_ACTIVE_CALL = InstancesManager::getInstance()->getHttpEngine().getConfiguration().getMaxActiveRequest();
|
|
|
|
Vector<AsyncResult<HttpResponse> > responses(MAX_ACTIVE_CALL);
|
|
|
|
const uint32 LOOP_COUNT = 10;
|
|
for (uint32 i = 0; i < LOOP_COUNT; i++)
|
|
{
|
|
for(Vector<AsyncResult<HttpResponse> >::iterator it = responses.begin(); it != responses.end(); ++it)
|
|
{
|
|
HighResolutionChrono chrono;
|
|
*it = client.sendRequest(request);
|
|
#if defined(US_RETAIL)
|
|
EXPECT_LT(chrono.getElapsed(), DurationNano(DurationMilli(2))); // Tolerance delay
|
|
#else
|
|
EXPECT_LT(chrono.getElapsed(), DurationNano(DurationMilli(15))); // Sanity check since debug/release are very inaccurate
|
|
#endif
|
|
// Needed to ensure the current request will have been processed before the next request starts
|
|
while (!it->isProcessing())
|
|
{
|
|
Helper::sleep(1);
|
|
}
|
|
}
|
|
|
|
for(Vector<AsyncResult<HttpResponse> >::iterator it = responses.begin(); it != responses.end(); ++it)
|
|
{
|
|
EXPECT_TRUE(it->wait());
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined(US_DLL)
|
|
namespace HttpClientImpl_BF
|
|
{
|
|
extern uint32 s_MAX_CONCURRENT_US_CALLS;
|
|
}
|
|
|
|
#if !defined(US_SDK_S2S)
|
|
#if defined(US_TARGET_SWITCH)
|
|
// The SWITCH console requires to limit the number of concurrent HTTPS requests (custom or not).
|
|
TEST_F(CORE_HTTP_PROXY_X2, HTTPBOX_MaximumRequestsPerFacade)
|
|
#else
|
|
TEST_F(CORE_HTTP_PROXY_X2, HTTPBOX_Win32MaximumRequestsPerFacade)
|
|
#endif
|
|
{
|
|
const uint32 DELAY = 1;
|
|
|
|
// Ensure the requests will be launched in parallel by setting a two seconds delay
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("get", String::formatText("delay=%d", DELAY)));
|
|
|
|
Vector<AsyncResult<HttpResponse> > results;
|
|
for (uint32 i = 0; i < HttpClientImpl_BF::s_MAX_CONCURRENT_US_CALLS; i++)
|
|
{
|
|
for (uint32 f = 0; f < getInstancesCount(); f++)
|
|
{
|
|
results.push_back(getFacade(f).getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader())));
|
|
}
|
|
}
|
|
|
|
const uint32 CHECKS_PER_SECOND = 10;
|
|
uint32 done = 0;
|
|
#if defined(US_TARGET_SWITCH)
|
|
bool maxReached = false;
|
|
#endif
|
|
for (int x = 0; x < ((DELAY*10)*CHECKS_PER_SECOND) && done < HttpClientImpl_BF::s_MAX_CONCURRENT_US_CALLS * getInstancesCount(); x++)
|
|
{
|
|
US_TEST_NS::Helper::sleep(1000/CHECKS_PER_SECOND);
|
|
|
|
int delayed = 0;
|
|
done = 0;
|
|
|
|
for (Vector<AsyncResult<HttpResponse> >::iterator it = results.begin(); it != results.end(); ++it)
|
|
{
|
|
if (AsyncResultBaseProxy::isCurrentlyDelayed(*it))
|
|
delayed++;
|
|
if (it->hasSucceeded() || it->hasFailed())
|
|
done++;
|
|
}
|
|
#if defined(US_TARGET_SWITCH)
|
|
maxReached = maxReached || (delayed == HttpClientImpl_BF::s_MAX_CONCURRENT_US_CALLS);
|
|
#else
|
|
// Not useful, the final ASSERT should detect when MAX_CONCURRENT not reached
|
|
// ASSERT_EQ(0u, delayed);
|
|
#endif
|
|
}
|
|
ASSERT_EQ(HttpClientImpl_BF::s_MAX_CONCURRENT_US_CALLS * getInstancesCount(), done);
|
|
#if defined(US_TARGET_SWITCH)
|
|
ASSERT_TRUE(maxReached);
|
|
#endif
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
namespace testHttp_BF
|
|
{
|
|
void validateTimeOutTest(const AsyncResult<HttpResponse>& result, uint32 RETRY_COUNT,
|
|
DurationMilli TIMEOUT_INTERVAL_MILLI, DurationMilli TIMEOUT_INCREMENT_MILLI)
|
|
{
|
|
HighResolutionChrono chrono;
|
|
Vector<DurationMilli> deltas;
|
|
uint32 previousCount = 0;
|
|
|
|
DurationMilli startTime = ClockSteady::getTimeMilli();
|
|
|
|
while (result.isProcessing())
|
|
{
|
|
const uint32 count = AsyncResultBaseProxy::getRetryCount(result);
|
|
if (previousCount != count)
|
|
{
|
|
deltas.push_back(ClockSteady::getTimeMilli() - startTime);
|
|
previousCount = count;
|
|
startTime = ClockSteady::getTimeMilli();
|
|
}
|
|
US_TEST_NS::Helper::sleep(100u);
|
|
}
|
|
|
|
result.wait();
|
|
EXPECT_FALSE(result.hasSucceeded());
|
|
EXPECT_EQ(RETRY_COUNT, static_cast<uint32>(deltas.size())) << "Must match the max retry count in the parameters.";
|
|
EXPECT_EQ(result.getError().m_code, Errors::HTTP_TIMEOUT) << "The final error code should be a timeout.";
|
|
|
|
ASSERT_EQ(RETRY_COUNT, deltas.size());
|
|
for (uint32 i = 1; i < (RETRY_COUNT + 1); i++)
|
|
{
|
|
#if defined(US_TARGET_IOS)
|
|
const uint32 TOLERANCE = 150u;
|
|
#else
|
|
const uint32 TOLERANCE = 100u;
|
|
#endif
|
|
EXPECT_GE(deltas.at(i - 1), TIMEOUT_INTERVAL_MILLI + (i*(i - 1)*TIMEOUT_INCREMENT_MILLI) - TOLERANCE);
|
|
EXPECT_LT(deltas.at(i - 1), TIMEOUT_INTERVAL_MILLI + (i*(i - 0)*TIMEOUT_INCREMENT_MILLI) + TOLERANCE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// @test Checks that HTTP timeout is actually increased by the right value at each retry.
|
|
/// In this test, the timeout parameters should be taken from the local HTTP timeout config
|
|
TEST_F(CORE_HTTP_CLIENT, HTTPBOX_TestIncrementalTimeoutPerRequestConfig)
|
|
{
|
|
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
|
|
{
|
|
US_EXPECT_LOG("HttpTimeoutComponent request timeout after 0 sec", LogLevel::Error, Times(1));
|
|
US_EXPECT_LOG("HttpTimeoutComponent request timeout after 1 sec", LogLevel::Error, Times(1));
|
|
US_EXPECT_LOG("HttpTimeoutComponent request timeout after 2 sec", LogLevel::Error, Times(1));
|
|
US_EXPECT_LOG("HttpTimeoutComponent request timeout after 3 sec", LogLevel::Error, Times(1));
|
|
});
|
|
|
|
Facade facade;
|
|
|
|
const uint32 RETRY_COUNT = 3;
|
|
const DurationMilli TIMEOUT_INTERVAL = 500u;
|
|
const DurationMilli TIMEOUT_INCREMENT = 250u;
|
|
HttpRetryConfig retryConfig(RETRY_COUNT,
|
|
HttpRetryConfig::RetryParam(0, 0, 0),
|
|
HttpRetryConfig::TimeoutParam(TIMEOUT_INTERVAL, TIMEOUT_INCREMENT));
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", "delay=1000"); //delay so high we don't care about it.
|
|
testHttp_BF::validateTimeOutTest(facade.getHttpClient().sendRequest(
|
|
HttpGet(url), retryConfig), RETRY_COUNT, TIMEOUT_INTERVAL, TIMEOUT_INCREMENT);
|
|
}
|
|
|
|
#if defined(US_TARGET_WINDOWS)
|
|
/// @test Checks that HTTP timeout is actually increased by the right value at each retry
|
|
/// In this test, the timeout parameters should be taken from the custom HTTP timeout config
|
|
#if defined(US_SDK_S2S) || defined(US_CHINA_ACCOUNTS_SETUP_NOT_DONE)
|
|
/// @disabled Test disabled on S2S because the us-sdkServerSettingsHttpGame (or any server group) is not set up on the portal
|
|
TEST_F(CORE_HTTP_NO_INIT, DISABLED_US_TestIncrementalTimeoutFromConfigCustom)
|
|
#else
|
|
TEST_F(CORE_HTTP_NO_INIT, US_TestIncrementalTimeoutFromConfigCustom)
|
|
#endif
|
|
{
|
|
initializeSdk();
|
|
|
|
// This applicationId has the following HTTP custom config
|
|
// httpParam : {
|
|
// maxCount : 2,
|
|
// RetryParam : {
|
|
// initialDelayMsec : 0,
|
|
// incrementFactorMsec : 0,
|
|
// randomDelayMsec : 0
|
|
// },
|
|
// TimeoutParam : {
|
|
// initialDelayMsec : 1000,
|
|
// incrementFactorMsec : 1000
|
|
// }
|
|
// }
|
|
|
|
{
|
|
const GameConfig gameConfig(
|
|
ApplicationId(US_TEST_NS::APP_ID_US_SDK_SPECIAL_TESTS_PC),
|
|
NS_COMMON::Options::getApplicationBuildId().c_str()
|
|
#if defined(US_SDK_S2S)
|
|
, NS_COMMON::Options::environment()
|
|
#endif
|
|
, OnlineAccessContext::Standard
|
|
#if !defined(US_SDK_S2S)
|
|
, GameConfigEvent(String(), US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector<EventTypeInfo>())
|
|
#endif
|
|
);
|
|
configureSdk(gameConfig);
|
|
|
|
{
|
|
SmartPtr<FacadeInternal> facade(US_NEW(FacadeInternal));
|
|
#if defined(US_SDK_S2S)
|
|
AsyncResult<Empty> resultLogin = facade->createSession(S2S_SECRET, {}, {});
|
|
#else
|
|
AsyncResult<Empty> resultLogin = facade->createSession(GET_CREDENTIALS(), {}, {});
|
|
#endif
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
|
|
const ParametersSettingsHttpGame& httpCustomParam = ParametersClientProxy::getParametersInfo(*facade).m_paramsSettingsHttpGame;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", "delay=1000"); //delay so high we don't shouldn't care about it.
|
|
testHttp_BF::validateTimeOutTest(facade->getHttpClient().sendRequest(HttpGet(url)),
|
|
httpCustomParam.m_maxCount, httpCustomParam.m_timeoutInitialDelayMsec.getCount(),
|
|
httpCustomParam.m_timeoutIncrementFactorMsec.getCount());
|
|
}
|
|
}
|
|
|
|
US_TEST_NS::Helper::uninitializeSdk();
|
|
}
|
|
#endif
|
|
|
|
typedef ParamFixture<CORE_HTTP, RestStatusCode::Enum> CORE_HTTP_CLIENT_SERVICES_RETRY_CODE;
|
|
INSTANTIATE_TEST_CASE_P(StatusCode, CORE_HTTP_CLIENT_SERVICES_RETRY_CODE, ::testing::Values(
|
|
RestStatusCode::Error500, RestStatusCode::Error502, RestStatusCode::Error503, RestStatusCode::Error504));
|
|
|
|
/// @test Validates that request are retried when the service returns specific HTTP status codes.
|
|
TEST_P(CORE_HTTP_CLIENT_SERVICES_RETRY_CODE, LOCAL_UbiServicesRetryHandler)
|
|
{
|
|
const uint32 MAX_RETRY_COUNT = 3;
|
|
|
|
HttpRequestContext context = MockHttpRequest_BF::createDummyContext(HttpMethod::HTTP_POST, US_NEW(HttpStringEntity), US_NEW(HttpStringEntity));
|
|
context.setStatusCode(this->getParam());
|
|
const_cast<HttpRetryConfig&>(const_cast<HttpRetryHandler&>(context.getRetryHandler()).getRetryConfig()).m_retryCount = MAX_RETRY_COUNT;
|
|
|
|
const HttpRetryHandler handler(context.getRetryHandler().getRetryConfig(), true);
|
|
ASSERT_TRUE(handler.isValid(context));
|
|
|
|
uint32 retryCount = 0;
|
|
while (handler.processRequestRetry(context))
|
|
{
|
|
retryCount++;
|
|
}
|
|
|
|
EXPECT_EQ(retryCount, MAX_RETRY_COUNT);
|
|
}
|
|
|
|
/// @test No retry should be performed if the server returns a status code 503
|
|
TEST_F(CORE_HTTP, LOCAL_UbiServicesRetryHandlerRest503status5003)
|
|
{
|
|
const uint32 MAX_RETRY_COUNT = 3;
|
|
const String restErrorBody = MockResultHelper::createRestErrorResult(RestStatusCode::Error503, 5003u).getResult().getBodyAsString();
|
|
|
|
HttpStringEntity* responseData = US_NEW(HttpStringEntity);
|
|
responseData->setBody(restErrorBody);
|
|
|
|
HttpRequestContext context = MockHttpRequest_BF::createDummyContext(HttpMethod::HTTP_POST, US_NEW(HttpStringEntity), responseData);
|
|
const_cast<HttpRetryConfig&>(const_cast<HttpRetryHandler&>(context.getRetryHandler()).getRetryConfig()).m_retryCount = MAX_RETRY_COUNT;
|
|
context.setStatusCode(RestStatusCode::Error503);
|
|
|
|
const HttpRetryHandler handler(context.getRetryHandler().getRetryConfig(), true);
|
|
ASSERT_TRUE(handler.isValid(context));
|
|
|
|
uint32 retryCount = 0;
|
|
while (handler.processRequestRetry(context))
|
|
{
|
|
retryCount++;
|
|
}
|
|
|
|
EXPECT_EQ(0u, retryCount) << "We shouldn't retry on a services with HTTP 503 code with a rest code 5003.";
|
|
}
|
|
|
|
typedef ParamFixture<CORE_HTTP, RestStatusCode::Enum> CORE_HTTP_CLIENT_SERVICES_NO_RETRY_CODE;
|
|
INSTANTIATE_TEST_CASE_P(StatusCode, CORE_HTTP_CLIENT_SERVICES_NO_RETRY_CODE, ::testing::Values(
|
|
RestStatusCode::Error400,RestStatusCode::Error401, RestStatusCode::Error402, RestStatusCode::Error403,
|
|
RestStatusCode::Error404, RestStatusCode::Error405, RestStatusCode::Error407, RestStatusCode::Error409,
|
|
RestStatusCode::Error413, RestStatusCode::Error414, RestStatusCode::Error418, RestStatusCode::Error429));
|
|
|
|
/// @test Validates that request are not retried when the service returns specific HTTP status codes.
|
|
TEST_P(CORE_HTTP_CLIENT_SERVICES_NO_RETRY_CODE, LOCAL_UbiServicesRetryHandler)
|
|
{
|
|
const uint32 MAX_RETRY_COUNT = 3;
|
|
HttpRequestContext context = MockHttpRequest_BF::createDummyContext(HttpMethod::HTTP_POST, US_NEW(HttpStringEntity), US_NEW(HttpStringEntity));
|
|
const_cast<HttpRetryConfig&>(const_cast<HttpRetryHandler&>(context.getRetryHandler()).getRetryConfig()).m_retryCount = MAX_RETRY_COUNT;
|
|
context.setStatusCode(this->getParam());
|
|
|
|
const HttpRetryHandler handler(context.getRetryHandler().getRetryConfig(), true);
|
|
ASSERT_TRUE(handler.isValid(context));
|
|
|
|
uint32 retryCount = 0;
|
|
while (handler.processRequestRetry(context))
|
|
{
|
|
retryCount++;
|
|
}
|
|
|
|
EXPECT_EQ(0u, retryCount) << "No retry is expected on the http code : " << this->getParam();
|
|
}
|
|
|
|
#endif // US_DLL
|
|
|
|
#if defined(US_TARGET_WINDOWS)
|
|
#pragma warning(disable:4068) // unknown pragma
|
|
#pragma BullseyeCoverage restore
|
|
#pragma warning(default:4068)
|
|
#endif
|
|
|
|
};
|