2600 lines
107 KiB
C++
2600 lines
107 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour //
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#include "tests/config.h"
|
|
#include "tests/fixture/authenticationFixture.h"
|
|
#include "tests/fixture/memoryCheckFixture.h"
|
|
#include "tests/fixture/paramFixture.h"
|
|
#include "tests/common/helpers.h"
|
|
#include "tests/common/restStatusCode.h"
|
|
#include "tests/mock/mockLogDevice.h"
|
|
#include "testsUnit/helpers/asyncResultBaseProxy.h"
|
|
#include <ubiservices/facade.h>
|
|
#include <ubiservices/core/errors/errorDetails.h>
|
|
#include <../private/ubiservices/core/extensions/staticAssert.h>
|
|
#include <ubiservices/core/JSON/jsonWriter.h>
|
|
#include <ubiservices/core/types/containers.h>
|
|
#include <ubiservices/core/types/string.h>
|
|
#include <ubiservices/core/types/dateTime.h>
|
|
#include <ubiservices/services/http/httpClient.h>
|
|
#include <ubiservices/services/http/request/httpRequestIncludes.h>
|
|
#include <ubiservices/services/parameters/parametersClient.h>
|
|
#include <../private/ubiservices/core/chrono/chronometer.h>
|
|
#include <../private/ubiservices/core/chrono/clockSystem.h>
|
|
#include <../private/ubiservices/core/configs/environmentHelper.h>
|
|
#include <../private/ubiservices/core/debug/systemChecker.h>
|
|
#include <../private/ubiservices/core/encoding/hmac.hpp>
|
|
#include <../private/ubiservices/core/encoding/base64.h>
|
|
#include <../private/ubiservices/core/http/httpObfuscatorHelper.h>
|
|
#include <../private/ubiservices/services/http/httpMultipartHelper.h>
|
|
#include <../private/ubiservices/services/http/request/httpRequestProxy.h>
|
|
#if defined(US_SDK_S2S)
|
|
#include <../private/ubiservices/services/parameters/gatewayResources.h>
|
|
#endif
|
|
|
|
#if !defined(US_DLL)
|
|
#include <../private/ubiservices/core/encoding/sha1.h>
|
|
#endif
|
|
#include <../private/ubiservices/core/helpers/streamOperators.h>
|
|
|
|
#include <usCommon/authenticationHelper.h>
|
|
#include <usCommon/firstPartyInit.h>
|
|
#include <usCommon/options.h>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
|
|
namespace US_NS {;
|
|
US_DEFINE_TEST(TestHttpClient);
|
|
// 'min' is defined as preprocessor in WinDef.h (and perhaps in other places)
|
|
// 'min' in '::std::min' is therefore replaced by the macro.
|
|
// Using the expression "(::std::min)" allows to prevent the macro replacement.
|
|
|
|
#if defined(US_SDK_S2S)
|
|
#define TEST_SERVICE_EVENTS_VERSION 4
|
|
#endif
|
|
|
|
#if !defined(US_RETAIL)
|
|
extern bool US_DLL_API s_forceNonSecure;
|
|
#endif
|
|
|
|
typedef LeakCheckerHttpClient HTTP_CLIENT;
|
|
typedef LeakCheckerHttpClientProxy HTTP_CLIENT_PROXY;
|
|
typedef LeakCheckerHttpClientNoInit HTTP_CLIENT_NO_INIT;
|
|
|
|
#if defined(US_TARGET_WINDOWS)
|
|
#pragma warning(disable:4068) // unknown pragma
|
|
#pragma BullseyeCoverage save off
|
|
#pragma warning(default:4068)
|
|
#endif
|
|
|
|
#if !defined(US_DLL)
|
|
#if defined(US_USE_OPENSSL)
|
|
namespace TestSslCert_BF
|
|
{
|
|
SSLPinningKeyList obfuscateSSLPinningKeys(const Vector<String>& sslPinningKeys, const SSLPinningKeyObfuscator& pinningKeysObfuscator);
|
|
extern const char* UBISERVICES_DEV_SSL_URL;
|
|
extern const char* UBISERVICES_UAT_PUBLIC_SSL_URL;
|
|
}
|
|
#endif // defined(US_USE_OPENSSL)
|
|
|
|
#if defined(US_SDK_S2S)
|
|
namespace TestExtendSession_BF
|
|
{
|
|
AuthenticationNotificationType waitForNotification(ListenerHandler<AuthenticationNotification>& listener,
|
|
DurationMicro waitNano);
|
|
}
|
|
|
|
#if !defined(DECLARE_TEST_EXTEND_SESSION_HELPER)
|
|
#define DECLARE_TEST_EXTEND_SESSION_HELPER
|
|
struct TestExtendSessionHelper
|
|
{
|
|
US_FRIEND_TEST(HTTP_CLIENT, HTTPBOX_TestSignatureSuccessAfterExtendSession);
|
|
static void reduceSessionDuration(Facade& facade, uint64 expirationTimeMs); // For test purposes ONLY
|
|
};
|
|
#endif // !defined(DECLARE_TEST_EXTEND_SESSION_HELPER)
|
|
#endif // defined(US_SDK_S2S)
|
|
#endif // !defined(US_DLL)
|
|
|
|
namespace InstancesManager_BF
|
|
{
|
|
// Decode Base64 string to obfuscated pinning key
|
|
void setObfuscatedSSLPinningKeys(SSLPinningKeyList& sslPinningObfuscatedKeys);
|
|
}
|
|
|
|
namespace TestHttpClient_BF
|
|
{
|
|
const char* const URL_INTERNET = "http://www.wolframalpha.com/";
|
|
|
|
#if defined(US_TARGET_WINDOWS) && !defined(US_DLL)
|
|
|
|
bool setRequestWithEmptyKeysAndObfuscator(HttpRequest& request, bool keyEmpty, bool obfuscatorEmpty)
|
|
{
|
|
// By default we have empty keys and obfuscator
|
|
SSLPinningKeyList keyList;
|
|
SSLPinningKeyObfuscator obfuscator;
|
|
// Later we change if required
|
|
if (!keyEmpty)
|
|
{
|
|
InstancesManager_BF::setObfuscatedSSLPinningKeys(keyList);
|
|
if (keyList.size() > 1)
|
|
{
|
|
// Keep only first item
|
|
keyList.erase(keyList.begin() + 1, keyList.end());
|
|
}
|
|
}
|
|
|
|
if (!obfuscatorEmpty)
|
|
{
|
|
obfuscator = HttpObfuscatorHelper::getObfuscator();
|
|
}
|
|
return request.setSSLPinningKeysAndObfuscator(keyList, obfuscator);;
|
|
}
|
|
|
|
bool setRequestWithCustomKeysAndObfuscator(HttpRequest& request, bool keyOK, bool obfuscatorOK, const uint32 selectedKeyIndex = 0) // 0 is default pinning key
|
|
{
|
|
size_t validKeyLength = HttpObfuscatorHelper::getObfuscatorLength();
|
|
|
|
// By default we have invalid keys and obfuscator
|
|
SSLPinningKeyList keyList;
|
|
Vector<uint8> key(validKeyLength - 10, 4); // shorter than valid key
|
|
SSLPinningKeyObfuscator obfuscator(validKeyLength - 10, 4); // shorter than valid obfuscator
|
|
|
|
// Later we change if required
|
|
if (keyOK)
|
|
{
|
|
SSLPinningKeyList validKeyList;
|
|
InstancesManager_BF::setObfuscatedSSLPinningKeys(validKeyList);
|
|
|
|
// Is index out of range ?
|
|
if (validKeyList.size() < selectedKeyIndex + 1)
|
|
{
|
|
return false;
|
|
}
|
|
keyList.push_back(validKeyList[selectedKeyIndex]);
|
|
}
|
|
else
|
|
{
|
|
keyList.push_back(key);
|
|
}
|
|
|
|
if (obfuscatorOK)
|
|
{
|
|
obfuscator = HttpObfuscatorHelper::getObfuscator();
|
|
}
|
|
return request.setSSLPinningKeysAndObfuscator(keyList, obfuscator);
|
|
}
|
|
#endif
|
|
|
|
void writeToFile(const String& fileName, const char* data, uint32 size)
|
|
{
|
|
std::ofstream file;
|
|
file.open(fileName.getBufferT().c_str(), std::ios::out | std::ios::binary);
|
|
|
|
file.write(data, size);
|
|
|
|
if ((file.rdstate() & std::ofstream::failbit) != 0)
|
|
{
|
|
std::cerr << _T("Error writing!!!");
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
String readTextFile(const String& fileName)
|
|
{
|
|
std::ifstream file;
|
|
file.open(fileName.getBufferT().c_str());
|
|
|
|
StringStream retVal;
|
|
|
|
if (file.is_open())
|
|
{
|
|
while(file.good())
|
|
{
|
|
std::string line;
|
|
getline(file, line);
|
|
retVal << line.c_str();
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
|
|
return retVal.getContent();
|
|
}
|
|
|
|
Vector<uint8> createUploadData(uint32 iteration)
|
|
{
|
|
EXPECT_GT(iteration, 0u);
|
|
|
|
Vector<uint8> data;
|
|
data.reserve(4*iteration);
|
|
|
|
for (; iteration > 0; iteration--)
|
|
{
|
|
data.push_back('t');
|
|
data.push_back('e');
|
|
data.push_back(0);
|
|
data.push_back('s');
|
|
data.push_back('t');
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
Vector<Json> getHeaderJsonFromHttpBoxBody(const String& body, const Vector<String> &headersToBeFound = Vector<String>())
|
|
{
|
|
Vector<Json> headersFound;
|
|
if (!body.isEmpty())
|
|
{
|
|
const Json reader(body);
|
|
if (reader.isValid() && reader.isTypeObject())
|
|
{
|
|
const Vector<Json>& items = reader["headers"].getItems();
|
|
headersFound.reserve(items.size());
|
|
|
|
if (!headersToBeFound.empty())
|
|
{
|
|
for (const auto& headerToBeFound : headersToBeFound)
|
|
{
|
|
const auto& it = find_if(items.begin(), items.end(), [&headerToBeFound](const Json& item) {
|
|
return headerToBeFound == item.getKey();
|
|
});
|
|
|
|
if (it != items.end())
|
|
{
|
|
headersFound.push_back(*it);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
headersFound = items;
|
|
}
|
|
}
|
|
|
|
}
|
|
return headersFound;
|
|
}
|
|
|
|
::testing::AssertionResult validateHttpBoxHeadersInBodyResponse(const String& requestBody, const Vector<String>& headersToValidate)
|
|
{
|
|
const Vector<Json>& jsonHeaders = getHeaderJsonFromHttpBoxBody(requestBody);
|
|
if(!jsonHeaders.empty())
|
|
{
|
|
for (const auto& headerToValidate : headersToValidate)
|
|
{
|
|
String errorString;
|
|
if (find_if(jsonHeaders.begin(), jsonHeaders.end(), [&headerToValidate, &errorString](const Json& item)
|
|
{
|
|
if (item.getKey() == headerToValidate)
|
|
{
|
|
if (item.getValueString().isEmpty())
|
|
{
|
|
errorString = headerToValidate + " was found on the HttpBox response, but it does not have a value set.";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
errorString = headerToValidate + " was not found on the httpBox response.";
|
|
return false;
|
|
}) == jsonHeaders.end())
|
|
{
|
|
return ::testing::AssertionFailure() << headerToValidate << " was not found on the HttpBox response.";
|
|
}
|
|
}
|
|
|
|
return ::testing::AssertionSuccess();
|
|
}
|
|
|
|
return ::testing::AssertionFailure() << "requestBody does not contain any header returned by HTTPBOX";
|
|
}
|
|
|
|
Vector<HttpMultipartData> createMultipartData()
|
|
{
|
|
const Vector<uint8> image = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x07, 0x08, 0x02, 0x00, 0x00, 0x00, 0xBE, 0xCE, 0x4B, 0x34, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, 0x61, 0x05, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, 0x6F, 0xA8, 0x64, 0x00, 0x00, 0x00, 0x12, 0x49, 0x44, 0x41, 0x54, 0x18, 0x57, 0x63, 0xE8, 0x60, 0x10, 0xC5, 0x83, 0x86, 0xAA, 0x34, 0x83, 0x28, 0x00, 0x53, 0x38, 0x2A, 0xEF, 0x36, 0xB0, 0x43, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 };
|
|
const StringA text("This is a multipart test string");
|
|
|
|
Vector<HttpMultipartData> multiparts;
|
|
multiparts.reserve(2);
|
|
multiparts.push_back(HttpMultipartData("image_data", image, "image/png", "test.png"));
|
|
multiparts.push_back(HttpMultipartData("text_data", Vector<uint8>(text.begin(), text.end())));
|
|
|
|
return multiparts;
|
|
}
|
|
|
|
#if defined(US_SDK_S2S)
|
|
enum class ParamsHmacState
|
|
{
|
|
Valid,
|
|
Invalid,
|
|
Empty,
|
|
ENUM_END,
|
|
ENUM_COUNT = ENUM_END
|
|
};
|
|
|
|
US_STATIC_ASSERT(static_cast<size_t>(TestHttpClient_BF::ParamsHmacState::ENUM_COUNT) == 3,
|
|
"TestHttpClient_BF::getPrivateCertificateValue needs to be updated");
|
|
const String getPrivateCertificateValue(ParamsHmacState hmacState)
|
|
{
|
|
switch (hmacState)
|
|
{
|
|
case ubiservices::TestHttpClient_BF::ParamsHmacState::Valid: return HmacHelper_BF::deobfuscateKey(S2S_OBFUSCATED_CERT_PRIVATE);
|
|
case ubiservices::TestHttpClient_BF::ParamsHmacState::Invalid: return String("InvalidPrivateKey");
|
|
case ubiservices::TestHttpClient_BF::ParamsHmacState::Empty:
|
|
default: return String();
|
|
}
|
|
}
|
|
#endif // defined(US_SDK_S2S)
|
|
}
|
|
|
|
namespace TestParametersHttpMultipart_BF
|
|
{
|
|
struct ParamsHttpRequest
|
|
{
|
|
HttpMethod method;
|
|
const char* stringMethod;
|
|
};
|
|
}
|
|
|
|
// Test that the header Content-Length must be specified by UbiServices internal implementation
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETWithContentLength)
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
header["Content-Length"] = "100";
|
|
EXPECT_EQ(100u, header.getContentLength());
|
|
|
|
Facade facade;
|
|
EXPECT_TRUE(AsyncHelper::waitFailure(facade.getHttpClient().sendRequest(HttpGet(url, header)), Errors::HTTP_CONTENT_LENGTH_REJECTED));
|
|
}
|
|
|
|
/// @test Checks that the header Content-Length must be specified by UbiServices internal implementation
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETWithUserAgent)
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
header["User-Agent"] = "Philip Bond";
|
|
|
|
Facade facade;
|
|
EXPECT_TRUE(AsyncHelper::waitFailure(facade.getHttpClient().sendRequest(HttpGet(url, header)), Errors::HTTP_USER_AGENT_REJECTED));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_MINRUN_PerformHttpGETSuccess)
|
|
{
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
EXPECT_TRUE(result.getResult().isBodyTypeString());
|
|
PRINT_STREAM("GET response body is " << result.getResult().getBodyAsString());
|
|
|
|
// Validate headers
|
|
const HttpHeader& respHeader = result.getResult().getAllHeader();
|
|
ASSERT_GT(respHeader.getSize(), 0ull);
|
|
EXPECT_GT(respHeader.getContentLength(), 0);
|
|
EXPECT_NE(String::npos, result.getResult().getHeader("Content-Type").findSubstringCase("application/json"));
|
|
}
|
|
#if defined(US_SDK_S2S)
|
|
/// @tests Test used for testing a http GET request with signature
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETSuccessWithSignedRequest)
|
|
{
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
// we need a valid session so that http signing can work
|
|
// it is also checked in FacadeInterface::signRequest
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
EXPECT_TRUE(result.getResult().isBodyTypeString());
|
|
ASSERT_TRUE(TestHttpClient_BF::validateHttpBoxHeadersInBodyResponse(
|
|
result.getResult().getBodyAsString(),
|
|
{ "Ubi-Signature-timestamp", "Ubi-Signature", "x-forwarded-url" })
|
|
);
|
|
}
|
|
|
|
/// @tests Test used for testing a http GET request with signature when no session is available
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETWithFailedSignedRequest)
|
|
{
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(result, Errors::HTTP_SIGNING_FAILURE));
|
|
}
|
|
|
|
/// @tests Test used for testing a http GET Events request with signature
|
|
TEST_F(HTTP_CLIENT, HTTP_PerformHttpGETEventsWithSignedRequest)
|
|
{
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
StringUrl url = facade.getParametersClient().getResourceUrl(GatewayResource::MAP_S2S.at(GatewayResource::ConfigsEvents).m_name, TEST_SERVICE_EVENTS_VERSION);
|
|
const String spaceId = facade.getWorkingSpaceId();
|
|
url = url.replace("{spaceId}", spaceId);
|
|
|
|
HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
header["Authorization"] = "ubi_s2s_v1 t=" + sessionInfo.getTicket();
|
|
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETWithBasicAuthenticationConfig)
|
|
{
|
|
setupDebugLogging();
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
|
|
const String email = "myEmail";
|
|
const String password = "myPwd";
|
|
HttpGet request(url, header);
|
|
const HttpRequest::HttpRequestBasicAuthenticationConfig httpBasicAuthentication(email, password);
|
|
request.setHttpBasicAuthenticationConfig(httpBasicAuthentication);
|
|
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(request);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
EXPECT_TRUE(result.getResult().isBodyTypeString());
|
|
PRINT_STREAM("GET response body is " << result.getResult().getBodyAsString());
|
|
|
|
ASSERT_TRUE(TestHttpClient_BF::validateHttpBoxHeadersInBodyResponse(result.getResult().getBodyAsString(), { "Authorization" }));
|
|
}
|
|
|
|
#endif //!defined(US_SDK_S2S)
|
|
/// @tests Test used to first request a resource ETag and after to request
|
|
/// the same resource but on this request it will add the obtained ETag in the
|
|
/// If-None-Match header, resulting normally in a 304 Not Modified response
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGetIfNoneMatchEtag)
|
|
{
|
|
Facade facade;
|
|
const StringUrl url = "https://uat-ubiservices.cdn.ubi.com/d0b38ea5-d884-4444-895d-fa99ec47a76a/logo/ubiservices.png";
|
|
|
|
// Perform a HEAD request in order to obtain the resource Etag
|
|
const AsyncResult<HttpResponse> headResult = facade.getHttpClient().sendRequest(HttpHead(url));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(headResult));
|
|
const HttpResponse& headResponse = headResult.getResult();
|
|
ASSERT_EQ(RestStatusCode::OK, headResponse.getStatusCode());
|
|
|
|
const String etag = headResponse.getHeader("ETag").replaceAll("\"", "");
|
|
|
|
// Perform a second request with the etag added in the If-None-Match header
|
|
HttpHeader header;
|
|
header.addHeader("If-None-Match", etag);
|
|
const AsyncResult<HttpResponse> secondResult = facade.getHttpClient().sendRequest(HttpGet(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(secondResult));
|
|
ASSERT_EQ(RestStatusCode::Error304, secondResult.getResult().getStatusCode());
|
|
}
|
|
|
|
/// @tests Test used to perform a conditional GET request with a wrong ETag.
|
|
/// The result should contain the requested resource and a 200 OK status code.
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGetIfNoneMatchWrongEtag)
|
|
{
|
|
Facade facade;
|
|
const StringUrl url = "https://uat-ubiservices.cdn.ubi.com/d0b38ea5-d884-4444-895d-fa99ec47a76a/logo/ubiservices.png";
|
|
|
|
const String etag = "781f58e0239a0d4308589b7b74aaee60";
|
|
|
|
// Perform a second request with the etag added in the If-None-Match header
|
|
HttpHeader header;
|
|
header.addHeader("If-None-Match", etag);
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
const HttpResponse& response = result.getResult();
|
|
ASSERT_EQ(RestStatusCode::OK, response.getStatusCode());
|
|
ASSERT_GT(response.getBodySize(), 0u);
|
|
}
|
|
|
|
/// @test Validates that the client can receive binary properly.
|
|
/// HttpBox server returns random bytes with the ETag header value containing the Sha1 hash.
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETSuccessBinary)
|
|
{
|
|
Facade facade;
|
|
|
|
const uint32 EXPECTED_SIZE = 1024;
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(String::formatText("bytes/%i", EXPECTED_SIZE));
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
const HttpResponse& response = result.getResult();
|
|
ASSERT_EQ(RestStatusCode::OK, response.getStatusCode());
|
|
|
|
// Validate headers
|
|
const HttpHeader& respHeader = response.getAllHeader();
|
|
ASSERT_GT(respHeader.getSize(), 0ull);
|
|
EXPECT_GT(respHeader.getContentLength(), 0);
|
|
EXPECT_NE(String::npos, response.getHeader("Content-Type").findSubstringCase("application/octet-stream"));
|
|
|
|
// Validate body and body size
|
|
EXPECT_EQ(EXPECTED_SIZE, response.getBodySize());
|
|
const Vector<uint8> body = response.getBodyAsBinary();
|
|
|
|
const String etag = response.getHeader("ETag").replaceAll("\"", "");
|
|
ASSERT_EQ(40, etag.getLength()) << "ETag must contain 40 hex characters (20 bytes): " << etag.getUtf8();
|
|
|
|
#if !defined(US_DLL)
|
|
SHA1 sha1;
|
|
const std::string hashResult = sha1(&body[0], body.size());
|
|
EXPECT_TRUE(etag.isEqualCaseInsensitive(hashResult.c_str()));
|
|
#endif
|
|
}
|
|
|
|
// This test use Httpbin because we don't want to receive a Content-Length header and
|
|
// it's not really possible to do that with HttpBox.
|
|
TEST_F(HTTP_CLIENT_PROXY, DISABLED_HTTPBIN_PerformHttpGETBinaryNoContentLength)
|
|
{
|
|
Facade facade;
|
|
|
|
const uint32 EXPECTED_SIZE = 1024;
|
|
const StringUrl url = String::formatText("http://httpbin.org/stream-bytes/%i", EXPECTED_SIZE);
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
|
|
// Using a custom request timeout to make sure this test
|
|
// won't fail if external site is very slow to respond.
|
|
HttpRetryConfig retryConfig = HttpRetryConfig(0, HttpRetryConfig::RetryParam(), HttpRetryConfig::TimeoutParam(60000));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header), retryConfig);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
const HttpResponse& response = result.getResult();
|
|
ASSERT_EQ(RestStatusCode::OK, response.getStatusCode());
|
|
|
|
// Validate headers
|
|
const HttpHeader& respHeader = response.getAllHeader();
|
|
ASSERT_GT(respHeader.getSize(), 0ull);
|
|
EXPECT_EQ(-1, respHeader.getContentLength());
|
|
EXPECT_NE(String::npos, response.getHeader("Content-Type").findSubstringCase("application/octet-stream"));
|
|
|
|
// Validate body size
|
|
const Vector<uint8> body = response.getBodyAsBinary();
|
|
EXPECT_EQ(EXPECTED_SIZE, response.getBodySize());
|
|
EXPECT_EQ(EXPECTED_SIZE, body.size());
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETTransferEncodingChunked)
|
|
{
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", "", false, true);
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
// Validate headers
|
|
const HttpResponse& response = result.getResult();
|
|
const HttpHeader& respHeader = response.getAllHeader();
|
|
ASSERT_GT(respHeader.getSize(), 0ull);
|
|
EXPECT_FALSE(respHeader.hasHeader("Content-Length"));
|
|
EXPECT_TRUE(respHeader.hasHeader("Transfer-Encoding"));
|
|
EXPECT_NE(String::npos, result.getResult().getHeader("Transfer-Encoding").findSubstringCase("chunked"));
|
|
EXPECT_NE(String::npos, result.getResult().getHeader("Content-Type").findSubstringCase("application/json"));
|
|
|
|
// Validate body
|
|
EXPECT_TRUE(response.isBodyTypeString());
|
|
EXPECT_TRUE(Json(response.getBodyAsString()).isValid());
|
|
}
|
|
|
|
// Validate that the client can receive binary with chunked encoding properly.
|
|
// HttpBox server returns random bytes with the ETag header value containing the Sha1 hash.
|
|
// A new chunk is returned by the server at every 1024 bytes.
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETTransferEncodingChunkedBinary)
|
|
{
|
|
Facade facade;
|
|
|
|
const uint32 EXPECTED_SIZE = 1024 * 5; // 5 chunks
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(String::formatText("stream/%i", EXPECTED_SIZE));
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
const HttpResponse& response = result.getResult();
|
|
|
|
// Validate headers
|
|
const HttpHeader& respHeader = response.getAllHeader();
|
|
ASSERT_GT(respHeader.getSize(), 0ull);
|
|
|
|
EXPECT_FALSE(respHeader.hasHeader("Content-Length"));
|
|
EXPECT_TRUE(respHeader.hasHeader("Transfer-Encoding"));
|
|
EXPECT_NE(String::npos, result.getResult().getHeader("Transfer-Encoding").findSubstringCase("chunked"));
|
|
EXPECT_NE(String::npos, respHeader["Content-Type"].findSubstringCase("application/octet-stream"));
|
|
|
|
// Validate body and body size
|
|
EXPECT_EQ(EXPECTED_SIZE, response.getBodySize());
|
|
const Vector<uint8> body = response.getBodyAsBinary();
|
|
|
|
const String etag = response.getHeader("ETag").replaceAll("\"", "");
|
|
ASSERT_EQ(40, etag.getLength()) << "ETag must contain 40 hex characters (20 bytes): " << etag.getUtf8();
|
|
|
|
#if !defined(US_DLL)
|
|
SHA1 sha1;
|
|
const std::string hashResult = sha1(&body[0], body.size());
|
|
EXPECT_TRUE(etag.isEqualCaseInsensitive(hashResult.c_str()));
|
|
#endif
|
|
}
|
|
|
|
/// @test Performs an HTTP GET and validate that query params are sent
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpWithQuery)
|
|
{
|
|
Facade facade;
|
|
|
|
const ::std::pair<String, String> queryParam1("param1", "val1");
|
|
const ::std::pair<String, String> queryParam2("param2", "val2");
|
|
const String queryString(queryParam1.first + "=" + queryParam1.second + "&" + queryParam2.first + "=" + queryParam2.second);
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", queryString);
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
// Validate that query was sent
|
|
EXPECT_TRUE(result.getResult().isBodyTypeString());
|
|
const Json reader(result.getResult().getBodyAsString());
|
|
ASSERT_TRUE(reader.isValid());
|
|
|
|
const Json args = reader["args"];
|
|
ASSERT_TRUE(args.isValid());
|
|
EXPECT_STREQ(queryParam1.second.getUtf8(), args[queryParam1.first].getValueString().getUtf8());
|
|
EXPECT_STREQ(queryParam2.second.getUtf8(), args[queryParam2.first].getValueString().getUtf8());
|
|
}
|
|
|
|
/// @test Performs an HTTP POST
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_ValidatePOSTResponseHeader)
|
|
{
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(_T("post"));
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPost(url, header, _T("data")));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
// Validate headers
|
|
const HttpHeader& respHeader = result.getResult().getAllHeader();
|
|
ASSERT_GT(respHeader.getSize(), 0u);
|
|
EXPECT_GT(respHeader.getContentLength(), 0);
|
|
EXPECT_NE(String::npos, result.getResult().getHeader("Content-Type").findSubstringCase("application/json"));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpHEADSuccess)
|
|
{
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(_T("head"));
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpHead(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
// Validate headers
|
|
const HttpHeader& respHeader = result.getResult().getAllHeader();
|
|
ASSERT_GT(respHeader.getSize(), 0u);
|
|
EXPECT_GT(respHeader.getContentLength(), 0);
|
|
EXPECT_NE(String::npos, result.getResult().getHeader("Content-Type").findSubstringCase("application/json"));
|
|
}
|
|
#if defined(US_SDK_S2S)
|
|
/// @test Performs an HTTP HEAD with signature
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpHEADWithSignedRequest)
|
|
{
|
|
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(_T("head"));
|
|
const HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpHead(url, header), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
}
|
|
#endif
|
|
|
|
/// @test Performs an HTTP GET
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETError)
|
|
{
|
|
Facade facade;
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("post");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_FALSE(response.isSuccessStatusCode());
|
|
EXPECT_EQ(RestStatusCode::Error405, response.getStatusCode());
|
|
}
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("put");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_FALSE(response.isSuccessStatusCode());
|
|
EXPECT_EQ(RestStatusCode::Error405, response.getStatusCode());
|
|
}
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("delete");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_FALSE(response.isSuccessStatusCode());
|
|
EXPECT_EQ(RestStatusCode::Error405, response.getStatusCode());
|
|
}
|
|
}
|
|
|
|
/// @test Test to validate http request retry mechanism
|
|
/// The HttpBox will always return 503, so this request always fails
|
|
/// We calculate the minimum time it should take for this specific retry config
|
|
/// then make sure the actual time it took is greater than the minimum time.
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_ValidateHttpRetryHandler)
|
|
{
|
|
Facade facade;
|
|
|
|
{
|
|
const HttpGet request(TestHelpers_BF::getHttpStatusCodeTestUrl(RestStatusCode::Error503), TestHelpers_BF::getHttpBoxHeader());
|
|
|
|
// Retry config (time in Msec)
|
|
const uint32 RETRY_COUNT = 2;
|
|
const uint32 RETRY_INTERVAL = 2000;
|
|
const uint32 RETRY_INCREMENT = 1000;
|
|
const uint32 RETRY_RANDOM_FACTOR = 100;
|
|
const uint32 TYPICAL_REQUEST_TIME = 200;
|
|
|
|
const uint32 minimumRequestTimeWithRetries =
|
|
(TYPICAL_REQUEST_TIME)+
|
|
(RETRY_INTERVAL + TYPICAL_REQUEST_TIME) +
|
|
(RETRY_INTERVAL + RETRY_INCREMENT + TYPICAL_REQUEST_TIME);
|
|
|
|
HttpRetryConfig retryConfig = HttpRetryConfig(RETRY_COUNT, HttpRetryConfig::RetryParam(RETRY_INTERVAL, RETRY_INCREMENT, RETRY_RANDOM_FACTOR));
|
|
|
|
HighResolutionChrono chrono;
|
|
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(request, retryConfig);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_FALSE(response.isSuccessStatusCode());
|
|
EXPECT_EQ(RestStatusCode::Error503, response.getStatusCode());
|
|
|
|
const DurationMilli totalTimeWithRetries = chrono.getElapsed<SI::milli>();
|
|
EXPECT_LT(minimumRequestTimeWithRetries, totalTimeWithRetries);
|
|
}
|
|
}
|
|
|
|
/// @test Performs an HTTP POST
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_MINRUN_PerformHttpPOST)
|
|
{
|
|
Facade facade;
|
|
|
|
JsonWriter writer;
|
|
writer["post some thing"] = 0;
|
|
const String body = writer.renderContent();
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("post");
|
|
//PRINT_STREAM("Sending post to " << url);
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPost(url, header, body));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
//PRINT_STREAM("POST response body is " << result.getResult().getBodyAsString());
|
|
}
|
|
|
|
// Errors
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPost(url, header, body));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_FALSE(response.isSuccessStatusCode());
|
|
EXPECT_EQ(RestStatusCode::Error405, response.getStatusCode());
|
|
}
|
|
}
|
|
|
|
typedef ParamFixture<HTTP_CLIENT, TestParametersHttpMultipart_BF::ParamsHttpRequest> PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE;
|
|
INSTANTIATE_TEST_CASE_P(ParamsHttpRequest, PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE, ::testing::Values(
|
|
TestParametersHttpMultipart_BF::ParamsHttpRequest{ HttpMethod::HTTP_POST, "post" },
|
|
TestParametersHttpMultipart_BF::ParamsHttpRequest{ HttpMethod::HTTP_PUT, "put" },
|
|
TestParametersHttpMultipart_BF::ParamsHttpRequest{ HttpMethod::HTTP_PATCH, "patch" },
|
|
TestParametersHttpMultipart_BF::ParamsHttpRequest{ HttpMethod::HTTP_DELETE, "delete" }
|
|
));
|
|
|
|
/// @test Performs HTTP POST/PUT/PATCH/DELETE request with multiparts provided at construction
|
|
TEST_P(PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE, HTTPBOX_PerformMultipart)
|
|
{
|
|
Facade facade;
|
|
HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
auto param = this->getParam();
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(param.stringMethod);
|
|
|
|
const String multipartBoundary = "----USMultipartBoundary";
|
|
const auto multipartData = TestHttpClient_BF::createMultipartData();
|
|
|
|
AsyncResult<HttpResponse> result;
|
|
switch (param.method)
|
|
{
|
|
case HttpMethod::HTTP_POST:
|
|
result = facade.getHttpClient().sendRequest(HttpPost(url, header, multipartData, multipartBoundary));
|
|
break;
|
|
case HttpMethod::HTTP_PUT:
|
|
result = facade.getHttpClient().sendRequest(HttpPut(url, header, multipartData, multipartBoundary));
|
|
break;
|
|
case HttpMethod::HTTP_PATCH:
|
|
result = facade.getHttpClient().sendRequest(HttpPatch(url, header, multipartData, multipartBoundary));
|
|
break;
|
|
case HttpMethod::HTTP_DELETE:
|
|
result = facade.getHttpClient().sendRequest(HttpDelete(url, header, multipartData, multipartBoundary));
|
|
break;
|
|
default:
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
//Somewhat future proof in case the multipartData changes
|
|
ASSERT_EQ(2u, multipartData.size());
|
|
ASSERT_EQ("image_data", multipartData[0].m_name);
|
|
ASSERT_EQ("text_data", multipartData[1].m_name);
|
|
|
|
// multipartData[0] contains the image data
|
|
String stringImageData = "data:image/png;base64,";
|
|
stringImageData += Base64::encode(multipartData[0].m_data);
|
|
|
|
// multipartData[1] contains the text data
|
|
const String stringTextData = String(reinterpret_cast<const char*>(multipartData[1].m_data.data()), multipartData[1].m_data.size());
|
|
|
|
Json json(result.getResult().getBodyAsString());
|
|
|
|
ASSERT_GT(json.getItemsCount(), 0);
|
|
|
|
EXPECT_EQ(stringImageData, json["files"]["image_data"].getValueString());
|
|
EXPECT_EQ(stringTextData, json["form"]["text_data"].getValueString());
|
|
}
|
|
|
|
/// @test Performs HTTP POST/PUT/PATCH/DELETE request with multiparts provided at construction, but with default boundary
|
|
TEST_P(PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE, HTTPBOX_PerformMultipartDefaultBoundary)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
auto param = this->getParam();
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(param.stringMethod);
|
|
|
|
const auto multipartData = TestHttpClient_BF::createMultipartData();
|
|
|
|
const String multipartGenerated = HttpMultipartHelper::generateDefaultMultipartBoundary();
|
|
|
|
AsyncResult<HttpResponse> result;
|
|
switch (param.method)
|
|
{
|
|
case HttpMethod::HTTP_POST:
|
|
result = facade.getHttpClient().sendRequest(HttpPost(url, header, multipartData, String()));
|
|
break;
|
|
case HttpMethod::HTTP_PUT:
|
|
result = facade.getHttpClient().sendRequest(HttpPut(url, header, multipartData, String()));
|
|
break;
|
|
case HttpMethod::HTTP_PATCH:
|
|
result = facade.getHttpClient().sendRequest(HttpPatch(url, header, multipartData, String()));
|
|
break;
|
|
case HttpMethod::HTTP_DELETE:
|
|
result = facade.getHttpClient().sendRequest(HttpDelete(url, header, multipartData, String()));
|
|
break;
|
|
default:
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
//Somewhat future proof in case the multipartData changes
|
|
ASSERT_EQ(2u, multipartData.size());
|
|
ASSERT_EQ("image_data", multipartData[0].m_name);
|
|
ASSERT_EQ("text_data", multipartData[1].m_name);
|
|
|
|
// multipartData[0] contains the image data
|
|
String stringImageData = "data:image/png;base64,";
|
|
stringImageData += Base64::encode(multipartData[0].m_data);
|
|
|
|
// multipartData[1] contains the text data
|
|
const String stringTextData = String(reinterpret_cast<const char*>(multipartData[1].m_data.data()), multipartData[1].m_data.size());
|
|
|
|
Json json(result.getResult().getBodyAsString());
|
|
|
|
ASSERT_GT(json.getItemsCount(), 0);
|
|
|
|
EXPECT_EQ(stringImageData, json["files"]["image_data"].getValueString());
|
|
EXPECT_EQ(stringTextData, json["form"]["text_data"].getValueString());
|
|
EXPECT_NE(String::npos, json["headers"]["Content-Type"].getValueString().findSubstringCase(multipartGenerated));
|
|
}
|
|
|
|
/// @test Performs HTTP POST/PUT/PATCH/DELETE request with multiparts provided with setBody
|
|
TEST_P(PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE, HTTPBOX_PerformMultipartWithSetBody)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
auto param = this->getParam();
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(param.stringMethod);
|
|
|
|
const String multipartBoundary = "----USMultipartBoundary";
|
|
const auto multipartData = TestHttpClient_BF::createMultipartData();
|
|
|
|
AsyncResult<HttpResponse> result;
|
|
switch (param.method)
|
|
{
|
|
case HttpMethod::HTTP_POST:
|
|
{
|
|
HttpPost request(url, "");
|
|
request.setBody(multipartData, multipartBoundary);
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_PUT:
|
|
{
|
|
HttpPut request(url, "");
|
|
request.setBody(multipartData, multipartBoundary);
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_PATCH:
|
|
{
|
|
HttpPatch request(url, "");
|
|
request.setBody(multipartData, multipartBoundary);
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_DELETE:
|
|
{
|
|
HttpDelete request(url, "");
|
|
request.setBody(multipartData, multipartBoundary);
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
default:
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
//Somewhat future proof in case the multipartData changes
|
|
ASSERT_EQ(2u, multipartData.size());
|
|
ASSERT_EQ("image_data", multipartData[0].m_name);
|
|
ASSERT_EQ("text_data", multipartData[1].m_name);
|
|
|
|
// multipartData[0] contains the image data
|
|
String stringImageData = "data:image/png;base64,";
|
|
stringImageData += Base64::encode(multipartData[0].m_data);
|
|
|
|
// multipartData[1] contains the text data
|
|
const String stringTextData = String(reinterpret_cast<const char*>(multipartData[1].m_data.data()), multipartData[1].m_data.size());
|
|
|
|
Json json(result.getResult().getBodyAsString());
|
|
|
|
ASSERT_GT(json.getItemsCount(), 0);
|
|
|
|
EXPECT_EQ(stringImageData, json["files"]["image_data"].getValueString());
|
|
EXPECT_EQ(stringTextData, json["form"]["text_data"].getValueString());
|
|
}
|
|
|
|
/// @test Performs HTTP POST/PUT/PATCH/DELETE request with multiparts provided with setBody, but with default boundary
|
|
TEST_P(PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE, HTTPBOX_PerformMultipartWithSetBodyAndDefaultBoundary)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
auto param = this->getParam();
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(param.stringMethod);
|
|
|
|
const auto multipartData = TestHttpClient_BF::createMultipartData();
|
|
|
|
const String multipartGenerated = HttpMultipartHelper::generateDefaultMultipartBoundary();
|
|
|
|
AsyncResult<HttpResponse> result;
|
|
switch (param.method)
|
|
{
|
|
case HttpMethod::HTTP_POST:
|
|
{
|
|
HttpPost request(url, "");
|
|
request.setBody(multipartData, String());
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_PUT:
|
|
{
|
|
HttpPut request(url, "");
|
|
request.setBody(multipartData, String());
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_PATCH:
|
|
{
|
|
HttpPatch request(url, "");
|
|
request.setBody(multipartData, String());
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_DELETE:
|
|
{
|
|
HttpDelete request(url, "");
|
|
request.setBody(multipartData, String());
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
default:
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
//Somewhat future proof in case the multipartData changes
|
|
ASSERT_EQ(2u, multipartData.size());
|
|
ASSERT_EQ("image_data", multipartData[0].m_name);
|
|
ASSERT_EQ("text_data", multipartData[1].m_name);
|
|
|
|
// multipartData[0] contains the image data
|
|
String stringImageData = "data:image/png;base64,";
|
|
stringImageData += Base64::encode(multipartData[0].m_data);
|
|
|
|
// multipartData[1] contains the text data
|
|
const String stringTextData = String(reinterpret_cast<const char*>(multipartData[1].m_data.data()), multipartData[1].m_data.size());
|
|
|
|
Json json(result.getResult().getBodyAsString());
|
|
|
|
ASSERT_GT(json.getItemsCount(), 0);
|
|
|
|
EXPECT_EQ(stringImageData, json["files"]["image_data"].getValueString());
|
|
EXPECT_EQ(stringTextData, json["form"]["text_data"].getValueString());
|
|
EXPECT_NE(String::npos, json["headers"]["Content-Type"].getValueString().findSubstringCase(multipartGenerated));
|
|
}
|
|
|
|
/// @test Performs HTTP POST/PUT/PATCH/DELETE request with multiparts provided at construction, with a too big boundary
|
|
TEST_P(PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE, HTTPBOX_PerformMultipartWithBoundaryTooBig)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
auto param = this->getParam();
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(param.stringMethod);
|
|
|
|
const String extraBoundary = "cut";
|
|
String multipartBoundary;
|
|
multipartBoundary.reserve(HttpMultipartHelper::s_MAX_BOUNDARY_LENGTH + extraBoundary.getLength());
|
|
for (unsigned int i = 0; i < HttpMultipartHelper::s_MAX_BOUNDARY_LENGTH; ++i)
|
|
{
|
|
multipartBoundary += "a";
|
|
}
|
|
|
|
multipartBoundary += extraBoundary;
|
|
|
|
const auto multipartData = TestHttpClient_BF::createMultipartData();
|
|
|
|
AsyncResult<HttpResponse> result;
|
|
switch (param.method)
|
|
{
|
|
case HttpMethod::HTTP_POST:
|
|
result = facade.getHttpClient().sendRequest(HttpPost(url, header, multipartData, multipartBoundary));
|
|
break;
|
|
case HttpMethod::HTTP_PUT:
|
|
result = facade.getHttpClient().sendRequest(HttpPut(url, header, multipartData, multipartBoundary));
|
|
break;
|
|
case HttpMethod::HTTP_PATCH:
|
|
result = facade.getHttpClient().sendRequest(HttpPatch(url, header, multipartData, multipartBoundary));
|
|
break;
|
|
case HttpMethod::HTTP_DELETE:
|
|
result = facade.getHttpClient().sendRequest(HttpDelete(url, header, multipartData, multipartBoundary));
|
|
break;
|
|
default:
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
//Somewhat future proof in case the multipartData changes
|
|
ASSERT_EQ(2u, multipartData.size());
|
|
ASSERT_EQ("image_data", multipartData[0].m_name);
|
|
ASSERT_EQ("text_data", multipartData[1].m_name);
|
|
|
|
// multipartData[0] contains the image data
|
|
String stringImageData = "data:image/png;base64,";
|
|
stringImageData += Base64::encode(multipartData[0].m_data);
|
|
|
|
// multipartData[1] contains the text data
|
|
const String stringTextData = String(reinterpret_cast<const char*>(multipartData[1].m_data.data()), multipartData[1].m_data.size());
|
|
|
|
Json json(result.getResult().getBodyAsString());
|
|
|
|
ASSERT_GT(json.getItemsCount(), 0);
|
|
|
|
EXPECT_EQ(stringImageData, json["files"]["image_data"].getValueString());
|
|
EXPECT_EQ(stringTextData, json["form"]["text_data"].getValueString());
|
|
|
|
String contentTypeBoundary = json["headers"]["Content-Type"].getValueString();
|
|
const size_t boundaryIndex = contentTypeBoundary.findSubstringCase("boundary=");
|
|
ASSERT_NE(String::npos, boundaryIndex);
|
|
|
|
const String boundary = contentTypeBoundary.substr(boundaryIndex + String("boundary=").getLength());
|
|
EXPECT_EQ(HttpMultipartHelper::s_MAX_BOUNDARY_LENGTH, boundary.getLength());
|
|
|
|
EXPECT_EQ(String::npos, boundary.findSubstringCase(extraBoundary));
|
|
}
|
|
|
|
/// @test Performs HTTP POST/PUT/PATCH/DELETE request with multiparts provided with setBody, with a too big boundary
|
|
TEST_P(PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE, HTTPBOX_PerformMultipartSetBodyWithBoundaryTooBig)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
auto param = this->getParam();
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl(param.stringMethod);
|
|
|
|
const String extraBoundary = "cut";
|
|
String multipartBoundary;
|
|
multipartBoundary.reserve(HttpMultipartHelper::s_MAX_BOUNDARY_LENGTH + extraBoundary.getLength());
|
|
for (unsigned int i = 0; i < HttpMultipartHelper::s_MAX_BOUNDARY_LENGTH; ++i)
|
|
{
|
|
multipartBoundary += "a";
|
|
}
|
|
|
|
multipartBoundary += extraBoundary;
|
|
|
|
const auto multipartData = TestHttpClient_BF::createMultipartData();
|
|
|
|
AsyncResult<HttpResponse> result;
|
|
switch (param.method)
|
|
{
|
|
case HttpMethod::HTTP_POST:
|
|
{
|
|
HttpPost request(url, "");
|
|
request.setBody(multipartData, multipartBoundary);
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_PUT:
|
|
{
|
|
HttpPut request(url, "");
|
|
request.setBody(multipartData, multipartBoundary);
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_PATCH:
|
|
{
|
|
HttpPatch request(url, "");
|
|
request.setBody(multipartData, multipartBoundary);
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_DELETE:
|
|
{
|
|
HttpDelete request(url, "");
|
|
request.setBody(multipartData, multipartBoundary);
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
default:
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
//Somewhat future proof in case the multipartData changes
|
|
ASSERT_EQ(2u, multipartData.size());
|
|
ASSERT_EQ("image_data", multipartData[0].m_name);
|
|
ASSERT_EQ("text_data", multipartData[1].m_name);
|
|
|
|
// multipartData[0] contains the image data
|
|
String stringImageData = "data:image/png;base64,";
|
|
stringImageData += Base64::encode(multipartData[0].m_data);
|
|
|
|
// multipartData[1] contains the text data
|
|
const String stringTextData = String(reinterpret_cast<const char*>(multipartData[1].m_data.data()), multipartData[1].m_data.size());
|
|
|
|
Json json(result.getResult().getBodyAsString());
|
|
|
|
ASSERT_GT(json.getItemsCount(), 0);
|
|
|
|
EXPECT_EQ(stringImageData, json["files"]["image_data"].getValueString());
|
|
EXPECT_EQ(stringTextData, json["form"]["text_data"].getValueString());
|
|
|
|
String contentTypeBoundary = json["headers"]["Content-Type"].getValueString();
|
|
const size_t boundaryIndex = contentTypeBoundary.findSubstringCase("boundary=");
|
|
ASSERT_NE(boundaryIndex, static_cast<size_t>(-1));
|
|
|
|
const String boundary = contentTypeBoundary.substr(boundaryIndex + String("boundary=").getLength());
|
|
EXPECT_EQ(HttpMultipartHelper::s_MAX_BOUNDARY_LENGTH, boundary.getLength());
|
|
|
|
EXPECT_EQ(String::npos, boundary.findSubstringCase(extraBoundary));
|
|
}
|
|
|
|
/// @test Performs HTTP POST/PUT/PATCH/DELETE standard request, but with multiparts provided at construction and body overwritten
|
|
TEST_P(PARAMETERS_HTTP_MULTIPART_REQUEST_FIXTURE, HTTPBOX_PerformStandardWithMultipartOverwritten)
|
|
{
|
|
Facade facade;
|
|
|
|
HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
auto param = this->getParam();
|
|
|
|
const String url = TestHelpers_BF::getHttpTestUrl(param.stringMethod);
|
|
|
|
const String multipartBoundary = "----USMultipartBoundary";
|
|
const auto multipartData = TestHttpClient_BF::createMultipartData();
|
|
|
|
JsonWriter writer;
|
|
writer["This is a standard HTTP Request"] = 0;
|
|
|
|
AsyncResult<HttpResponse> result;
|
|
switch (param.method)
|
|
{
|
|
case HttpMethod::HTTP_POST:
|
|
{
|
|
HttpPost request(url, header, multipartData, multipartBoundary);
|
|
request.setBody(writer.renderContent());
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_PUT:
|
|
{
|
|
HttpPut request(url, header, multipartData, multipartBoundary);
|
|
request.setBody(writer.renderContent());
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_PATCH:
|
|
{
|
|
HttpPatch request(url, header, multipartData, multipartBoundary);
|
|
request.setBody(writer.renderContent());
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
case HttpMethod::HTTP_DELETE:
|
|
{
|
|
HttpDelete request(url, header, multipartData, multipartBoundary);
|
|
request.setBody(writer.renderContent());
|
|
result = facade.getHttpClient().sendRequest(request);
|
|
break;
|
|
}
|
|
default:
|
|
ASSERT_TRUE(false);
|
|
}
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
ASSERT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
Json json(result.getResult().getBodyAsString());
|
|
|
|
ASSERT_GT(json.getItemsCount(), 0);
|
|
EXPECT_EQ(0, json["files"].getItemsCount());
|
|
EXPECT_EQ(0, json["form"].getItemsCount());
|
|
EXPECT_EQ(String::npos, json["headers"]["Content-Type"].getValueString().findSubstringCase(multipartBoundary));
|
|
}
|
|
|
|
#if defined(US_SDK_S2S)
|
|
/// @test Performs an HTTP POST with signature
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpPOSTWithSignedRequest)
|
|
{
|
|
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
// we need a valid session so that http signing can work
|
|
// it is also checked in FacadeInterface::signRequest
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
JsonWriter writer;
|
|
writer["post some thing"] = 0;
|
|
const String body = writer.renderContent();
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("post");
|
|
//PRINT_STREAM("Sending post to " << url);
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPost(url, header, body), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
EXPECT_TRUE(result.getResult().isBodyTypeString());
|
|
|
|
ASSERT_TRUE(TestHttpClient_BF::validateHttpBoxHeadersInBodyResponse(
|
|
result.getResult().getBodyAsString(),
|
|
{ "Ubi-Signature-timestamp", "Ubi-Signature", "x-forwarded-url" })
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpPOSTBinary)
|
|
{
|
|
Facade facade;
|
|
|
|
const Vector<uint8> data = TestHttpClient_BF::createUploadData(500u);
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("bytes");
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
//PRINT_STREAM("Sending post to " << url);
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPost(url, header, data));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_EQ(RestStatusCode::OK, response.getStatusCode());
|
|
|
|
// Validate data sent
|
|
const String& etag = response.getHeader("ETag").replaceAll("\"", "");
|
|
ASSERT_EQ(40, etag.getLength()) << "ETag must contain 40 hex characters (20 bytes): " << etag.getUtf8();
|
|
|
|
#if !defined(US_DLL)
|
|
SHA1 sha1;
|
|
const std::string hashResult = sha1(&data[0], data.size());
|
|
EXPECT_TRUE(etag.isEqualCaseInsensitive(hashResult.c_str()));
|
|
#endif
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpPUTBinary)
|
|
{
|
|
Facade facade;
|
|
|
|
const Vector<uint8> data = TestHttpClient_BF::createUploadData(500u);
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("bytes");
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
//PRINT_STREAM("Sending post to " << url);
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPut(url, header, data));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_EQ(RestStatusCode::OK, response.getStatusCode());
|
|
|
|
// Validate data sent
|
|
const String& etag = response.getHeader("ETag").replaceAll("\"", "");
|
|
ASSERT_EQ(40, etag.getLength()) << "ETag must contain 40 hex characters (20 bytes): " << etag.getUtf8();
|
|
|
|
#if !defined(US_DLL)
|
|
SHA1 sha1;
|
|
const std::string hashResult = sha1(&data[0], data.size());
|
|
EXPECT_TRUE(etag.isEqualCaseInsensitive(hashResult.c_str()));
|
|
#endif
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpPostSuccessNoBody)
|
|
{
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("post");
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
//PRINT_STREAM("Sending post to " << url);
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPost(url, header, ""));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
|
|
/// @test Performs an HTTP PUT
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_MINRUN_PerformHttpPUT)
|
|
{
|
|
Facade facade;
|
|
|
|
JsonWriter writer;
|
|
writer["put some thing"] = 0;
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
const String body = writer.renderContent();
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("put"));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPut(url, header, body));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
//PRINT_STREAM("PUT response body is " << result.getResult().getBodyAsString());
|
|
}
|
|
|
|
// Errors
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPut(url, header, body));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_FALSE(response.isSuccessStatusCode());
|
|
EXPECT_EQ(RestStatusCode::Error405, response.getStatusCode());
|
|
}
|
|
}
|
|
|
|
#if defined(US_SDK_S2S)
|
|
/// @test Performs an HTTP PUT with signature
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpPUTWithSignedRequest)
|
|
{
|
|
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
const SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
JsonWriter writer;
|
|
writer["put some thing"] = 0;
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
const String body = writer.renderContent();
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("put"));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPut(url, header, body), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
EXPECT_TRUE(result.getResult().isBodyTypeString());
|
|
ASSERT_TRUE(TestHttpClient_BF::validateHttpBoxHeadersInBodyResponse(
|
|
result.getResult().getBodyAsString(),
|
|
{ "Ubi-Signature-timestamp", "Ubi-Signature", "x-forwarded-url" })
|
|
);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
/// @test Performs an HTTP PATCH
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpPatch)
|
|
{
|
|
Facade facade;
|
|
|
|
JsonWriter writer;
|
|
writer["patch this bug"] = 0;
|
|
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
const String body = writer.renderContent();
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("patch"));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPatch(url, header, body));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
PRINT_STREAM("PATCH response body is " << result.getResult().getBodyAsString());
|
|
}
|
|
|
|
// Errors
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("get"));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPatch(url, header, body));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_FALSE(response.isSuccessStatusCode());
|
|
EXPECT_EQ(RestStatusCode::Error405, response.getStatusCode());
|
|
}
|
|
}
|
|
|
|
#if defined(US_SDK_S2S)
|
|
/// @test Performs an HTTP PATCH with signature
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpPATCHWithSignedRequest)
|
|
{
|
|
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
JsonWriter writer;
|
|
writer["patch this bug"] = 0;
|
|
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
const String body = writer.renderContent();
|
|
{
|
|
const StringUrl url(TestHelpers_BF::getHttpTestUrl("patch"));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPatch(url, header, body), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
EXPECT_TRUE(result.getResult().isBodyTypeString());
|
|
|
|
ASSERT_TRUE(TestHttpClient_BF::validateHttpBoxHeadersInBodyResponse(
|
|
result.getResult().getBodyAsString(),
|
|
{ "Ubi-Signature-timestamp", "Ubi-Signature", "x-forwarded-url" })
|
|
);
|
|
}
|
|
}
|
|
|
|
US_STATIC_ASSERT(static_cast<size_t>(TestHttpClient_BF::ParamsHmacState::ENUM_COUNT) == 3, "HTTP_CLIENT_NO_INIT_HMAC_FIXTURE needs to be updated");
|
|
typedef ParamFixture<HTTP_CLIENT_NO_INIT, TestHttpClient_BF::ParamsHmacState> HTTP_CLIENT_NO_INIT_HMAC_FIXTURE;
|
|
INSTANTIATE_TEST_CASE_P(ParamsHmacState, HTTP_CLIENT_NO_INIT_HMAC_FIXTURE, ::testing::Values(
|
|
TestHttpClient_BF::ParamsHmacState{ TestHttpClient_BF::ParamsHmacState::Valid }, // valid private key
|
|
TestHttpClient_BF::ParamsHmacState{ TestHttpClient_BF::ParamsHmacState::Invalid }, // invalid private key
|
|
TestHttpClient_BF::ParamsHmacState{ TestHttpClient_BF::ParamsHmacState::Empty })); // empty private key
|
|
|
|
|
|
// Test must run in dev environment, (HMAC check is not done on UAT server side).
|
|
TEST_P(HTTP_CLIENT_NO_INIT_HMAC_FIXTURE, TestHmacSignature)
|
|
{
|
|
const US_TEST_NS::ScopedInit autoInit;
|
|
|
|
{
|
|
HttpConfig httpConfig;
|
|
httpConfig.m_proxy = HttpProxyConfig(US_TEST_NS::PROXY_NAME, US_TEST_NS::PROXY_PORT);
|
|
|
|
const GameConfig gameConfig(
|
|
ApplicationId(NS_COMMON::Options::getApplicationId().c_str()),
|
|
NS_COMMON::Options::getApplicationBuildId().c_str(),
|
|
Environment::Dev);
|
|
|
|
const SystemConfig systemConfig(ThreadingConfig(), httpConfig);
|
|
configureSdk(gameConfig, systemConfig);
|
|
}
|
|
|
|
const TestHttpClient_BF::ParamsHmacState& hmacState = this->getParam();
|
|
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {},
|
|
TestHttpClient_BF::getPrivateCertificateValue(hmacState));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
JsonWriter writer;
|
|
writer["communicationOptIn"] = false;
|
|
writer["communicationThirdPartyOptIn"] = false;
|
|
writer["country"] = "US";
|
|
writer["dateOfBirth"] = "1982-02-20";
|
|
writer["email"] = "ussdk032@ubisoft.com";
|
|
writer["firstName"] = "";
|
|
writer["gender"] = "M";
|
|
writer["isDateOfBirthApprox"] = false;
|
|
writer["lastName"] = "";
|
|
writer["password"] = "a1234567";
|
|
writer["nameOnPlatform"] = "ussdk032";
|
|
writer["preferredLanguage"] = "en";
|
|
writer["accountType"] = "Ubisoft";
|
|
|
|
HttpHeader header;
|
|
header["Ubi-AppId"] = facade.getParametersClient().getUsApplicationId();
|
|
header["Ubi-SessionId"] = facade.getAuthenticationClient().getSessionInfo().getSessionId();
|
|
header["Authorization"] = "ubi_s2s_v1 t=" + facade.getAuthenticationClient().getSessionInfo().getTicket();
|
|
header["Content-Type"] = "application/json";
|
|
|
|
const StringUrl url("https://msr-dev-s2s-ubiservices.ubi.com/v3/users/validateCreation");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(
|
|
HttpPost(url, header, writer.renderContent()), HttpRequestSignType::Hmac);
|
|
|
|
if (hmacState == TestHttpClient_BF::ParamsHmacState::Valid)
|
|
{
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
else
|
|
{
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(result, Errors::HTTP_SIGNING_FAILURE));
|
|
}
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT_NO_INIT, TestHmacSignatureSuccessWithUnicode)
|
|
{
|
|
const US_TEST_NS::ScopedInit autoInit;
|
|
|
|
{
|
|
HttpConfig httpConfig;
|
|
httpConfig.m_proxy = HttpProxyConfig(US_TEST_NS::PROXY_NAME, US_TEST_NS::PROXY_PORT);
|
|
|
|
const GameConfig gameConfig(
|
|
ApplicationId(NS_COMMON::Options::getApplicationId().c_str()),
|
|
NS_COMMON::Options::getApplicationBuildId().c_str(),
|
|
Environment::Dev);
|
|
|
|
const SystemConfig systemConfig(ThreadingConfig(), httpConfig);
|
|
configureSdk(gameConfig, systemConfig);
|
|
}
|
|
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
HttpHeader header;
|
|
header["Ubi-AppId"] = facade.getParametersClient().getUsApplicationId();
|
|
header["Ubi-SessionId"] = facade.getAuthenticationClient().getSessionInfo().getSessionId();
|
|
header["Authorization"] = "ubi_s2s_v1 t=" + facade.getAuthenticationClient().getSessionInfo().getTicket();
|
|
header["Content-Type"] = "application/json";
|
|
|
|
String body("{\"firstName\":\"My\u000BFirst\u000bName\"}");
|
|
|
|
const StringUrl url("https://msr-dev-s2s-ubiservices.ubi.com/v3/users/d27c61c4-7129-445f-9f5b-3bb7221a10e5"); // ussdk032
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(
|
|
HttpPatch(url, header, body), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
#endif
|
|
|
|
/// @test Performs an HTTP Delete
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_MINRUN_PerformHttpDELETE)
|
|
{
|
|
Facade facade;
|
|
|
|
JsonWriter writer;
|
|
writer["delete some thing"] = 0;
|
|
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
const String body = writer.renderContent();
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("delete");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpDelete(url, header, body));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
|
|
// Errors
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpDelete(url, header, body));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_FALSE(response.isSuccessStatusCode());
|
|
EXPECT_EQ(RestStatusCode::Error405, response.getStatusCode());
|
|
}
|
|
}
|
|
|
|
#if defined(US_SDK_S2S)
|
|
/// @test Performs an HTTP DELETE with signature
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpDELETEWithSignedRequest)
|
|
{
|
|
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
JsonWriter writer;
|
|
writer["delete some thing"] = 0;
|
|
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
const String body = writer.renderContent();
|
|
{
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("delete");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpDelete(url, header, body), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
EXPECT_TRUE(result.getResult().isBodyTypeString());
|
|
ASSERT_TRUE(TestHttpClient_BF::validateHttpBoxHeadersInBodyResponse(
|
|
result.getResult().getBodyAsString(),
|
|
{ "Ubi-Signature-timestamp", "Ubi-Signature", "x-forwarded-url" })
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
#if !defined(US_DLL)
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_TestSignatureSuccessAfterExtendSession)
|
|
{
|
|
Facade facade;
|
|
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.hasValidSession());
|
|
|
|
JsonWriter writer;
|
|
writer["delete some thing"] = 0;
|
|
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
const String body = writer.renderContent();
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("delete");
|
|
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpDelete(url, header, body), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
|
|
|
|
// Create listener before triggering state change, so we can catch notification
|
|
ListenerHandler<AuthenticationNotification> notificationListener = facade.getAuthenticationClient().createNotificationListener();
|
|
|
|
TestExtendSessionHelper::reduceSessionDuration(facade, ClockSteady::getTimeMilli() + 500); // Setup expiration
|
|
|
|
ASSERT_EQ(AuthenticationNotificationType::SessionInfoChanged, TestExtendSession_BF::waitForNotification(notificationListener, DurationMicro(Duration<>(5))));
|
|
|
|
sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.hasValidSession());
|
|
|
|
const AsyncResult<HttpResponse> result2 = facade.getHttpClient().sendRequest(HttpDelete(url, header, body), HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result2));
|
|
EXPECT_EQ(RestStatusCode::OK, result2.getResult().getStatusCode());
|
|
}
|
|
#endif // !defined(US_DLL)
|
|
#endif // defined(US_SDK_S2S)
|
|
//
|
|
// /// @test Validates that the number of calls to *ubiservices.ubi.com is not limited.
|
|
// /// @note It's not really reliable to test that some asyncs will be delayed since it highly
|
|
// /// depends on the timing. Therefore, the test only checks that the calls were successful.
|
|
// // TODO: Review this test as per US_HttpClientRequestLimitCustom implementation
|
|
// TEST_F(HTTP_CLIENT, US_HttpClientRequestLimit)
|
|
// {
|
|
// const uint32 REQUEST_COUNT = 11;
|
|
// Vector<AsyncResultBase> allRequests;
|
|
//
|
|
// Facade facade;
|
|
//
|
|
// const StringUrl url("https://public-ubiservices.ubi.com/v1/status");
|
|
//
|
|
// EXPECT_TRUE(AsyncHelper::waitSuccess(facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()))));
|
|
//
|
|
// for (uint32 i = 0; i <= REQUEST_COUNT; ++i)
|
|
// {
|
|
// allRequests.push_back(facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader())));
|
|
// }
|
|
// for (Vector<AsyncResultBase>::const_iterator it = allRequests.begin(); it != allRequests.end(); ++it)
|
|
// {
|
|
// EXPECT_TRUE(AsyncHelper::waitSuccess(*it));
|
|
// }
|
|
// }
|
|
|
|
#if !defined(US_DLL)
|
|
namespace HttpClientImpl_BF
|
|
{
|
|
extern uint32 s_MAX_CONCURRENT_US_CALLS;
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, US_HttpClientRequestLimitUbiServices)
|
|
{
|
|
const uint32 DELAY = 4; // This value must be below 10 seconds (until timeout value is specified)
|
|
const uint32 REQUEST_COUNT = 20;
|
|
ASSERT_GT(REQUEST_COUNT, HttpClientImpl_BF::s_MAX_CONCURRENT_US_CALLS) << "Sanity check for the test configuration";
|
|
|
|
Vector<std::pair<AsyncResult<HttpResponse>,char>> allRequests;
|
|
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", String::formatText("delay=%d", DELAY), false, false);
|
|
ASSERT_TRUE(url.findSubstringCase("ubiservices.ubi.com") != String::npos) << "The test requirement is missing.";
|
|
|
|
for (uint32 i = 0; i < REQUEST_COUNT; ++i)
|
|
{
|
|
allRequests.push_back(std::pair<AsyncResult<HttpResponse>, char>(facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader())), '_'));
|
|
US_TEST_NS::Helper::sleep((DELAY/20)*1000);
|
|
}
|
|
|
|
US_TEST_NS::Helper::sleep((DELAY/2)*1000);
|
|
|
|
// The following loop is not required for the test, but it is disabled for Linux,
|
|
// because tests run in a VM with limited resources and exposes the weakness of this test:
|
|
// The async result states may change while iterating, leading to invalid counters values!
|
|
#if !defined(US_TARGET_LINUX)
|
|
{
|
|
uint32 delayed = 0;
|
|
String states;
|
|
for (auto& request : allRequests)
|
|
{
|
|
request.second = AsyncResultBaseProxy::isCurrentlyDelayed(request.first)? 'D' : '.';
|
|
states += String::formatText("%c", request.second);
|
|
if (AsyncResultBaseProxy::isCurrentlyDelayed(request.first))
|
|
{
|
|
delayed++;
|
|
}
|
|
}
|
|
//PRINT_LINE("STATES [%s]", states.getUtf8());
|
|
|
|
EXPECT_EQ(REQUEST_COUNT - HttpClientImpl_BF::s_MAX_CONCURRENT_US_CALLS, delayed) <<
|
|
"The SDK limits the number of concurrent requests with URL containing 'ubiservices.ubi.com'.";
|
|
}
|
|
#endif
|
|
HighResolutionChrono chrono;
|
|
|
|
uint32 done = 0;
|
|
const uint32 MAX_TIME_FOR_ALL_REQUESTS_SEC = 30;
|
|
while (done < allRequests.size() && chrono.getElapsed<Ratio<1, 1> >() < MAX_TIME_FOR_ALL_REQUESTS_SEC)
|
|
{
|
|
done = 0;
|
|
String statesLog;
|
|
|
|
// This implementation is not perfect. While iterating
|
|
// through the vector, processed async objects state could change in the meantime!
|
|
// Minimizing the processing in this loop is a best effort...
|
|
// (copying allRequests would have no benefit as the AsyncResult instances would perform shallow copies)
|
|
for (auto& request : allRequests)
|
|
{
|
|
if (request.second == 'F') // Request already finished and checked
|
|
{
|
|
continue;
|
|
}
|
|
// Update status
|
|
request.second = AsyncResultBaseProxy::isCurrentlyDelayed(request.first) ? 'D' : (request.first.hasFailed() || request.first.hasSucceeded()) ? 'X' : '.';
|
|
}
|
|
|
|
uint32 processingCount = 0;
|
|
bool hasNewRequestFinished = false;
|
|
for (auto& request : allRequests)
|
|
{
|
|
processingCount = processingCount + (request.second == '.');
|
|
if (request.second == 'F')
|
|
{
|
|
done++;
|
|
}
|
|
if (request.second == 'X')
|
|
{
|
|
done++;
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(request.first));
|
|
EXPECT_EQ(200, reinterpret_cast<const AsyncResult<HttpResponse>&>(request.first).getResult().getStatusCode());
|
|
request.second = 'F';
|
|
hasNewRequestFinished = true;
|
|
}
|
|
statesLog = statesLog + String::formatText("%c", request.second);
|
|
}
|
|
EXPECT_LE(processingCount, HttpClientImpl_BF::s_MAX_CONCURRENT_US_CALLS) << "States: " << statesLog.getUtf8() << " | Done = " << done;
|
|
if (hasNewRequestFinished)
|
|
{
|
|
//PRINT_LINE("States [%s], done = %d", statesLog.getUtf8(), done);
|
|
}
|
|
}
|
|
EXPECT_EQ(allRequests.size(), done) << MAX_TIME_FOR_ALL_REQUESTS_SEC << " seconds timeout reached";
|
|
}
|
|
|
|
// This test is used to verify that there is no limit on the number
|
|
// of paralel HTTP requests done to hosts other than ubiservices.ubi.com
|
|
TEST_F(HTTP_CLIENT, US_HttpClientRequestLimitCustom)
|
|
{
|
|
const uint32 DELAY = 2;
|
|
const uint32 REQUEST_COUNT = 10;
|
|
Vector<AsyncResult<HttpResponse> > allRequests;
|
|
|
|
Facade facade;
|
|
|
|
// This URL should work over HTTP from any office.
|
|
// Note: It might stop working someday as the server was moved in the cloud, but at the moment using our HttpBox URL is the best solution.
|
|
const StringUrl url = "http://msr-d-us-iis01.ubisoft.onbe/UbiServices.HttpBox.Services/v1/httpbox/get?" + String::formatText("delay=%d", DELAY);
|
|
HttpRetryConfig config(0, HttpRetryConfig::RetryParam(), HttpRetryConfig::TimeoutParam(30000ul));
|
|
for (uint32 i = 0; i < REQUEST_COUNT; ++i)
|
|
{
|
|
allRequests.push_back(facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()), config));
|
|
}
|
|
|
|
US_TEST_NS::Helper::sleep((DELAY/2)*1000);
|
|
|
|
uint32 delayed = 0;
|
|
for (Vector<AsyncResult<HttpResponse> >::const_iterator it = allRequests.begin(); it != allRequests.end(); ++it)
|
|
{
|
|
if (AsyncResultBaseProxy::isCurrentlyDelayed(*it))
|
|
delayed++;
|
|
}
|
|
|
|
//PRINT_LINE("BEFORE");
|
|
#if defined(US_TARGET_SWITCH) // Only SWITCH console requires to limit the number of concurrent HTTPS requests (custom or not).
|
|
EXPECT_EQ(REQUEST_COUNT - HttpClientImpl_BF::s_MAX_CONCURRENT_US_CALLS, delayed) <<
|
|
"The SWITCH requires to limit the number of concurrent HTTPS requests (custom or not).";
|
|
#else
|
|
EXPECT_EQ(0, delayed);
|
|
#endif
|
|
//PRINT_LINE("AFTER");
|
|
|
|
for (Vector<AsyncResult<HttpResponse> >::const_iterator it = allRequests.begin(); it != allRequests.end(); ++it)
|
|
{
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(*it));
|
|
EXPECT_EQ(200, reinterpret_cast<const AsyncResult<HttpResponse>&>(*it).getResult().getStatusCode());
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(US_TARGET_WINDOWS) && !defined(US_DLL)
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertWithoutKeysAndObfuscator)
|
|
{
|
|
// If public-UbiServices, verification is automatic
|
|
HttpGet requestUbiservices(TestSslCert_BF::UBISERVICES_UAT_PUBLIC_SSL_URL, TestHelpers_BF::getHttpBoxHeader());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(requestUbiservices)));
|
|
|
|
// For non public-UbiServices URL, if both keys and obfuscator are not specified, no verification is required (see testSslCertificate.cpp)
|
|
// By default if there aren't any verifications, the request will always be sent with success.
|
|
HttpGet requestAny(TestSslCert_BF::UBISERVICES_DEV_SSL_URL, TestHelpers_BF::getHttpBoxHeader());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(requestAny)));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateRequestWithEmptyKeysAndObfuscator)
|
|
{
|
|
// Any URL will work since the request is not sent, we only want to verify empty request settings
|
|
HttpGet requestAny(TestSslCert_BF::UBISERVICES_DEV_SSL_URL, TestHelpers_BF::getHttpBoxHeader());
|
|
// A request setting is valid only if both keys and obfuscator are not empty
|
|
ASSERT_FALSE(TestHttpClient_BF::setRequestWithEmptyKeysAndObfuscator(requestAny, true, true));
|
|
ASSERT_FALSE(TestHttpClient_BF::setRequestWithEmptyKeysAndObfuscator(requestAny, false, true));
|
|
ASSERT_FALSE(TestHttpClient_BF::setRequestWithEmptyKeysAndObfuscator(requestAny, true, false));
|
|
ASSERT_TRUE(TestHttpClient_BF::setRequestWithEmptyKeysAndObfuscator(requestAny, false, false));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertWithEmptyKeysAndObfuscator)
|
|
{
|
|
// If UbiServices, verification is automatic
|
|
HttpGet requestUbiservices(TestSslCert_BF::UBISERVICES_UAT_PUBLIC_SSL_URL, TestHelpers_BF::getHttpBoxHeader());
|
|
ASSERT_FALSE(TestHttpClient_BF::setRequestWithEmptyKeysAndObfuscator(requestUbiservices, true, true));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(requestUbiservices)));
|
|
|
|
// For non public-UbiServices URL, if both keys and obfuscator are empty, verification is not possible
|
|
// By default if there aren't any verifications, the request will always be sent with success.
|
|
HttpGet requestAny(TestSslCert_BF::UBISERVICES_DEV_SSL_URL, TestHelpers_BF::getHttpBoxHeader());
|
|
ASSERT_FALSE(TestHttpClient_BF::setRequestWithEmptyKeysAndObfuscator(requestAny, true, true));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(requestAny)));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertWithInvalidKeysAndObfuscator)
|
|
{
|
|
// These cases only apply to custom URL, since with UbiServices, verification is automatic
|
|
HttpGet requestAny("https://google.com", TestHelpers_BF::getHttpBoxHeader());
|
|
|
|
// setRequestWithCustomKeysAndObfuscator() will always return true
|
|
// since both keys and obfuscator are not empty, they're just invalid.
|
|
|
|
// invalid key, invalid obfuscator
|
|
ASSERT_TRUE(TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(requestAny, false, false));
|
|
ASSERT_FALSE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(requestAny)));
|
|
|
|
// valid key, invalid obfuscator
|
|
ASSERT_TRUE(TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(requestAny, true, false));
|
|
ASSERT_FALSE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(requestAny)));
|
|
|
|
// invalid key, valid obfuscator
|
|
ASSERT_TRUE(TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(requestAny, false, true));
|
|
ASSERT_FALSE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(requestAny)));
|
|
|
|
// valid key, valid obfuscator -> This does not work with non-ubiservices url since the keys are ubiservices only.
|
|
//ASSERT_TRUE(TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(requestAny, true));
|
|
//ASSERT_TRUE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(requestAny)));
|
|
}
|
|
|
|
// Validation only works on PC because ussdkcertvalidation server
|
|
// certificate is not signed by a trusted CA on consoles.
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertChainBadIntermediate)
|
|
{
|
|
HttpGet request("https://ussdkcertvalidation.ubi.com:8443", TestHelpers_BF::getHttpBoxHeader());
|
|
// Need to provide at least one pinning key to activate pinning
|
|
// because url is not pointing to our default url, and our keys
|
|
// won't be added automatically !
|
|
TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(request, true, true);
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(Facade().getHttpClient().sendRequest(request), Errors::SSL_CERTIFICATE_INVALID_SIGNATURE));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertChainBadIssuer)
|
|
{
|
|
HttpGet request("https://ussdkcertvalidation.ubi.com:8444", TestHelpers_BF::getHttpBoxHeader());
|
|
|
|
// Need to provide at least one pinning key to activate pinning
|
|
// because url is not pointing to our default url, and our keys
|
|
// won't be added automatically !
|
|
TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(request, true, true);
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(Facade().getHttpClient().sendRequest(request), Errors::SSL_CERTIFICATE_INVALID_SIGNATURE));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertLeafCN)
|
|
{
|
|
HttpGet request("https://ussdkcertvalidation.ubi.com:8445", TestHelpers_BF::getHttpBoxHeader());
|
|
|
|
// Need to provide at least one pinning key to activate pinning
|
|
// because url is not pointing to our default url, and our keys
|
|
// won't be added automatically !
|
|
TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(request, true, true);
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(Facade().getHttpClient().sendRequest(request), Errors::SSL_CERTIFICATE_INVALID));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertDateNotYetValid)
|
|
{
|
|
HttpGet request("https://ussdkcertvalidation.ubi.com:8447", TestHelpers_BF::getHttpBoxHeader());
|
|
|
|
// Need to provide at least one pinning key to activate pinning
|
|
// because url is not pointing to our default url, and our keys
|
|
// won't be added automatically !
|
|
TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(request, false, true, true);
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(Facade().getHttpClient().sendRequest(request), Errors::SSL_CERTIFICATE_ULTERIOR));
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertDateExpired)
|
|
{
|
|
HttpGet request("https://ussdkcertvalidation.ubi.com:8448", TestHelpers_BF::getHttpBoxHeader());
|
|
|
|
// Need to provide at least one pinning key to activate pinning
|
|
// because url is not pointing to our default url, and our keys
|
|
// won't be added automatically !
|
|
TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(request, true, true);
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(Facade().getHttpClient().sendRequest(request), Errors::SSL_CERTIFICATE_EXPIRED));
|
|
}
|
|
|
|
/// @test Checks that an error is returned when the certificate does not contain the expected public key.
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateCertPinningWrongPublicKey)
|
|
{
|
|
HttpGet request("https://ussdkcertvalidation.ubi.com:8449", TestHelpers_BF::getHttpBoxHeader());
|
|
|
|
// Need to provide at least one pinning key to activate pinning
|
|
// because url is not pointing to our default url, and our keys
|
|
// won't be added automatically !
|
|
TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(request, true, true);
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(Facade().getHttpClient().sendRequest(request), Errors::SSL_CERTIFICATE_INVALID));
|
|
}
|
|
|
|
|
|
/// @test Checks that the hardcoded obfuscated public keys are valid and functional
|
|
/// Return an error when the certificate does not contain the expected public key.
|
|
/// --- Important Note ---
|
|
/// Key indexes are taken from UBISERVICES_SSL_ALLOWED_PUBLIC_KEY_OBFUSCATED_BASE64 in InstanceManager.cpp
|
|
///
|
|
/// @disabled certificates must be renewed for this test to work. See JIRA US-15709 to reenable this test
|
|
TEST_F(HTTP_CLIENT, SSL_ValidateLiveAndBackupCertificatePinningKeys)
|
|
{
|
|
struct pinningTestData
|
|
{
|
|
String url;
|
|
uint32 keyIndex;
|
|
OnlineAccessContext context;
|
|
bool testEnabled;
|
|
};
|
|
|
|
// *.ubi.com backup keys tests are disabled
|
|
// Certificates need to be updated !!!
|
|
Vector<pinningTestData> testUrls = {
|
|
{ "https://public-ubiservices.ubi.com/v1/status", 0, OnlineAccessContext::Standard, true }, // Live *.ubi.com
|
|
{ "https://ussdkcertvalidation.ubisoft.cn:8474", 1, OnlineAccessContext::China, true }, // Live *.ubisoft.cn
|
|
{ "https://ussdkcertvalidation.ubi.com:8471", 2, OnlineAccessContext::Standard, false },// Backup 1 *.ubi.com
|
|
{ "https://ussdkcertvalidation.ubisoft.cn:8475", 3, OnlineAccessContext::China, true }, // Backup 1 *.ubisoft.cn
|
|
{ "https://ussdkcertvalidation.ubi.com:8472", 4, OnlineAccessContext::Standard, false },// Backup 2 *.ubi.com
|
|
{ "https://ussdkcertvalidation.ubisoft.cn:8476", 5, OnlineAccessContext::China, true }, // Backup 2 *.ubisoft.cn
|
|
{ "https://ussdkcertvalidation.ubi.com:8473", 6, OnlineAccessContext::Standard, false },// Backup 3 *.ubi.com
|
|
{ "https://ussdkcertvalidation.ubisoft.cn:8477", 7, OnlineAccessContext::China, true } // Backup 3 *.ubisoft.cn
|
|
};
|
|
|
|
for (pinningTestData testData : testUrls)
|
|
{
|
|
if (!testData.testEnabled)
|
|
{
|
|
PRINT_LINE("%s test is disabled", testData.url.getUtf8());
|
|
continue;
|
|
}
|
|
|
|
PRINT_LINE("Testing %s in pinning key index %d in OnlineAccedContext %s", testData.url.getUtf8(),
|
|
testData.keyIndex, testData.context == OnlineAccessContext::Standard ? "Standard" : "China");
|
|
|
|
HttpGet request(testData.url, TestHelpers_BF::getHttpBoxHeader());
|
|
TestHttpClient_BF::setRequestWithCustomKeysAndObfuscator(request, true, true, testData.keyIndex);
|
|
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(Facade().getHttpClient().sendRequest(request)))
|
|
<< "Pinning failed for URL: " << testData.url.getUtf8();
|
|
}
|
|
}
|
|
|
|
#endif // US_TARGET_WINDOWS
|
|
|
|
/// @test Validates the SSL certificate over the managed UbiServices URL and for each environment.
|
|
/// @note For these URLs, it's irrelevant what the requests return (forbidden code for most of them).
|
|
/// We just want to make sure SSL can connect to the server.
|
|
/// @disabled SSL Tests are useless for Xbox One and Xbox 360 since we do not have control over the enforcement.
|
|
#if !defined(US_TARGET_XONE)
|
|
|
|
TEST_F(HTTP_CLIENT_NO_INIT, US_HttpsValidateCertificate)
|
|
{
|
|
const US_TEST_NS::ScopedInit autoInit;
|
|
|
|
// This test requires HTTPS and SSL validation
|
|
#if !defined(US_RETAIL)
|
|
s_forceNonSecure = false;
|
|
#endif
|
|
{
|
|
HttpConfig httpConfig;
|
|
|
|
#if defined(US_TARGET_WINDOWS) || defined(US_TARGET_OSX) || defined(US_TARGET_LINUX) || defined(US_TARGET_GGP)
|
|
httpConfig.m_proxy = HttpProxyConfig(US_TEST_NS::PROXY_NAME, US_TEST_NS::PROXY_PORT);
|
|
#endif
|
|
const GameConfig gameConfig(
|
|
ApplicationId(NS_COMMON::Options::getApplicationId().c_str()),
|
|
NS_COMMON::Options::getApplicationBuildId().c_str()
|
|
#if defined(US_SDK_S2S)
|
|
, NS_COMMON::Options::environment()
|
|
#endif
|
|
, OnlineAccessContext::Standard
|
|
#if !defined(US_SDK_S2S)
|
|
, GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector<EventTypeInfo>())
|
|
#endif
|
|
#if defined(US_TARGET_PS4) || defined(US_TARGET_SWITCH) || defined(US_TARGET_XONE) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI)
|
|
, GameConfigConsole(NS_COMMON::FIRSTPARTY_APP_ID)
|
|
#endif
|
|
);
|
|
|
|
const SystemConfig systemConfig(ThreadingConfig(), httpConfig);
|
|
configureSdk(gameConfig, systemConfig);
|
|
}
|
|
|
|
{
|
|
Facade facade;
|
|
|
|
const Vector<StringUrl> urls
|
|
{
|
|
//UBISERVICES.ubi.com
|
|
"https://dev-public-ubiservices.ubi.com",
|
|
"https://uat-public-ubiservices.ubi.com",
|
|
"https://cert2-public-ubiservices.ubi.com",
|
|
"https://public-ubiservices.ubi.com", //prod
|
|
|
|
//UPLAY.ubi.com
|
|
"https://dev-wspuplay-ext.ubi.com",
|
|
"https://uat-uplay-ext.ubi.com",
|
|
"https://cert2-wspuplay-ext.ubi.com",
|
|
"https://wspuplay-ext.ubi.com" //prod
|
|
};
|
|
|
|
for (const auto& urlIt : urls)
|
|
{
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(urlIt, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(result.wait(NS_COMMON::DEFAULT_WAIT_TIME));
|
|
|
|
// If there's an error, it shouldn't be a SSL certificate one.
|
|
if(result.hasFailed())
|
|
{
|
|
const uint32 code = static_cast<uint32>(result.getError().m_code);
|
|
EXPECT_FALSE(code >= Errors::SSL_GENERIC_FAILURE && code < Errors::SSL_END_SECTION_GUARD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// @test Performs an HTTP request retrieving a gzip body.
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_HttpGetEncodingGzip)
|
|
{
|
|
setupDebugLogging();
|
|
const String typeOfEncoding = "gzip";
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", "", true);
|
|
HttpHeader headers = TestHelpers_BF::getHttpBoxHeader();
|
|
headers["Accept-Encoding"] = typeOfEncoding;
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, headers));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_EQ(RestStatusCode::OK, response.getStatusCode());
|
|
EXPECT_GT(response.getBodySize(), 0u);
|
|
|
|
//PRINT_STREAM("Response: " << response);
|
|
|
|
const String& contentEncoding = response.getHeader("Content-Encoding");
|
|
const String& contentLength = response.getHeader("Content-Length");
|
|
|
|
// Note: XB1/PS4/WINAPPSTORE automatically decompresses gzipped body
|
|
// The Content-Length and the Content-Encoding should reflect this according to RFC 2616.
|
|
#if defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_UWP) || defined(US_TARGET_KILI)
|
|
// Note : Checking the exact size of what is received is useful to ensure the uncompressed gzip received
|
|
// has the expected size. However, there is currently no known way to get a response with a body with a constant length
|
|
// Current test returns the headers sent, which depends on the platform, the firmware version, and others...
|
|
// JIRA US-8156 to reenable this line
|
|
// EXPECT_EQ(331u, response.getBodySize());
|
|
|
|
// Note : In case of bug, HttpResponse::getBodySize() could differ from HttpResponse::getBodyAsString().getLength()
|
|
// e.g. JIRA US-7419
|
|
const String body = response.getBodyAsString();
|
|
EXPECT_EQ(response.getBodySize(), body.getLength());
|
|
EXPECT_EQ("", contentEncoding);
|
|
EXPECT_EQ("", contentLength);
|
|
EXPECT_TRUE(Json(body).isValid());
|
|
#else // The other platforms do not uncompress the zipped body.
|
|
// http://stackoverflow.com/questions/9487752/decoding-compressed-response-libcurl
|
|
EXPECT_EQ(typeOfEncoding, contentEncoding);
|
|
EXPECT_NE(contentLength, "");
|
|
|
|
#endif
|
|
}
|
|
|
|
/// @test Performs an HTTP request retrieving a deflate body.
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_HttpGetEncodingDeflate)
|
|
{
|
|
setupDebugLogging();
|
|
const String typeOfEncoding = "deflate";
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", "", true);
|
|
HttpHeader headers = TestHelpers_BF::getHttpBoxHeader();
|
|
headers["Accept-Encoding"] = typeOfEncoding;
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, headers));
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
const HttpResponse& response = result.getResult();
|
|
EXPECT_EQ(RestStatusCode::OK, response.getStatusCode());
|
|
EXPECT_GT(response.getBodySize(), 0u);
|
|
|
|
//PRINT_STREAM("Response: " << response);
|
|
|
|
const String& contentEncoding = response.getHeader("Content-Encoding");
|
|
const String& contentLength = response.getHeader("Content-Length");
|
|
|
|
// Note: XB1/WINAPPSTORE automatically decompresses gzipped body
|
|
// The Content-Length and the Content-Encoding should reflect this according to RFC 2616.
|
|
#if defined(US_TARGET_XONE) || defined(US_TARGET_UWP)
|
|
// Note : Checking the exact size of what is received is useful to ensure the uncompressed gzip received
|
|
// has the expected size. However, there is currently no known way to get a response with a body with a constant length
|
|
// Current test returns the headers sent, which depends on the platform, the firmware version, and others...
|
|
// JIRA US-8156 to reenable this line
|
|
// EXPECT_EQ(331u, response.getBodySize());
|
|
|
|
// Note : In case of bug, HttpResponse::getBodySize() could differ from HttpResponse::getBodyAsString().getLength()
|
|
// e.g. JIRA US-7419
|
|
const String body = response.getBodyAsString();
|
|
EXPECT_EQ(response.getBodySize(), body.getLength());
|
|
EXPECT_EQ("", contentEncoding);
|
|
EXPECT_EQ("", contentLength);
|
|
EXPECT_TRUE(Json(body).isValid());
|
|
#else // The other platforms do not uncompress the zipped body.
|
|
// http://stackoverflow.com/questions/9487752/decoding-compressed-response-libcurl
|
|
EXPECT_EQ(typeOfEncoding, contentEncoding);
|
|
EXPECT_NE(contentLength, "");
|
|
#endif
|
|
}
|
|
|
|
/// @test Verifies the maximum URL length the client can process on the platform.
|
|
/// HttpBin returns a 404 code (not found) if the URL is treated by the server,
|
|
/// else it returns a 400 bad request (Request Line is too large).
|
|
TEST_F(HTTP_CLIENT, DISABLED_MANUAL_URLMaximumLength)
|
|
{
|
|
char const URL_BASE[] = "http://httpbin.org/";
|
|
const size_t URL_LENGTH_MINIMUM = 2048u;
|
|
const size_t URL_STEPPING_LENGTH = 64u;
|
|
const std::string filler(URL_LENGTH_MINIMUM + (URL_STEPPING_LENGTH - (sizeof(URL_BASE) - 1)), 'a');
|
|
StringUrl url = StringUrl(URL_BASE) + filler.c_str();
|
|
EXPECT_EQ(URL_LENGTH_MINIMUM + URL_STEPPING_LENGTH, url.getLength());
|
|
|
|
Facade facade;
|
|
|
|
{
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::Error404, result.getResult().getStatusCode()) <<
|
|
"The client should support URL's length of at least " << URL_LENGTH_MINIMUM;
|
|
}
|
|
|
|
while (SystemChecker::GetTrue())
|
|
{
|
|
url += std::string(URL_STEPPING_LENGTH, 'a').c_str();
|
|
|
|
AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
if (result.getResult().getStatusCode() == RestStatusCode::Error404)
|
|
{
|
|
if (url.getLength() > 0xFFFF)
|
|
{
|
|
PRINT_LINE("Processed size can be greater than %d. Validation ending.", url.getLength());
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
PRINT_LINE("Succesfully Processed size: %d", url.getLength());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (result.getResult().getStatusCode() != RestStatusCode::Error404)
|
|
{
|
|
url = url.truncateTo(url.getLength() - 1);
|
|
result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(result));
|
|
}
|
|
|
|
PRINT_LINE("Error code returned: %d", result.getResult().getStatusCode());
|
|
PRINT_LINE("URL Maximum size processed: %d", (url.getLength() - 1));
|
|
#if defined(US_TARGET_WINDOWS)
|
|
EXPECT_EQ(8210u, url.getLength());
|
|
#else
|
|
// TODO
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// @test Checks the support for the HTTP status codes
|
|
TEST_F(HTTP_CLIENT, DISABLED_MANUAL_HttpStatusCodes)
|
|
{
|
|
Facade facade;
|
|
|
|
const Vector<uint32> statusCodes
|
|
{
|
|
RestStatusCode::Error304,
|
|
RestStatusCode::Error406,
|
|
RestStatusCode::Error416,
|
|
RestStatusCode::Error501,
|
|
RestStatusCode::Error505
|
|
};
|
|
|
|
for (const auto& code : statusCodes)
|
|
{
|
|
{
|
|
StringUrl url = StringUrl("http://httpbin.org/status/" + String::getText(code));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader()));
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(code, result.getResult().getStatusCode()) << "The HTTP client should support status code " << code;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(US_TARGET_PS4) || defined(US_TARGET_KILI)
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_HttpSslPoolsUsage)
|
|
{
|
|
Facade facade;
|
|
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("post");
|
|
const HttpHeader header = TestHelpers_BF::getHttpContentHeader();
|
|
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpPost(url, header, ""));
|
|
while (result.isProcessing())
|
|
{
|
|
HttpSslPoolsUsage memoryPools = facade.getHttpClient().getHttpSslPoolsUsage();
|
|
ASSERT_TRUE(memoryPools.m_httpPoolSizeInBytes != 0);
|
|
ASSERT_TRUE(memoryPools.m_sslPoolSizeInBytes != 0);
|
|
|
|
PRINT_STREAM("HTTP Memory pool usage : " << memoryPools.m_httpBytesInUse << "/" << memoryPools.m_httpPoolSizeInBytes);
|
|
PRINT_STREAM("SSL Memory pool usage : " << memoryPools.m_sslBytesInUse << "/" << memoryPools.m_sslPoolSizeInBytes);
|
|
|
|
US_NS::Helper::sleep(10);
|
|
}
|
|
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
}
|
|
#endif
|
|
|
|
#if defined(US_TARGET_XONE)
|
|
/// @test Checks that changing the host header in a xml request works correctly
|
|
TEST_F(HTTP_CLIENT, HTTPBOX_PerformHttpGETWithDifferentHost)
|
|
{
|
|
Facade facade;
|
|
const StringUrl url = TestHelpers_BF::getHttpTestUrl("get");
|
|
HttpHeader header = TestHelpers_BF::getHttpBoxHeader();
|
|
// Because we have the property "XHR_PROP_NO_DEFAULT_HEADERS", the request will use the Host header and fail.
|
|
// Without that property the request would use the destination Host and succeed.
|
|
header.addHeader("Host", "randomtesthost");
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url, header));
|
|
ASSERT_TRUE(AsyncHelper::waitFailure(result,Errors::HTTP_REQUEST_TRANSFER_FAILURE));
|
|
}
|
|
#endif
|
|
|
|
#if defined(US_TARGET_OSX) || defined(US_TARGET_IOS)
|
|
// Note: The test was disabled as it fails most of the time on the build farm.
|
|
// Could it may be a wrong(network) configuration on some agents? [US-15052]
|
|
TEST_F(HTTP_CLIENT_NO_INIT, DISABLED_USNOT_SendRequestUsingGlobalAutoDiscovery)
|
|
#else
|
|
TEST_F(HTTP_CLIENT_NO_INIT, USNOT_SendRequestUsingGlobalAutoDiscovery)
|
|
#endif
|
|
{
|
|
const US_TEST_NS::ScopedInit autoInit;
|
|
{
|
|
HttpConfig httpConfig;
|
|
httpConfig.m_proxy.m_proxyType = HttpProxyConfig::Type::AutoProxy;
|
|
|
|
// This code sets the environment variable all_proxy so that libcurl can detect it automatically.
|
|
#if defined(US_TARGET_WINDOWS)
|
|
{
|
|
_putenv_s("all_proxy", US_TEST_NS::ProxyHelper::findAutomaticProxySettings(TestHttpClient_BF::URL_INTERNET).getUtf8());
|
|
}
|
|
#endif
|
|
|
|
const GameConfig gameConfig(
|
|
ApplicationId(NS_COMMON::Options::getApplicationId().c_str()),
|
|
NS_COMMON::Options::getApplicationBuildId().c_str()
|
|
#if defined(US_SDK_S2S)
|
|
, NS_COMMON::Options::environment()
|
|
#endif
|
|
, OnlineAccessContext::Standard
|
|
#if !defined(US_SDK_S2S)
|
|
, GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector<EventTypeInfo>())
|
|
#endif
|
|
#if defined(US_TARGET_PS4) || defined(US_TARGET_SWITCH) || defined(US_TARGET_XONE) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI)
|
|
, GameConfigConsole(NS_COMMON::FIRSTPARTY_APP_ID)
|
|
#endif
|
|
);
|
|
|
|
configureSdk(gameConfig, SystemConfig(ThreadingConfig(), httpConfig));
|
|
}
|
|
{
|
|
const AsyncResult<HttpResponse> result = Facade().getHttpClient().sendRequest(HttpGet(TestHttpClient_BF::URL_INTERNET));
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT_NO_INIT, USNOT_SendRequestUsingGlobalProxyInfo)
|
|
{
|
|
const US_TEST_NS::ScopedInit autoInit;
|
|
{
|
|
HttpConfig httpConfig;
|
|
httpConfig.m_proxy = HttpProxyConfig(US_TEST_NS::PROXY_NAME, US_TEST_NS::PROXY_PORT);
|
|
httpConfig.m_proxy.m_proxyType = HttpProxyConfig::Type::Enabled;
|
|
|
|
const GameConfig gameConfig(
|
|
ApplicationId(NS_COMMON::Options::getApplicationId().c_str()),
|
|
NS_COMMON::Options::getApplicationBuildId().c_str()
|
|
#if defined(US_SDK_S2S)
|
|
, NS_COMMON::Options::environment()
|
|
#endif
|
|
, OnlineAccessContext::Standard
|
|
#if !defined(US_SDK_S2S)
|
|
, GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector<EventTypeInfo>())
|
|
#endif
|
|
#if defined(US_TARGET_PS4) || defined(US_TARGET_SWITCH) || defined(US_TARGET_XONE) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI)
|
|
, GameConfigConsole(NS_COMMON::FIRSTPARTY_APP_ID)
|
|
#endif
|
|
);
|
|
|
|
configureSdk(gameConfig, SystemConfig(ThreadingConfig(), httpConfig));
|
|
}
|
|
{
|
|
const AsyncResult<HttpResponse> result = Facade().getHttpClient().sendRequest(HttpGet(TestHttpClient_BF::URL_INTERNET));
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
}
|
|
|
|
#if defined(US_USE_LIBCURL) && !defined(US_TARGET_SWITCH) // The Switch ignores libcurl proxy settings.
|
|
TEST_F(HTTP_CLIENT_NO_INIT, USNOT_SendRequestUsingGlobalProxyInfoWrong)
|
|
{
|
|
const US_TEST_NS::ScopedInit autoInit;
|
|
{
|
|
HttpConfig httpConfig;
|
|
httpConfig.m_proxy = HttpProxyConfig("BadProxyInfo", US_TEST_NS::PROXY_PORT, String(), String(), HttpProxyConfig::Type::Enabled);
|
|
|
|
const GameConfig gameConfig(
|
|
ApplicationId(NS_COMMON::Options::getApplicationId().c_str()),
|
|
NS_COMMON::Options::getApplicationBuildId().c_str()
|
|
#if defined(US_SDK_S2S)
|
|
, NS_COMMON::Options::environment()
|
|
#endif
|
|
, OnlineAccessContext::Standard
|
|
#if !defined(US_SDK_S2S)
|
|
, GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector<EventTypeInfo>())
|
|
#endif
|
|
#if defined(US_TARGET_PS4) || defined(US_TARGET_SWITCH) || defined(US_TARGET_XONE) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI)
|
|
, GameConfigConsole(NS_COMMON::FIRSTPARTY_APP_ID)
|
|
#endif
|
|
);
|
|
|
|
configureSdk(gameConfig, SystemConfig(ThreadingConfig(), httpConfig));
|
|
}
|
|
{
|
|
EXPECT_TRUE(AsyncHelper::waitFailure(Facade().getHttpClient().sendRequest(HttpGet(TestHttpClient_BF::URL_INTERNET)), Errors::HTTP_REQUEST_TRANSFER_FAILURE));
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(US_ENABLE_PROXY_CONFIG_PER_REQ)
|
|
TEST_F(HTTP_CLIENT, USNOT_SendRequestUsingRequestAutoProxy)
|
|
{
|
|
Facade facade;
|
|
HttpGet request(TestHttpClient_BF::URL_INTERNET);
|
|
HttpProxyConfig proxyConfig;
|
|
proxyConfig.m_proxyType = HttpProxyConfig::Type::AutoProxy;
|
|
|
|
// This code sets the environment variable all_proxy so that libcurl can detect it automatically.
|
|
#if defined(US_TARGET_WINDOWS)
|
|
{
|
|
_putenv_s("all_proxy", US_TEST_NS::ProxyHelper::findAutomaticProxySettings(TestHttpClient_BF::URL_INTERNET).getUtf8());
|
|
}
|
|
#endif
|
|
|
|
request.setProxyInfo(proxyConfig);
|
|
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(request);
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, USNOT_SendRequestUsingRequestProxyEnabled)
|
|
{
|
|
{
|
|
Facade facade;
|
|
HttpGet request(TestHttpClient_BF::URL_INTERNET);
|
|
HttpProxyConfig proxyConfig = HttpProxyConfig(US_TEST_NS::PROXY_NAME, US_TEST_NS::PROXY_PORT);
|
|
proxyConfig.m_proxyType = HttpProxyConfig::Type::Enabled;
|
|
request.setProxyInfo(proxyConfig);
|
|
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(request);
|
|
EXPECT_TRUE(AsyncHelper::waitSuccess(result));
|
|
EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
{
|
|
Facade facade;
|
|
HttpGet request(TestHttpClient_BF::URL_INTERNET);
|
|
HttpProxyConfig proxyConfig = HttpProxyConfig(US_TEST_NS::PROXY_NAME, US_TEST_NS::PROXY_PORT);
|
|
proxyConfig.m_proxyType = HttpProxyConfig::Type::Disabled;
|
|
request.setProxyInfo(proxyConfig);
|
|
|
|
EXPECT_TRUE(AsyncHelper::waitFailure(facade.getHttpClient().sendRequest(request), Errors::HTTP_TIMEOUT));
|
|
//EXPECT_EQ(RestStatusCode::OK, result.getResult().getStatusCode());
|
|
}
|
|
{
|
|
Facade facade;
|
|
HttpGet request("http://annahar.com/"); // URL provided by elias.chaoul@ubisoft.com for proxy tests.
|
|
HttpProxyConfig proxyConfig;
|
|
proxyConfig.m_proxyType = HttpProxyConfig::Type::Disabled;
|
|
request.setProxyInfo(proxyConfig);
|
|
|
|
EXPECT_TRUE(AsyncHelper::waitFailure(facade.getHttpClient().sendRequest(request), Errors::HTTP_TIMEOUT));
|
|
}
|
|
}
|
|
|
|
TEST_F(HTTP_CLIENT, USNOT_SendRequestUsingRequestProxyWrong)
|
|
{
|
|
Facade facade;
|
|
HttpGet request(TestHttpClient_BF::URL_INTERNET);
|
|
HttpProxyConfig proxyConfig = HttpProxyConfig("BadProxyUrl", US_TEST_NS::PROXY_PORT);
|
|
proxyConfig.m_proxyType = HttpProxyConfig::Type::Enabled;
|
|
request.setProxyInfo(proxyConfig);
|
|
|
|
EXPECT_TRUE(AsyncHelper::waitFailure(facade.getHttpClient().sendRequest(request), Errors::HTTP_REQUEST_TRANSFER_FAILURE));
|
|
}
|
|
|
|
#endif // defined(US_ENABLE_PROXY_CONFIG_PER_REQ)
|
|
|
|
#if defined(US_SDK_S2S)
|
|
// This test make sure that any request can be signed and not just "remotelog" / "ubiservices.ubi.com" domain. This test
|
|
// use Httpbin because we want to hit a domain that is outside of Ubisoft and it's not possible to do that with HttpBox.
|
|
TEST_F(HTTP_CLIENT, HTTPBIN_PerformSignedHttpGETOutsideUbisoftDomain)
|
|
{
|
|
Facade facade;
|
|
const AsyncResult<Empty> resultLogin = facade.createSession(GET_CREDENTIALS(), {}, {}, GET_AUTHENTICATION_CERTIFICATE_PRIVATE());
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(resultLogin));
|
|
SessionInfo sessionInfo = facade.getAuthenticationClient().getSessionInfo();
|
|
ASSERT_TRUE(sessionInfo.getSessionId().isValid());
|
|
|
|
const StringUrl url = "http://httpbin.org/headers"; // Return the incoming request's HTTP headers.
|
|
|
|
// Using a custom request timeout to make sure this test won't fail if external site is very slow to respond.
|
|
HttpRetryConfig retryConfig = HttpRetryConfig(0, HttpRetryConfig::RetryParam(), HttpRetryConfig::TimeoutParam(60000));
|
|
const AsyncResult<HttpResponse> result = facade.getHttpClient().sendRequest(HttpGet(url), retryConfig, HttpRequestSignType::Hmac);
|
|
ASSERT_TRUE(AsyncHelper::waitSuccess(result));
|
|
|
|
const HttpResponse& response = result.getResult();
|
|
ASSERT_EQ(RestStatusCode::OK, response.getStatusCode());
|
|
|
|
String body = response.getBodyAsString();
|
|
// These headers are present in our request only if the request is signed
|
|
ASSERT_TRUE(TestHttpClient_BF::validateHttpBoxHeadersInBodyResponse(
|
|
response.getBodyAsString(),
|
|
{ "Ubi-Signature-Timestamp", "Ubi-Signature", "X-Forwarded-Url" }));
|
|
}
|
|
#endif // defined(US_SDK_S2S)
|
|
|
|
#if defined(US_TARGET_WINDOWS)
|
|
#pragma warning(disable:4068) // unknown pragma
|
|
#pragma BullseyeCoverage restore
|
|
#pragma warning(default:4068)
|
|
#endif
|
|
|
|
};
|