Initial client merge (#2)
This commit is contained in:
Родитель
b946468a0d
Коммит
4d26f135c9
|
@ -0,0 +1,4 @@
|
|||
packages/
|
||||
Debug/
|
||||
Release/
|
||||
.vs/
|
|
@ -0,0 +1,6 @@
|
|||
[submodule "submodules/vcpkg"]
|
||||
path = submodules/vcpkg
|
||||
url = https://github.com/Microsoft/vcpkg
|
||||
[submodule "submodules/googletest"]
|
||||
path = submodules/googletest
|
||||
url = https://github.com/google/googletest
|
|
@ -0,0 +1,19 @@
|
|||
cmake_minimum_required (VERSION 2.8.11)
|
||||
project (signalrclient)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fPIC -L -lcpprest")
|
||||
|
||||
set(CPPREST_INCLUDE_DIR "" CACHE FILEPATH "Path to casablanca include dir")
|
||||
|
||||
include_directories (
|
||||
include
|
||||
"${CPPREST_INCLUDE_DIR}")
|
||||
|
||||
find_library(CPPREST_SO NAMES "cpprest" PATHS ${CPPREST_LIB_DIR} REQUIRED)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
add_subdirectory(src/signalrclient)
|
||||
add_subdirectory(test)
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildThisFileDirectory)Version.props" />
|
||||
<PropertyGroup>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or '$(SolutionDir)' == '*Undefined*'">$(MSBuildThisFileDirectory)..\</SolutionDir>
|
||||
<Configuration Condition="'$(Configuration)'==''">Debug</Configuration>
|
||||
<Platform Condition="'$(Platform)'==''">Win32</Platform>
|
||||
<PlatformToolset Condition=" '$(PlatformToolset)' == '' And '$(VisualStudioVersion)' == '12.0'">v120</PlatformToolset>
|
||||
<PlatformToolset Condition=" '$(PlatformToolset)' == '' And '$(VisualStudioVersion)' == '14.0'">v140</PlatformToolset>
|
||||
<PlatformToolset Condition=" '$(PlatformToolset)' == ''">v140</PlatformToolset>
|
||||
<SubSystem Condition="'$(SubSystem)' != 'UWP'">Desktop</SubSystem>
|
||||
<OutDir Condition="'$(OutDir)' == ''">$(SolutionDir)bin\$(SubSystem)\$(Platform)\$(Configuration)\</OutDir>
|
||||
<SignalrClientTargetName>signalrclient</SignalrClientTargetName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildThisFileDirectory)\Common.Build.Settings" />
|
||||
<PropertyGroup>
|
||||
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">true</DownloadNuGetExe>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(SubSystem)' == 'UWP'">
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<GenerateProjectSpecificOutputFolder>False</GenerateProjectSpecificOutputFolder>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<TreatWarningAsError Condition="'$(TreatWarningsAsErrors)' != ''">true</TreatWarningAsError>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<StringPooling>true</StringPooling>
|
||||
<CompileAsWinRT Condition="'$(SubSystem)' == 'UWP'">true</CompileAsWinRT>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem Condition="'$(SubSystem)' != 'UWP'">Windows</SubSystem>
|
||||
<SubSystem Condition="'$(SubSystem)' == 'UWP'">Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(OutDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<StripPrivateSymbols>$(OutDir)$(TargetName).pub.pdb</StripPrivateSymbols>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<Profile>true</Profile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_WIN64;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_WIN64;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<!-- When changing version remember to update version in $src\signalrclient\constants.h -->
|
||||
<SignalRClientCppVersionMajor>1</SignalRClientCppVersionMajor>
|
||||
<SignalRClientCppVersionMinor>0</SignalRClientCppVersionMinor>
|
||||
<SignalRClientCppVersionPatch>0</SignalRClientCppVersionPatch>
|
||||
<SignalRClientCppVersionSuffix>-alpha0</SignalRClientCppVersionSuffix>
|
||||
<!-- $(build_number) generated by Team City -->
|
||||
<SignalRClientCppVersionSuffix Condition="'$(build_number)' != '' And '$(build_branch)' != 'release'">$(SignalRClientCppVersionSuffix)-$(build_number)</SignalRClientCppVersionSuffix>
|
||||
<SignalRClientCppVersionString>$(SignalRClientCppVersionMajor).$(SignalRClientCppVersionMinor).$(SignalRClientCppVersionPatch)$(SignalRClientCppVersionSuffix)</SignalRClientCppVersionString>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef NO_SIGNALRCLIENT_EXPORTS
|
||||
#define SIGNALRCLIENT_API
|
||||
#else
|
||||
#ifdef SIGNALRCLIENT_EXPORTS
|
||||
#define SIGNALRCLIENT_API __declspec(dllexport)
|
||||
#else
|
||||
#define SIGNALRCLIENT_API __declspec(dllimport)
|
||||
#endif // SIGNALRCLIENT_EXPORTS
|
||||
#endif // NO_SIGNALRCLIENT_EXPORTS
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_exports.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include "connection_state.h"
|
||||
#include "trace_level.h"
|
||||
#include "log_writer.h"
|
||||
#include "signalr_client_config.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class connection_impl;
|
||||
|
||||
class connection
|
||||
{
|
||||
public:
|
||||
typedef std::function<void __cdecl(const std::string&)> message_received_handler;
|
||||
|
||||
SIGNALRCLIENT_API explicit connection(const std::string& url, trace_level trace_level = trace_level::all, std::shared_ptr<log_writer> log_writer = nullptr);
|
||||
|
||||
SIGNALRCLIENT_API ~connection();
|
||||
|
||||
connection(const connection&) = delete;
|
||||
|
||||
connection& operator=(const connection&) = delete;
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl start(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl set_message_received(const message_received_handler& message_received_callback);
|
||||
SIGNALRCLIENT_API void __cdecl set_disconnected(const std::function<void __cdecl()>& disconnected_callback);
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl set_client_config(const signalr_client_config& config);
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl stop(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
|
||||
SIGNALRCLIENT_API connection_state __cdecl get_connection_state() const noexcept;
|
||||
SIGNALRCLIENT_API std::string __cdecl get_connection_id() const;
|
||||
|
||||
private:
|
||||
// The recommended smart pointer to use when doing pImpl is the `std::unique_ptr`. However
|
||||
// we are capturing the m_pImpl instance in the lambdas used by tasks which can outlive
|
||||
// the connection instance. Using `std::shared_ptr` guarantees that we won't be using
|
||||
// a deleted object if the task is run after the `connection` instance goes away.
|
||||
std::shared_ptr<connection_impl> m_pImpl;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
enum class connection_state
|
||||
{
|
||||
connecting,
|
||||
connected,
|
||||
disconnecting,
|
||||
disconnected
|
||||
};
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
enum class http_method
|
||||
{
|
||||
GET,
|
||||
POST
|
||||
};
|
||||
|
||||
class http_request
|
||||
{
|
||||
public:
|
||||
http_method method;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string content;
|
||||
std::chrono::seconds timeout;
|
||||
};
|
||||
|
||||
class http_response
|
||||
{
|
||||
public:
|
||||
http_response() {}
|
||||
http_response(http_response&& rhs) noexcept : status_code(rhs.status_code), content(std::move(rhs.content)) {}
|
||||
http_response(int code, const std::string& content) : status_code(code), content(content) {}
|
||||
|
||||
http_response& operator=(http_response&& rhs)
|
||||
{
|
||||
status_code = rhs.status_code;
|
||||
content = std::move(rhs.content);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
int status_code = 0;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
class http_client
|
||||
{
|
||||
public:
|
||||
virtual void send(std::string url, http_request request, std::function<void(http_response, std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual ~http_client() {}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_exports.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include "cpprest/json.h"
|
||||
#include "connection_state.h"
|
||||
#include "trace_level.h"
|
||||
#include "log_writer.h"
|
||||
#include "signalr_client_config.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class hub_connection_impl;
|
||||
|
||||
class hub_connection
|
||||
{
|
||||
public:
|
||||
typedef std::function<void __cdecl (const web::json::value&)> method_invoked_handler;
|
||||
|
||||
SIGNALRCLIENT_API explicit hub_connection(const std::string& url, trace_level trace_level = trace_level::all,
|
||||
std::shared_ptr<log_writer> log_writer = nullptr);
|
||||
|
||||
SIGNALRCLIENT_API ~hub_connection();
|
||||
|
||||
hub_connection(const hub_connection&) = delete;
|
||||
|
||||
hub_connection& operator=(const hub_connection&) = delete;
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl start(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
SIGNALRCLIENT_API void __cdecl stop(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
|
||||
SIGNALRCLIENT_API connection_state __cdecl get_connection_state() const;
|
||||
SIGNALRCLIENT_API std::string __cdecl get_connection_id() const;
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl set_disconnected(const std::function<void __cdecl()>& disconnected_callback);
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl set_client_config(const signalr_client_config& config);
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl on(const std::string& event_name, const method_invoked_handler& handler);
|
||||
|
||||
SIGNALRCLIENT_API void invoke(const std::string& method_name, const web::json::value& arguments = web::json::value::array(), std::function<void(const web::json::value&, std::exception_ptr)> callback = [](const web::json::value&, std::exception_ptr) {}) noexcept;
|
||||
|
||||
SIGNALRCLIENT_API void send(const std::string& method_name, const web::json::value& arguments = web::json::value::array(), std::function<void(std::exception_ptr)> callback = [](std::exception_ptr) {}) noexcept;
|
||||
|
||||
private:
|
||||
std::shared_ptr<hub_connection_impl> m_pImpl;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include "signalr_exception.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class hub_exception : public signalr_exception
|
||||
{
|
||||
public:
|
||||
hub_exception(const std::string &what)
|
||||
: signalr_exception(what)
|
||||
{}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class log_writer
|
||||
{
|
||||
public:
|
||||
// NOTE: the caller does not enforce thread safety of this call
|
||||
virtual void __cdecl write(const std::string &entry) = 0;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpprest/http_client.h"
|
||||
#include "cpprest/ws_client.h"
|
||||
#include "_exports.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class signalr_client_config
|
||||
{
|
||||
public:
|
||||
SIGNALRCLIENT_API void __cdecl set_proxy(const web::web_proxy &proxy);
|
||||
// Please note that setting credentials does not work in all cases.
|
||||
// For example, Basic Authentication fails under Win32.
|
||||
// As a workaround, you can set the required authorization headers directly
|
||||
// using signalr_client_config::set_http_headers
|
||||
SIGNALRCLIENT_API void __cdecl set_credentials(const web::credentials &credentials);
|
||||
|
||||
SIGNALRCLIENT_API web::http::client::http_client_config __cdecl get_http_client_config() const;
|
||||
SIGNALRCLIENT_API void __cdecl set_http_client_config(const web::http::client::http_client_config& http_client_config);
|
||||
|
||||
SIGNALRCLIENT_API web::websockets::client::websocket_client_config __cdecl get_websocket_client_config() const noexcept;
|
||||
SIGNALRCLIENT_API void __cdecl set_websocket_client_config(const web::websockets::client::websocket_client_config& websocket_client_config);
|
||||
|
||||
SIGNALRCLIENT_API web::http::http_headers __cdecl get_http_headers() const noexcept;
|
||||
SIGNALRCLIENT_API void __cdecl set_http_headers(const web::http::http_headers& http_headers);
|
||||
|
||||
private:
|
||||
web::http::client::http_client_config m_http_client_config;
|
||||
web::websockets::client::websocket_client_config m_websocket_client_config;
|
||||
web::http::http_headers m_http_headers;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class signalr_exception : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit signalr_exception(const std::string &what)
|
||||
: runtime_error(what)
|
||||
{}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
enum class trace_level : int
|
||||
{
|
||||
none = 0,
|
||||
messages = 1,
|
||||
events = 2,
|
||||
state_changes = 4,
|
||||
errors = 8,
|
||||
info = 16,
|
||||
all = messages | events | state_changes | errors | info
|
||||
};
|
||||
|
||||
inline trace_level operator|(trace_level lhs, trace_level rhs) noexcept
|
||||
{
|
||||
return static_cast<trace_level>(static_cast<int>(lhs) | static_cast<int>(rhs));
|
||||
}
|
||||
|
||||
inline trace_level operator&(trace_level lhs, trace_level rhs) noexcept
|
||||
{
|
||||
return static_cast<trace_level>(static_cast<int>(lhs) & static_cast<int>(rhs));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
enum class transfer_format
|
||||
{
|
||||
text,
|
||||
binary
|
||||
};
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
enum class transport_type
|
||||
{
|
||||
long_polling,
|
||||
websockets
|
||||
};
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class web_exception : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
web_exception(const std::string &what, unsigned short status_code)
|
||||
: runtime_error(what), m_status_code(status_code)
|
||||
{}
|
||||
|
||||
unsigned short status_code() const noexcept
|
||||
{
|
||||
return m_status_code;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned short m_status_code;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "transfer_format.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class websocket_client
|
||||
{
|
||||
public:
|
||||
virtual ~websocket_client() {};
|
||||
|
||||
virtual void start(std::string url, transfer_format format, std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void stop(std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void send(std::string payload, std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void receive(std::function<void(std::string, std::exception_ptr)> callback) = 0;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "hub_connection.h"
|
||||
#include "log_writer.h"
|
||||
#include <future>
|
||||
|
||||
class logger : public signalr::log_writer
|
||||
{
|
||||
// Inherited via log_writer
|
||||
virtual void __cdecl write(const std::string & entry) override
|
||||
{
|
||||
//std::cout << utility::conversions::to_utf8string(entry) << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
void send_message(signalr::hub_connection& connection, const std::string& message)
|
||||
{
|
||||
web::json::value args{};
|
||||
args[0] = web::json::value(utility::conversions::to_string_t(message));
|
||||
|
||||
// if you get an internal compiler error uncomment the lambda below or install VS Update 4
|
||||
connection.invoke("Send", args, [](const web::json::value& value, std::exception_ptr exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
|
||||
ucout << U("Received: ") << value.serialize() << std::endl;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
ucout << U("Error while sending data: ") << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void chat()
|
||||
{
|
||||
signalr::hub_connection connection("http://localhost:5000/default", signalr::trace_level::all, std::make_shared<logger>());
|
||||
connection.on("Send", [](const web::json::value & m)
|
||||
{
|
||||
ucout << std::endl << m.at(0).as_string() << /*U(" wrote:") << m.at(1).as_string() <<*/ std::endl << U("Enter your message: ");
|
||||
});
|
||||
|
||||
std::promise<void> task;
|
||||
connection.start([&connection, &task](std::exception_ptr exception)
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
catch (const std::exception & ex)
|
||||
{
|
||||
ucout << U("exception when starting connection: ") << ex.what() << std::endl;
|
||||
}
|
||||
task.set_value();
|
||||
return;
|
||||
}
|
||||
|
||||
ucout << U("Enter your message:");
|
||||
for (;;)
|
||||
{
|
||||
std::string message;
|
||||
std::getline(std::cin, message);
|
||||
|
||||
if (message == ":q")
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
send_message(connection, message);
|
||||
}
|
||||
|
||||
connection.stop([&task](std::exception_ptr exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
|
||||
ucout << U("connection stopped successfully") << std::endl;
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
ucout << U("exception when stopping connection: ") << e.what() << std::endl;
|
||||
}
|
||||
|
||||
task.set_value();
|
||||
});
|
||||
});
|
||||
|
||||
task.get_future().get();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
chat();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\Build\SignalRClient.Build.Settings" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>HubConnectionSample</RootNamespace>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">true</DownloadNuGetExe>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include\signalrclient;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>$(MSBuildThisFileDirectory)..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\lib\native\v140\windesktop\msvcstl\dyn\rt-dyn\x86\Debug\cpprest140d_2_9.lib;$(MSBuildThisFileDirectory)..\..\bin\Desktop\Win32\Debug\dll\signalrclient.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include\signalrclient;$(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="HubConnectionSample.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\signalrclient\Build\VS\signalrclient.vcxproj">
|
||||
<Project>{87ed3ad4-d820-48cd-8382-a12564213a12}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
<Exec Command="copy /y "$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\dll\$(SignalrClientTargetName).dll" "$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\$(SignalrClientTargetName).dll"" />
|
||||
<Exec Command="copy /y "$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\dll\$(SignalrClientTargetName).pdb" "$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\$(SignalrClientTargetName).pdb"" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HubConnectionSample.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
|
@ -0,0 +1,112 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.0.0
|
||||
MinimumVisualStudioVersion = 16.0.0.0
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "signalrclient", "src\signalrclient\Build\VS\signalrclient.vcxproj", "{87ED3AD4-D820-48CD-8382-A12564213A12}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "signalrclienttests", "test\signalrclienttests\Build\VS\signalrclienttests.vcxproj", "{10376148-BCF4-4B55-98A5-3C98C87FD898}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "signalrclientdll", "src\signalrclientdll\Build\VS\signalrclientdll.vcxproj", "{18377AE8-E372-40CE-94FD-7F65008D39A3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{AABF08B1-12A4-4D06-A188-F01FBF8A9658}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HubConnectionSample", "samples\HubConnectionSample\HubConnectionSample.vcxproj", "{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3} = {18377AE8-E372-40CE-94FD-7F65008D39A3}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "test\gtest\gtest.vcxproj", "{CAC1267B-8778-4257-AAC6-CAF481723B01}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|Mixed Platforms = Debug|Mixed Platforms
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|Mixed Platforms = Release|Mixed Platforms
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Debug|x64.Build.0 = Debug|x64
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Release|Win32.Build.0 = Release|Win32
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Release|x64.ActiveCfg = Release|x64
|
||||
{87ED3AD4-D820-48CD-8382-A12564213A12}.Release|x64.Build.0 = Release|x64
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Debug|x64.Build.0 = Debug|x64
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Release|Win32.Build.0 = Release|Win32
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Release|x64.ActiveCfg = Release|x64
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898}.Release|x64.Build.0 = Release|x64
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Debug|x64.Build.0 = Debug|x64
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Release|Win32.Build.0 = Release|Win32
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Release|x64.ActiveCfg = Release|x64
|
||||
{18377AE8-E372-40CE-94FD-7F65008D39A3}.Release|x64.Build.0 = Release|x64
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Release|Win32.Build.0 = Release|Win32
|
||||
{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}.Release|x64.ActiveCfg = Release|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x64.Build.0 = Debug|x64
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|Win32.Build.0 = Release|Win32
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x64.ActiveCfg = Release|x64
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{10376148-BCF4-4B55-98A5-3C98C87FD898} = {AABF08B1-12A4-4D06-A188-F01FBF8A9658}
|
||||
{CAC1267B-8778-4257-AAC6-CAF481723B01} = {AABF08B1-12A4-4D06-A188-F01FBF8A9658}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0286287F-3519-42E6-9165-FB342028F9FF}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,112 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\Build\SignalRClient.Build.Settings" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{87ED3AD4-D820-48CD-8382-A12564213A12}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>signalr</RootNamespace>
|
||||
<ProjectName>signalrclient</ProjectName>
|
||||
<TargetName>$(SignalrClientTargetName)</TargetName>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\..\</SolutionDir>
|
||||
<OutDir Condition="'$(OutDir)' == ''">$(SolutionDir)$(Configuration)\</OutDir>
|
||||
<OutDir>$(OutDir)lib\</OutDir>
|
||||
<IntDir>$(Configuration)\lib\</IntDir>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\Build\Config.Definitions.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_WINDOWS;_USRDLL;NO_SIGNALRCLIENT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection_state.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\http_client.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_connection.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\log_writer.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\signalr_client_config.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\signalr_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\trace_level.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transfer_format.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transport_type.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\websocket_client.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\web_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\_exports.h" />
|
||||
<ClInclude Include="..\..\case_insensitive_comparison_utils.h" />
|
||||
<ClInclude Include="..\..\connection_impl.h" />
|
||||
<ClInclude Include="..\..\constants.h" />
|
||||
<ClInclude Include="..\..\default_http_client.h" />
|
||||
<ClInclude Include="..\..\default_websocket_client.h" />
|
||||
<ClInclude Include="..\..\event.h" />
|
||||
<ClInclude Include="..\..\http_sender.h" />
|
||||
<ClInclude Include="..\..\hub_connection_impl.h" />
|
||||
<ClInclude Include="..\..\callback_manager.h" />
|
||||
<ClInclude Include="..\..\logger.h" />
|
||||
<ClInclude Include="..\..\negotiation_response.h" />
|
||||
<ClInclude Include="..\..\negotiate.h" />
|
||||
<ClInclude Include="..\..\stdafx.h" />
|
||||
<ClInclude Include="..\..\trace_log_writer.h" />
|
||||
<ClInclude Include="..\..\transport.h" />
|
||||
<ClInclude Include="..\..\transport_factory.h" />
|
||||
<ClInclude Include="..\..\url_builder.h" />
|
||||
<ClInclude Include="..\..\websocket_transport.h" />
|
||||
<ClInclude Include="..\..\web_request.h" />
|
||||
<ClInclude Include="..\..\web_request_factory.h" />
|
||||
<ClInclude Include="..\..\web_response.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\connection.cpp" />
|
||||
<ClCompile Include="..\..\connection_impl.cpp" />
|
||||
<ClCompile Include="..\..\default_http_client.cpp" />
|
||||
<ClCompile Include="..\..\http_sender.cpp" />
|
||||
<ClCompile Include="..\..\hub_connection.cpp" />
|
||||
<ClCompile Include="..\..\hub_connection_impl.cpp" />
|
||||
<ClCompile Include="..\..\callback_manager.cpp" />
|
||||
<ClCompile Include="..\..\logger.cpp" />
|
||||
<ClCompile Include="..\..\negotiate.cpp" />
|
||||
<ClCompile Include="..\..\signalr_client_config.cpp" />
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\trace_log_writer.cpp" />
|
||||
<ClCompile Include="..\..\transport.cpp" />
|
||||
<ClCompile Include="..\..\transport_factory.cpp" />
|
||||
<ClCompile Include="..\..\url_builder.cpp" />
|
||||
<ClCompile Include="..\..\default_websocket_client.cpp" />
|
||||
<ClCompile Include="..\..\websocket_transport.cpp" />
|
||||
<ClCompile Include="..\..\web_request.cpp" />
|
||||
<ClCompile Include="..\..\web_request_factory.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="cpprestsdk" Version="2.9.1.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,183 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\_exports.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transport_type.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\trace_level.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\url_builder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\constants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\http_sender.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\web_exception.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\negotiation_response.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\websocket_transport.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\connection_impl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection_state.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\default_websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\log_writer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\logger.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\trace_log_writer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\transport_factory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\web_request_factory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\transport.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\web_response.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\web_request.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\hub_connection_impl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\callback_manager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_exception.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_connection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\case_insensitive_comparison_utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\event.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\signalr_exception.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\signalr_client_config.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\default_http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\negotiate.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transfer_format.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\url_builder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\web_request.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\connection_impl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\http_sender.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\connection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\transport_factory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\web_request_factory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\default_websocket_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\websocket_transport.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\logger.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\transport.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\hub_connection_impl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\callback_manager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\hub_connection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\trace_log_writer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\signalr_client_config.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\default_http_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\negotiate.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
|
||||
set (SOURCES
|
||||
callback_manager.cpp
|
||||
connection.cpp
|
||||
connection_impl.cpp
|
||||
default_websocket_client.cpp
|
||||
http_sender.cpp
|
||||
hub_connection.cpp
|
||||
hub_connection_impl.cpp
|
||||
logger.cpp
|
||||
request_sender.cpp
|
||||
signalr_client_config.cpp
|
||||
stdafx.cpp
|
||||
trace_log_writer.cpp
|
||||
transport.cpp
|
||||
transport_factory.cpp
|
||||
url_builder.cpp
|
||||
web_request.cpp
|
||||
web_request_factory.cpp
|
||||
websocket_transport.cpp
|
||||
)
|
||||
|
||||
add_library (signalrclient SHARED ${SOURCES})
|
||||
|
||||
target_link_libraries(signalrclient ${CPPREST_SO})
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "callback_manager.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
// dtor_clear_arguments will be passed when closing any pending callbacks when the `callback_manager` is
|
||||
// destroyed (i.e. in the dtor)
|
||||
callback_manager::callback_manager(const web::json::value& dtor_clear_arguments)
|
||||
: m_dtor_clear_arguments(dtor_clear_arguments)
|
||||
{ }
|
||||
|
||||
callback_manager::~callback_manager()
|
||||
{
|
||||
clear(m_dtor_clear_arguments);
|
||||
}
|
||||
|
||||
// note: callback must not throw except for the `on_progress` callback which will never be invoked from the dtor
|
||||
std::string callback_manager::register_callback(const std::function<void(const web::json::value&)>& callback)
|
||||
{
|
||||
auto callback_id = get_callback_id();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_map_lock);
|
||||
|
||||
m_callbacks.insert(std::make_pair(callback_id, callback));
|
||||
}
|
||||
|
||||
return callback_id;
|
||||
}
|
||||
|
||||
|
||||
// invokes a callback and stops tracking it if remove callback set to true
|
||||
bool callback_manager::invoke_callback(const std::string& callback_id, const web::json::value& arguments, bool remove_callback)
|
||||
{
|
||||
std::function<void(const web::json::value& arguments)> callback;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_map_lock);
|
||||
|
||||
auto iter = m_callbacks.find(callback_id);
|
||||
if (iter == m_callbacks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
callback = iter->second;
|
||||
|
||||
if (remove_callback)
|
||||
{
|
||||
m_callbacks.erase(callback_id);
|
||||
}
|
||||
}
|
||||
|
||||
callback(arguments);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool callback_manager::remove_callback(const std::string& callback_id)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_map_lock);
|
||||
|
||||
return m_callbacks.erase(callback_id) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void callback_manager::clear(const web::json::value& arguments)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_map_lock);
|
||||
|
||||
for (auto& kvp : m_callbacks)
|
||||
{
|
||||
kvp.second(arguments);
|
||||
}
|
||||
|
||||
m_callbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
std::string callback_manager::get_callback_id()
|
||||
{
|
||||
const auto callback_id = m_id++;
|
||||
std::stringstream ss;
|
||||
ss << callback_id;
|
||||
return ss.str();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include "cpprest/json.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class callback_manager
|
||||
{
|
||||
public:
|
||||
explicit callback_manager(const web::json::value& dtor_error);
|
||||
~callback_manager();
|
||||
|
||||
callback_manager(const callback_manager&) = delete;
|
||||
callback_manager& operator=(const callback_manager&) = delete;
|
||||
|
||||
std::string register_callback(const std::function<void(const web::json::value&)>& callback);
|
||||
bool invoke_callback(const std::string& callback_id, const web::json::value& arguments, bool remove_callback);
|
||||
bool remove_callback(const std::string& callback_id);
|
||||
void clear(const web::json::value& arguments);
|
||||
|
||||
private:
|
||||
std::atomic<int> m_id { 0 };
|
||||
std::unordered_map<std::string, std::function<void(const web::json::value&)>> m_callbacks;
|
||||
std::mutex m_map_lock;
|
||||
const web::json::value m_dtor_clear_arguments;
|
||||
|
||||
std::string get_callback_id();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <cctype>
|
||||
#include "cpprest/details/basic_types.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
// Note: These functions are not pretending to be all-purpose helpers for case insensitive string comparison. Rather
|
||||
// we use them to compare hub and hub method names which are expected to be almost exclusively ASCII and this is the
|
||||
// simplest thing that would work without having to take dependencies on third party libraries.
|
||||
struct case_insensitive_equals : std::binary_function<std::string, std::string, bool>
|
||||
{
|
||||
bool operator()(const std::string& s1, const std::string& s2) const
|
||||
{
|
||||
if (s1.length() != s2.length())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto s1_iterator = s1.begin(), s2_iterator = s2.begin(); s1_iterator != s1.end(); ++s1_iterator, ++s2_iterator)
|
||||
{
|
||||
if (std::toupper(*s1_iterator) != std::toupper(*s2_iterator))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct case_insensitive_hash : std::unary_function<std::string, std::size_t>
|
||||
{
|
||||
std::size_t operator()(const std::string& s) const noexcept
|
||||
{
|
||||
size_t hash = 0;
|
||||
std::hash<size_t> hasher;
|
||||
for (const utility::char_t& c : s)
|
||||
{
|
||||
hash ^= hasher(std::toupper(c)) + (hash << 5) + (hash >> 2);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "signalrclient/connection.h"
|
||||
#include "signalrclient/transport_type.h"
|
||||
#include "connection_impl.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
connection::connection(const std::string& url, trace_level trace_level, std::shared_ptr<log_writer> log_writer)
|
||||
: m_pImpl(connection_impl::create(url, trace_level, std::move(log_writer)))
|
||||
{}
|
||||
|
||||
// Do NOT remove this destructor. Letting the compiler generate and inline the default dtor may lead to
|
||||
// undefinded behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/
|
||||
connection::~connection() = default;
|
||||
|
||||
void connection::start(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
m_pImpl->start(callback);
|
||||
}
|
||||
|
||||
void connection::send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
m_pImpl->send(data, callback);
|
||||
}
|
||||
|
||||
void connection::set_message_received(const message_received_handler& message_received_callback)
|
||||
{
|
||||
m_pImpl->set_message_received(message_received_callback);
|
||||
}
|
||||
|
||||
void connection::set_disconnected(const std::function<void()>& disconnected_callback)
|
||||
{
|
||||
m_pImpl->set_disconnected(disconnected_callback);
|
||||
}
|
||||
|
||||
void connection::set_client_config(const signalr_client_config& config)
|
||||
{
|
||||
m_pImpl->set_client_config(config);
|
||||
}
|
||||
|
||||
void connection::stop(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
m_pImpl->stop(callback);
|
||||
}
|
||||
|
||||
connection_state connection::get_connection_state() const noexcept
|
||||
{
|
||||
return m_pImpl->get_connection_state();
|
||||
}
|
||||
|
||||
std::string connection::get_connection_id() const
|
||||
{
|
||||
return m_pImpl->get_connection_id();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,627 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include "constants.h"
|
||||
#include "connection_impl.h"
|
||||
#include "negotiate.h"
|
||||
#include "url_builder.h"
|
||||
#include "trace_log_writer.h"
|
||||
#include "make_unique.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
#include "default_http_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
// unnamed namespace makes it invisble outside this translation unit
|
||||
namespace
|
||||
{
|
||||
// this is a workaround for a compiler bug where mutable lambdas won't sometimes compile
|
||||
static void log(const logger& logger, trace_level level, const std::string& entry);
|
||||
}
|
||||
|
||||
std::shared_ptr<connection_impl> connection_impl::create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer)
|
||||
{
|
||||
return connection_impl::create(url, trace_level, log_writer, nullptr, std::make_unique<transport_factory>());
|
||||
}
|
||||
|
||||
std::shared_ptr<connection_impl> connection_impl::create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory)
|
||||
{
|
||||
return std::shared_ptr<connection_impl>(new connection_impl(url, trace_level,
|
||||
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move(http_client), std::move(transport_factory)));
|
||||
}
|
||||
|
||||
connection_impl::connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory)
|
||||
: m_base_url(url), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level), m_transport(nullptr),
|
||||
m_transport_factory(std::move(transport_factory)), m_message_received([](const std::string&) noexcept {}), m_disconnected([]() noexcept {})
|
||||
{
|
||||
if (http_client != nullptr)
|
||||
{
|
||||
m_http_client = std::move(http_client);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_http_client = std::unique_ptr<class http_client>(new default_http_client());
|
||||
}
|
||||
}
|
||||
|
||||
connection_impl::~connection_impl()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Signaling the event is safe here. We are in the dtor so noone is using this instance. There might be some
|
||||
// outstanding threads that hold on to the connection via a weak pointer but they won't be able to acquire
|
||||
// the instance since it is being destroyed. Note that the event may actually be in non-signaled state here.
|
||||
m_start_completed_event.set();
|
||||
shutdown().get();
|
||||
}
|
||||
catch (const pplx::task_canceled&)
|
||||
{
|
||||
// because we are in the dtor and the `connection_imp` is ref counted we should not get the `task_canceled`
|
||||
// exception because it would indicate that some other thread/task still holds reference to this instance
|
||||
// so how come we are in the dtor?
|
||||
_ASSERTE(false);
|
||||
return;
|
||||
}
|
||||
catch (...) // must not throw from destructors
|
||||
{ }
|
||||
|
||||
m_transport = nullptr;
|
||||
change_state(connection_state::disconnected);
|
||||
}
|
||||
|
||||
void connection_impl::start(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_stop_lock);
|
||||
if (!change_state(connection_state::disconnected, connection_state::connecting))
|
||||
{
|
||||
callback(std::make_exception_ptr(signalr_exception("cannot start a connection that is not in the disconnected state")));
|
||||
return;
|
||||
}
|
||||
|
||||
// there should not be any active transport at this point
|
||||
_ASSERTE(!m_transport);
|
||||
|
||||
m_disconnect_cts = pplx::cancellation_token_source();
|
||||
m_start_completed_event.reset();
|
||||
m_connection_id = "";
|
||||
}
|
||||
|
||||
start_negotiate(m_base_url, 0)
|
||||
.then([callback](pplx::task<void> prev_task)
|
||||
{
|
||||
try
|
||||
{
|
||||
prev_task.get();
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pplx::task<void> connection_impl::start_negotiate(const std::string& url, int redirect_count)
|
||||
{
|
||||
if (redirect_count >= MAX_NEGOTIATE_REDIRECTS)
|
||||
{
|
||||
return pplx::task_from_exception<void>(signalr_exception("Negotiate redirection limit exceeded."));
|
||||
}
|
||||
|
||||
pplx::task_completion_event<void> start_tce;
|
||||
|
||||
std::weak_ptr<connection_impl> weak_connection = shared_from_this();
|
||||
|
||||
pplx::task_from_result()
|
||||
.then([weak_connection, url]()
|
||||
{
|
||||
auto connection = weak_connection.lock();
|
||||
if (!connection)
|
||||
{
|
||||
return pplx::task_from_exception<negotiation_response>("connection no longer exists");
|
||||
}
|
||||
return negotiate::negotiate(*connection->m_http_client, url, connection->m_signalr_client_config);
|
||||
}, m_disconnect_cts.get_token())
|
||||
.then([weak_connection, start_tce, redirect_count, url](negotiation_response negotiation_response)
|
||||
{
|
||||
auto connection = weak_connection.lock();
|
||||
if (!connection)
|
||||
{
|
||||
return pplx::task_from_exception<void>("connection no longer exists");
|
||||
}
|
||||
|
||||
if (!negotiation_response.error.empty())
|
||||
{
|
||||
return pplx::task_from_exception<void>(signalr_exception(negotiation_response.error));
|
||||
}
|
||||
|
||||
if (!negotiation_response.url.empty())
|
||||
{
|
||||
if (!negotiation_response.accessToken.empty())
|
||||
{
|
||||
auto headers = connection->m_signalr_client_config.get_http_headers();
|
||||
headers[_XPLATSTR("Authorization")] = utility::conversions::to_string_t("Bearer " + negotiation_response.accessToken);
|
||||
connection->m_signalr_client_config.set_http_headers(headers);
|
||||
}
|
||||
return connection->start_negotiate(negotiation_response.url, redirect_count + 1);
|
||||
}
|
||||
|
||||
connection->m_connection_id = std::move(negotiation_response.connectionId);
|
||||
|
||||
// TODO: fallback logic
|
||||
|
||||
bool foundWebsockets = false;
|
||||
for (auto availableTransport : negotiation_response.availableTransports)
|
||||
{
|
||||
if (availableTransport.transport == "WebSockets")
|
||||
{
|
||||
foundWebsockets = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundWebsockets)
|
||||
{
|
||||
return pplx::task_from_exception<void>(signalr_exception("The server does not support WebSockets which is currently the only transport supported by this client."));
|
||||
}
|
||||
|
||||
// TODO: use transfer format
|
||||
|
||||
return connection->start_transport(url)
|
||||
.then([weak_connection, start_tce](std::shared_ptr<transport> transport)
|
||||
{
|
||||
auto connection = weak_connection.lock();
|
||||
if (!connection)
|
||||
{
|
||||
return pplx::task_from_exception<void>("connection no longer exists");
|
||||
}
|
||||
connection->m_transport = transport;
|
||||
|
||||
if (!connection->change_state(connection_state::connecting, connection_state::connected))
|
||||
{
|
||||
connection->m_logger.log(trace_level::errors,
|
||||
std::string("internal error - transition from an unexpected state. expected state: connecting, actual state: ")
|
||||
.append(translate_connection_state(connection->get_connection_state())));
|
||||
|
||||
_ASSERTE(false);
|
||||
}
|
||||
|
||||
return pplx::task_from_result();
|
||||
});
|
||||
}, m_disconnect_cts.get_token())
|
||||
.then([start_tce, weak_connection](pplx::task<void> previous_task)
|
||||
{
|
||||
auto connection = weak_connection.lock();
|
||||
if (!connection)
|
||||
{
|
||||
return pplx::task_from_exception<void>(_XPLATSTR("connection no longer exists"));
|
||||
}
|
||||
try
|
||||
{
|
||||
previous_task.get();
|
||||
connection->m_start_completed_event.set();
|
||||
start_tce.set();
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
auto task_canceled_exception = dynamic_cast<const pplx::task_canceled*>(&e);
|
||||
if (task_canceled_exception)
|
||||
{
|
||||
connection->m_logger.log(trace_level::info,
|
||||
"starting the connection has been canceled.");
|
||||
}
|
||||
else
|
||||
{
|
||||
connection->m_logger.log(trace_level::errors,
|
||||
std::string("connection could not be started due to: ")
|
||||
.append(e.what()));
|
||||
}
|
||||
|
||||
connection->m_transport = nullptr;
|
||||
connection->change_state(connection_state::disconnected);
|
||||
connection->m_start_completed_event.set();
|
||||
start_tce.set_exception(std::current_exception());
|
||||
}
|
||||
|
||||
return pplx::task_from_result();
|
||||
});
|
||||
|
||||
return pplx::create_task(start_tce);
|
||||
}
|
||||
|
||||
pplx::task<std::shared_ptr<transport>> connection_impl::start_transport(const std::string& url)
|
||||
{
|
||||
auto connection = shared_from_this();
|
||||
|
||||
pplx::task_completion_event<void> connect_request_tce;
|
||||
|
||||
auto weak_connection = std::weak_ptr<connection_impl>(connection);
|
||||
const auto& disconnect_cts = m_disconnect_cts;
|
||||
const auto& logger = m_logger;
|
||||
|
||||
auto transport = connection->m_transport_factory->create_transport(
|
||||
transport_type::websockets, connection->m_logger, connection->m_signalr_client_config);
|
||||
|
||||
transport->on_receive([disconnect_cts, connect_request_tce, logger, weak_connection](std::string message, std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Rethrowing the exception so we can log it
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
// When a connection is stopped we don't wait for its transport to stop. As a result if the same connection
|
||||
// is immediately re-started the old transport can still invoke this callback. To prevent this we capture
|
||||
// the disconnect_cts by value which allows distinguishing if the error is for the running connection
|
||||
// or for the one that was already stopped. If this is the latter we just ignore it.
|
||||
if (disconnect_cts.get_token().is_canceled())
|
||||
{
|
||||
logger.log(trace_level::info,
|
||||
std::string{ "ignoring stray error received after connection was restarted. error: " }
|
||||
.append(e.what()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// no op after connection started successfully
|
||||
connect_request_tce.set_exception(exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (disconnect_cts.get_token().is_canceled())
|
||||
{
|
||||
logger.log(trace_level::info,
|
||||
std::string{ "ignoring stray message received after connection was restarted. message: " }
|
||||
.append(message));
|
||||
return;
|
||||
}
|
||||
|
||||
auto connection = weak_connection.lock();
|
||||
if (connection)
|
||||
{
|
||||
connection->process_response(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pplx::create_task([connect_request_tce, disconnect_cts, weak_connection]()
|
||||
{
|
||||
// TODO? std::this_thread::sleep_for(std::chrono::milliseconds(negotiation_response.transport_connect_timeout));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
|
||||
|
||||
// if the disconnect_cts is canceled it means that the connection has been stopped or went out of scope in
|
||||
// which case we should not throw due to timeout. Instead we need to set the tce prevent the task that is
|
||||
// using this tce from hanging indifinitely. (This will eventually result in throwing the pplx::task_canceled
|
||||
// exception to the user since this is what we do in the start() function if disconnect_cts is tripped).
|
||||
if (disconnect_cts.get_token().is_canceled())
|
||||
{
|
||||
connect_request_tce.set();
|
||||
}
|
||||
else
|
||||
{
|
||||
connect_request_tce.set_exception(signalr_exception("transport timed out when trying to connect"));
|
||||
}
|
||||
});
|
||||
|
||||
return connection->send_connect_request(transport, url, connect_request_tce)
|
||||
.then([transport](){ return pplx::task_from_result(transport); });
|
||||
}
|
||||
|
||||
pplx::task<void> connection_impl::send_connect_request(const std::shared_ptr<transport>& transport, const std::string& url, const pplx::task_completion_event<void>& connect_request_tce)
|
||||
{
|
||||
auto logger = m_logger;
|
||||
auto query_string = "id=" + m_connection_id;
|
||||
auto connect_url = url_builder::build_connect(url, transport->get_transport_type(), query_string);
|
||||
|
||||
transport->start(connect_url, transfer_format::text, [transport, connect_request_tce, logger](std::exception_ptr exception)
|
||||
mutable {
|
||||
try
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
connect_request_tce.set();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("transport could not connect due to: ")
|
||||
.append(e.what()));
|
||||
|
||||
connect_request_tce.set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
return pplx::create_task(connect_request_tce);
|
||||
}
|
||||
|
||||
void connection_impl::process_response(const std::string& response)
|
||||
{
|
||||
m_logger.log(trace_level::messages,
|
||||
std::string("processing message: ").append(response));
|
||||
|
||||
invoke_message_received(response);
|
||||
}
|
||||
|
||||
void connection_impl::invoke_message_received(const std::string& message)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_message_received(message);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
m_logger.log(
|
||||
trace_level::errors,
|
||||
std::string("message_received callback threw an exception: ")
|
||||
.append(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
m_logger.log(trace_level::errors, "message_received callback threw an unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
void connection_impl::send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
// To prevent an (unlikely) condition where the transport is nulled out after we checked the connection_state
|
||||
// and before sending data we store the pointer in the local variable. In this case `send()` will throw but
|
||||
// we won't crash.
|
||||
auto transport = m_transport;
|
||||
|
||||
const auto connection_state = get_connection_state();
|
||||
if (connection_state != signalr::connection_state::connected || !transport)
|
||||
{
|
||||
callback(std::make_exception_ptr(signalr_exception(
|
||||
std::string("cannot send data when the connection is not in the connected state. current connection state: ")
|
||||
.append(translate_connection_state(connection_state)))));
|
||||
return;
|
||||
}
|
||||
|
||||
auto logger = m_logger;
|
||||
|
||||
logger.log(trace_level::info, std::string("sending data: ").append(data));
|
||||
|
||||
transport->send(data, [logger, callback](std::exception_ptr exception)
|
||||
mutable {
|
||||
try
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("error sending data: ")
|
||||
.append(e.what()));
|
||||
|
||||
callback(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void connection_impl::stop(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
m_logger.log(trace_level::info, "stopping connection");
|
||||
|
||||
auto connection = shared_from_this();
|
||||
shutdown()
|
||||
.then([connection, callback](pplx::task<void> prev_task)
|
||||
{
|
||||
try
|
||||
{
|
||||
prev_task.get();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(std::current_exception());
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// the lock prevents a race where the user calls `stop` on a disconnected connection and calls `start`
|
||||
// on a different thread at the same time. In this case we must not null out the transport if we are
|
||||
// not in the `disconnecting` state to not affect the 'start' invocation.
|
||||
std::lock_guard<std::mutex> lock(connection->m_stop_lock);
|
||||
if (connection->change_state(connection_state::disconnecting, connection_state::disconnected))
|
||||
{
|
||||
// we do let the exception through (especially the task_canceled exception)
|
||||
connection->m_transport = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
connection->m_disconnected();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
connection->m_logger.log(
|
||||
trace_level::errors,
|
||||
std::string("disconnected callback threw an exception: ")
|
||||
.append(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
connection->m_logger.log(
|
||||
trace_level::errors,
|
||||
std::string("disconnected callback threw an unknown exception"));
|
||||
}
|
||||
|
||||
callback(nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
// This function is called from the dtor so you must not use `shared_from_this` here (it will throw).
|
||||
pplx::task<void> connection_impl::shutdown()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_stop_lock);
|
||||
m_logger.log(trace_level::info, "acquired lock in shutdown()");
|
||||
|
||||
const auto current_state = get_connection_state();
|
||||
if (current_state == connection_state::disconnected)
|
||||
{
|
||||
return pplx::task_from_result();
|
||||
}
|
||||
|
||||
if (current_state == connection_state::disconnecting)
|
||||
{
|
||||
// canceled task will be returned if `stop` was called while another `stop` was already in progress.
|
||||
// This is to prevent from resetting the `m_transport` in the upstream callers because doing so might
|
||||
// affect the other invocation which is using it.
|
||||
auto cts = pplx::cancellation_token_source();
|
||||
cts.cancel();
|
||||
return pplx::create_task([]() noexcept {}, cts.get_token());
|
||||
}
|
||||
|
||||
// we request a cancellation of the ongoing start (if any) and wait until it is canceled
|
||||
m_disconnect_cts.cancel();
|
||||
|
||||
while (m_start_completed_event.wait(60000) != 0)
|
||||
{
|
||||
m_logger.log(trace_level::errors,
|
||||
"internal error - stopping the connection is still waiting for the start operation to finish which should have already finished or timed out");
|
||||
}
|
||||
|
||||
// at this point we are either in the connected or disconnected state. If we are in the disconnected state
|
||||
// we must break because the transport has already been nulled out.
|
||||
if (m_connection_state == connection_state::disconnected)
|
||||
{
|
||||
return pplx::task_from_result();
|
||||
}
|
||||
|
||||
_ASSERTE(m_connection_state == connection_state::connected);
|
||||
|
||||
change_state(connection_state::disconnecting);
|
||||
}
|
||||
|
||||
pplx::task_completion_event<void> tce;
|
||||
m_transport->stop([tce](std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
tce.set_exception(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
tce.set();
|
||||
}
|
||||
});
|
||||
|
||||
return pplx::create_task(tce);
|
||||
}
|
||||
|
||||
connection_state connection_impl::get_connection_state() const noexcept
|
||||
{
|
||||
return m_connection_state.load();
|
||||
}
|
||||
|
||||
std::string connection_impl::get_connection_id() const noexcept
|
||||
{
|
||||
if (m_connection_state.load() == connection_state::connecting)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return m_connection_id;
|
||||
}
|
||||
|
||||
void connection_impl::set_message_received(const std::function<void(const std::string&)>& message_received)
|
||||
{
|
||||
ensure_disconnected("cannot set the callback when the connection is not in the disconnected state. ");
|
||||
m_message_received = message_received;
|
||||
}
|
||||
|
||||
void connection_impl::set_client_config(const signalr_client_config& config)
|
||||
{
|
||||
ensure_disconnected("cannot set client config when the connection is not in the disconnected state. ");
|
||||
m_signalr_client_config = config;
|
||||
}
|
||||
|
||||
void connection_impl::set_disconnected(const std::function<void()>& disconnected)
|
||||
{
|
||||
ensure_disconnected("cannot set the disconnected callback when the connection is not in the disconnected state. ");
|
||||
m_disconnected = disconnected;
|
||||
}
|
||||
|
||||
void connection_impl::ensure_disconnected(const std::string& error_message) const
|
||||
{
|
||||
const auto state = get_connection_state();
|
||||
if (state != connection_state::disconnected)
|
||||
{
|
||||
throw signalr_exception(
|
||||
error_message + "current connection state: " + translate_connection_state(state));
|
||||
}
|
||||
}
|
||||
|
||||
bool connection_impl::change_state(connection_state old_state, connection_state new_state)
|
||||
{
|
||||
if (m_connection_state.compare_exchange_strong(old_state, new_state, std::memory_order_seq_cst))
|
||||
{
|
||||
handle_connection_state_change(old_state, new_state);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
connection_state connection_impl::change_state(connection_state new_state)
|
||||
{
|
||||
auto old_state = m_connection_state.exchange(new_state);
|
||||
if (old_state != new_state)
|
||||
{
|
||||
handle_connection_state_change(old_state, new_state);
|
||||
}
|
||||
|
||||
return old_state;
|
||||
}
|
||||
|
||||
void connection_impl::handle_connection_state_change(connection_state old_state, connection_state new_state)
|
||||
{
|
||||
m_logger.log(
|
||||
trace_level::state_changes,
|
||||
translate_connection_state(old_state)
|
||||
.append(" -> ")
|
||||
.append(translate_connection_state(new_state)));
|
||||
|
||||
// Words of wisdom (if we decide to add a state_changed callback and invoke it from here):
|
||||
// "Be extra careful when you add this callback, because this is sometimes being called with the m_stop_lock.
|
||||
// This could lead to interesting problems.For example, you could run into a segfault if the connection is
|
||||
// stopped while / after transitioning into the connecting state."
|
||||
}
|
||||
|
||||
std::string connection_impl::translate_connection_state(connection_state state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case connection_state::connecting:
|
||||
return "connecting";
|
||||
case connection_state::connected:
|
||||
return "connected";
|
||||
case connection_state::disconnecting:
|
||||
return "disconnecting";
|
||||
case connection_state::disconnected:
|
||||
return "disconnected";
|
||||
default:
|
||||
_ASSERTE(false);
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include "signalrclient/http_client.h"
|
||||
#include "signalrclient/trace_level.h"
|
||||
#include "signalrclient/connection_state.h"
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
#include "web_request_factory.h"
|
||||
#include "transport_factory.h"
|
||||
#include "logger.h"
|
||||
#include "negotiation_response.h"
|
||||
#include "event.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
// Note:
|
||||
// Factory methods and private constructors prevent from using this class incorrectly. Because this class
|
||||
// derives from `std::enable_shared_from_this` the instance has to be owned by a `std::shared_ptr` whenever
|
||||
// a member method calls `std::shared_from_this()` otherwise the behavior is undefined. Therefore constructors
|
||||
// are private to disallow creating instances directly and factory methods return `std::shared_ptr<connection_impl>`.
|
||||
class connection_impl : public std::enable_shared_from_this<connection_impl>
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<connection_impl> create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer);
|
||||
|
||||
static std::shared_ptr<connection_impl> create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
connection_impl(const connection_impl&) = delete;
|
||||
|
||||
connection_impl& operator=(const connection_impl&) = delete;
|
||||
|
||||
~connection_impl();
|
||||
|
||||
void start(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
void send(const std::string &data, std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
void stop(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
|
||||
connection_state get_connection_state() const noexcept;
|
||||
std::string get_connection_id() const noexcept;
|
||||
|
||||
void set_message_received(const std::function<void(const std::string&)>& message_received);
|
||||
void set_disconnected(const std::function<void()>& disconnected);
|
||||
void set_client_config(const signalr_client_config& config);
|
||||
|
||||
private:
|
||||
std::string m_base_url;
|
||||
std::atomic<connection_state> m_connection_state;
|
||||
logger m_logger;
|
||||
std::shared_ptr<transport> m_transport;
|
||||
std::unique_ptr<transport_factory> m_transport_factory;
|
||||
|
||||
std::function<void(const std::string&)> m_message_received;
|
||||
std::function<void()> m_disconnected;
|
||||
signalr_client_config m_signalr_client_config;
|
||||
|
||||
pplx::cancellation_token_source m_disconnect_cts;
|
||||
std::mutex m_stop_lock;
|
||||
event m_start_completed_event;
|
||||
std::string m_connection_id;
|
||||
std::unique_ptr<http_client> m_http_client;
|
||||
|
||||
connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
pplx::task<std::shared_ptr<transport>> start_transport(const std::string& url);
|
||||
pplx::task<void> send_connect_request(const std::shared_ptr<transport>& transport,
|
||||
const std::string& url, const pplx::task_completion_event<void>& connect_request_tce);
|
||||
pplx::task<void> start_negotiate(const std::string& url, int redirect_count);
|
||||
|
||||
void process_response(const std::string& response);
|
||||
|
||||
pplx::task<void> shutdown();
|
||||
|
||||
bool change_state(connection_state old_state, connection_state new_state);
|
||||
connection_state change_state(connection_state new_state);
|
||||
void handle_connection_state_change(connection_state old_state, connection_state new_state);
|
||||
void invoke_message_received(const std::string& message);
|
||||
|
||||
static std::string translate_connection_state(connection_state state);
|
||||
void ensure_disconnected(const std::string& error_message) const;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define SIGNALR_VERSION "0.1.0-alpha0"
|
||||
#define USER_AGENT "SignalR.Client.Cpp/" SIGNALR_VERSION
|
||||
#define MAX_NEGOTIATE_REDIRECTS 100
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "default_http_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
void default_http_client::send(std::string url, http_request request, std::function<void(http_response, std::exception_ptr)> callback)
|
||||
{
|
||||
web::http::method method;
|
||||
if (request.method == http_method::GET)
|
||||
{
|
||||
method = U("GET");
|
||||
}
|
||||
else if (request.method == http_method::POST)
|
||||
{
|
||||
method = U("POST");
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(http_response(), std::make_exception_ptr(std::runtime_error("unknown http method")));
|
||||
return;
|
||||
}
|
||||
|
||||
web::http::http_request http_request;
|
||||
http_request.set_method(method);
|
||||
http_request.set_body(request.content);
|
||||
if (request.headers.size() > 0)
|
||||
{
|
||||
web::http::http_headers headers;
|
||||
for (auto& header : request.headers)
|
||||
{
|
||||
headers.add(utility::conversions::to_string_t(header.first), utility::conversions::to_string_t(header.second));
|
||||
}
|
||||
http_request.headers() = headers;
|
||||
}
|
||||
|
||||
auto milliseconds = std::chrono::milliseconds(request.timeout).count();
|
||||
pplx::cancellation_token_source cts;
|
||||
if (milliseconds != 0)
|
||||
{
|
||||
pplx::create_task([milliseconds, cts]()
|
||||
{
|
||||
pplx::wait((unsigned int)milliseconds);
|
||||
cts.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
web::http::client::http_client client(utility::conversions::to_string_t(url));
|
||||
client.request(http_request, cts.get_token())
|
||||
.then([callback](pplx::task<web::http::http_response> response_task)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto http_response = response_task.get();
|
||||
auto status_code = http_response.status_code();
|
||||
http_response.extract_utf8string()
|
||||
.then([callback, status_code](std::string response_body)
|
||||
{
|
||||
signalr::http_response response;
|
||||
response.content = response_body;
|
||||
response.status_code = status_code;
|
||||
callback(std::move(response), nullptr);
|
||||
});
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(http_response(), std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "signalrclient/http_client.h"
|
||||
#include "cpprest/http_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class default_http_client : public http_client
|
||||
{
|
||||
public:
|
||||
void send(std::string url, http_request request, std::function<void(http_response, std::exception_ptr)> callback) override;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "default_websocket_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace
|
||||
{
|
||||
static web::websockets::client::websocket_client_config create_client_config(const signalr_client_config& signalr_client_config) noexcept
|
||||
{
|
||||
auto websocket_client_config = signalr_client_config.get_websocket_client_config();
|
||||
websocket_client_config.headers() = signalr_client_config.get_http_headers();
|
||||
|
||||
return websocket_client_config;
|
||||
}
|
||||
}
|
||||
|
||||
default_websocket_client::default_websocket_client(const signalr_client_config& signalr_client_config) noexcept
|
||||
: m_underlying_client(create_client_config(signalr_client_config))
|
||||
{ }
|
||||
|
||||
void default_websocket_client::start(std::string url, transfer_format, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
m_underlying_client.connect(utility::conversions::to_string_t(url))
|
||||
.then([callback](pplx::task<void> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
task.get();
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void default_websocket_client::stop(std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
m_underlying_client.close()
|
||||
.then([callback](pplx::task<void> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void default_websocket_client::send(std::string payload, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
web::websockets::client::websocket_outgoing_message msg;
|
||||
msg.set_utf8_message(payload);
|
||||
m_underlying_client.send(msg)
|
||||
.then([callback](pplx::task<void> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
task.get();
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void default_websocket_client::receive(std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
m_underlying_client.receive()
|
||||
.then([callback](pplx::task<web::websockets::client::websocket_incoming_message> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto response = task.get();
|
||||
auto msg = response.extract_string().get();
|
||||
callback(msg, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback("", std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpprest/ws_client.h"
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
#include "signalrclient/websocket_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class default_websocket_client : public websocket_client
|
||||
{
|
||||
public:
|
||||
explicit default_websocket_client(const signalr_client_config& signalr_client_config = {}) noexcept;
|
||||
|
||||
void start(std::string url, transfer_format format, std::function<void(std::exception_ptr)> callback) override;
|
||||
void stop(std::function<void(std::exception_ptr)> callback) override;
|
||||
void send(std::string payload, std::function<void(std::exception_ptr)> callback) override;
|
||||
void receive(std::function<void(std::string, std::exception_ptr)> callback) override;
|
||||
private:
|
||||
web::websockets::client::websocket_client m_underlying_client;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class event
|
||||
{
|
||||
private:
|
||||
std::mutex m_lock;
|
||||
std::condition_variable m_condition;
|
||||
bool m_signaled;
|
||||
public:
|
||||
|
||||
static const unsigned int timeout_infinite = 0xFFFFFFFF;
|
||||
|
||||
event() noexcept
|
||||
: m_signaled(false)
|
||||
{
|
||||
}
|
||||
|
||||
void set()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_lock);
|
||||
m_signaled = true;
|
||||
m_condition.notify_all();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_lock);
|
||||
m_signaled = false;
|
||||
}
|
||||
|
||||
unsigned int wait(unsigned int timeout)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_lock);
|
||||
if (timeout == event::timeout_infinite)
|
||||
{
|
||||
m_condition.wait(lock, [this]() noexcept { return m_signaled; });
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::chrono::milliseconds period(timeout);
|
||||
const auto status = m_condition.wait_for(lock, period, [this]() noexcept { return m_signaled; });
|
||||
assert(status == m_signaled);
|
||||
// Return 0 if the wait completed as a result of signaling the event. Otherwise, return timeout_infinite
|
||||
return status ? 0 : event::timeout_infinite;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int wait()
|
||||
{
|
||||
return wait(event::timeout_infinite);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "signalrclient/web_exception.h"
|
||||
#include "web_request_factory.h"
|
||||
#include "constants.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace http_sender
|
||||
{
|
||||
pplx::task<std::string> get(web_request_factory& request_factory, const std::string& url,
|
||||
const signalr_client_config& signalr_client_config)
|
||||
{
|
||||
auto request = request_factory.create_web_request(url);
|
||||
request->set_method(utility::conversions::to_utf8string(web::http::methods::GET));
|
||||
|
||||
request->set_user_agent(USER_AGENT);
|
||||
request->set_client_config(signalr_client_config);
|
||||
|
||||
return request->get_response().then([](web_response response)
|
||||
{
|
||||
if (response.status_code != 200)
|
||||
{
|
||||
std::stringstream oss;
|
||||
oss << "web exception - " << response.status_code << " " << response.reason_phrase;
|
||||
throw web_exception(oss.str(), response.status_code);
|
||||
}
|
||||
|
||||
return response.body;
|
||||
});
|
||||
}
|
||||
|
||||
pplx::task<std::string> post(web_request_factory& request_factory, const std::string& url,
|
||||
const signalr_client_config& signalr_client_config)
|
||||
{
|
||||
auto request = request_factory.create_web_request(url);
|
||||
request->set_method(utility::conversions::to_utf8string(web::http::methods::POST));
|
||||
|
||||
request->set_user_agent(USER_AGENT);
|
||||
request->set_client_config(signalr_client_config);
|
||||
|
||||
return request->get_response().then([](web_response response)
|
||||
{
|
||||
if (response.status_code != 200)
|
||||
{
|
||||
std::stringstream oss;
|
||||
oss << "web exception - " << response.status_code << " " << response.reason_phrase;
|
||||
throw web_exception(oss.str(), response.status_code);
|
||||
}
|
||||
|
||||
return response.body;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pplx/pplxtasks.h"
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
#include "web_request_factory.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace http_sender
|
||||
{
|
||||
pplx::task<std::string> get(web_request_factory& request_factory, const std::string& url,
|
||||
const signalr_client_config& client_config = signalr_client_config{});
|
||||
|
||||
pplx::task<std::string> post(web_request_factory& request_factory, const std::string& url,
|
||||
const signalr_client_config& client_config = signalr_client_config{});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "signalrclient/hub_connection.h"
|
||||
#include "hub_connection_impl.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
hub_connection::hub_connection(const std::string& url,
|
||||
trace_level trace_level, std::shared_ptr<log_writer> log_writer)
|
||||
: m_pImpl(hub_connection_impl::create(url, trace_level, std::move(log_writer)))
|
||||
{}
|
||||
|
||||
// Do NOT remove this destructor. Letting the compiler generate and inline the default dtor may lead to
|
||||
// undefinded behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/
|
||||
hub_connection::~hub_connection() = default;
|
||||
|
||||
void hub_connection::start(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
callback(std::make_exception_ptr(signalr_exception("start() cannot be called on destructed hub_connection instance")));
|
||||
}
|
||||
|
||||
m_pImpl->start(callback);
|
||||
}
|
||||
|
||||
void hub_connection::stop(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
callback(std::make_exception_ptr(signalr_exception("stop() cannot be called on destructed hub_connection instance")));
|
||||
}
|
||||
|
||||
m_pImpl->stop(callback);
|
||||
}
|
||||
|
||||
void hub_connection::on(const std::string& event_name, const method_invoked_handler& handler)
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
throw signalr_exception("on() cannot be called on destructed hub_connection instance");
|
||||
}
|
||||
|
||||
return m_pImpl->on(event_name, handler);
|
||||
}
|
||||
|
||||
void hub_connection::invoke(const std::string& method_name, const web::json::value& arguments, std::function<void(const web::json::value&, std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
callback(web::json::value(), std::make_exception_ptr(signalr_exception("invoke() cannot be called on destructed hub_connection instance")));
|
||||
return;
|
||||
}
|
||||
|
||||
return m_pImpl->invoke(method_name, arguments, callback);
|
||||
}
|
||||
|
||||
void hub_connection::send(const std::string& method_name, const web::json::value& arguments, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
callback(std::make_exception_ptr(signalr_exception("send() cannot be called on destructed hub_connection instance")));
|
||||
return;
|
||||
}
|
||||
|
||||
m_pImpl->send(method_name, arguments, callback);
|
||||
}
|
||||
|
||||
connection_state hub_connection::get_connection_state() const
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
throw signalr_exception("get_connection_state() cannot be called on destructed hub_connection instance");
|
||||
}
|
||||
|
||||
return m_pImpl->get_connection_state();
|
||||
}
|
||||
|
||||
std::string hub_connection::get_connection_id() const
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
throw signalr_exception("get_connection_id() cannot be called on destructed hub_connection instance");
|
||||
}
|
||||
|
||||
return m_pImpl->get_connection_id();
|
||||
}
|
||||
|
||||
void hub_connection::set_disconnected(const std::function<void()>& disconnected_callback)
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
throw signalr_exception("set_disconnected() cannot be called on destructed hub_connection instance");
|
||||
}
|
||||
|
||||
m_pImpl->set_disconnected(disconnected_callback);
|
||||
}
|
||||
|
||||
void hub_connection::set_client_config(const signalr_client_config& config)
|
||||
{
|
||||
if (!m_pImpl)
|
||||
{
|
||||
throw signalr_exception("set_client_config() cannot be called on destructed hub_connection instance");
|
||||
}
|
||||
|
||||
m_pImpl->set_client_config(config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,405 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "hub_connection_impl.h"
|
||||
#include "signalrclient/hub_exception.h"
|
||||
#include "trace_log_writer.h"
|
||||
#include "make_unique.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
|
||||
using namespace web;
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
// unnamed namespace makes it invisble outside this translation unit
|
||||
namespace
|
||||
{
|
||||
static std::function<void(const json::value&)> create_hub_invocation_callback(const logger& logger,
|
||||
const std::function<void(const json::value&)>& set_result,
|
||||
const std::function<void(const std::exception_ptr e)>& set_exception);
|
||||
}
|
||||
|
||||
std::shared_ptr<hub_connection_impl> hub_connection_impl::create(const std::string& url, trace_level trace_level,
|
||||
const std::shared_ptr<log_writer>& log_writer)
|
||||
{
|
||||
return hub_connection_impl::create(url, trace_level, log_writer,
|
||||
nullptr, std::make_unique<transport_factory>());
|
||||
}
|
||||
|
||||
std::shared_ptr<hub_connection_impl> hub_connection_impl::create(const std::string& url, trace_level trace_level,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<http_client> http_client,
|
||||
std::unique_ptr<transport_factory> transport_factory)
|
||||
{
|
||||
auto connection = std::shared_ptr<hub_connection_impl>(new hub_connection_impl(url, trace_level,
|
||||
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move(http_client), std::move(transport_factory)));
|
||||
|
||||
connection->initialize();
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
hub_connection_impl::hub_connection_impl(const std::string& url, trace_level trace_level,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<http_client> http_client,
|
||||
std::unique_ptr<transport_factory> transport_factory)
|
||||
: m_connection(connection_impl::create(url, trace_level, log_writer,
|
||||
std::move(http_client), std::move(transport_factory))), m_logger(log_writer, trace_level),
|
||||
m_callback_manager(json::value::parse(_XPLATSTR("{ \"error\" : \"connection went out of scope before invocation result was received\"}"))),
|
||||
m_disconnected([]() noexcept {}), m_handshakeReceived(false)
|
||||
{ }
|
||||
|
||||
void hub_connection_impl::initialize()
|
||||
{
|
||||
// weak_ptr prevents a circular dependency leading to memory leak and other problems
|
||||
std::weak_ptr<hub_connection_impl> weak_hub_connection = shared_from_this();
|
||||
|
||||
m_connection->set_message_received([weak_hub_connection](const std::string& message)
|
||||
{
|
||||
auto connection = weak_hub_connection.lock();
|
||||
if (connection)
|
||||
{
|
||||
connection->process_message(message);
|
||||
}
|
||||
});
|
||||
|
||||
m_connection->set_disconnected([weak_hub_connection]()
|
||||
{
|
||||
auto connection = weak_hub_connection.lock();
|
||||
if (connection)
|
||||
{
|
||||
connection->m_handshakeTask.set_exception(signalr_exception("connection closed while handshake was in progress."));
|
||||
connection->m_disconnected();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void hub_connection_impl::on(const std::string& event_name, const std::function<void(const json::value &)>& handler)
|
||||
{
|
||||
if (event_name.length() == 0)
|
||||
{
|
||||
throw std::invalid_argument("event_name cannot be empty");
|
||||
}
|
||||
|
||||
auto weak_connection = std::weak_ptr<hub_connection_impl>(shared_from_this());
|
||||
auto connection = weak_connection.lock();
|
||||
if (connection && connection->get_connection_state() != connection_state::disconnected)
|
||||
{
|
||||
throw signalr_exception("can't register a handler if the connection is in a disconnected state");
|
||||
}
|
||||
|
||||
if (m_subscriptions.find(event_name) != m_subscriptions.end())
|
||||
{
|
||||
throw signalr_exception(
|
||||
"an action for this event has already been registered. event name: " + event_name);
|
||||
}
|
||||
|
||||
m_subscriptions.insert(std::pair<std::string, std::function<void(const json::value &)>> {event_name, handler});
|
||||
}
|
||||
|
||||
void hub_connection_impl::start(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
if (m_connection->get_connection_state() != connection_state::disconnected)
|
||||
{
|
||||
callback(std::make_exception_ptr(signalr_exception(
|
||||
"the connection can only be started if it is in the disconnected state")));
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection->set_client_config(m_signalr_client_config);
|
||||
m_handshakeTask = pplx::task_completion_event<void>();
|
||||
m_handshakeReceived = false;
|
||||
std::weak_ptr<hub_connection_impl> weak_connection = shared_from_this();
|
||||
m_connection->start([weak_connection, callback](std::exception_ptr start_exception)
|
||||
{
|
||||
auto connection = weak_connection.lock();
|
||||
if (!connection)
|
||||
{
|
||||
// The connection has been destructed
|
||||
callback(std::make_exception_ptr(signalr_exception("the hub connection has been deconstructed")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (start_exception)
|
||||
{
|
||||
connection->m_connection->stop([start_exception, callback, weak_connection](std::exception_ptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto connection = weak_connection.lock();
|
||||
if (!connection)
|
||||
{
|
||||
callback(std::make_exception_ptr(signalr_exception("the hub connection has been deconstructed")));
|
||||
return;
|
||||
}
|
||||
pplx::task<void>(connection->m_handshakeTask).get();
|
||||
}
|
||||
catch (...) {}
|
||||
|
||||
callback(start_exception);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Generate this later when we have the protocol abstraction
|
||||
auto handshake_request = "{\"protocol\":\"json\",\"version\":1}\x1e";
|
||||
connection->m_connection->send(handshake_request, [weak_connection, callback](std::exception_ptr exception)
|
||||
{
|
||||
auto connection = weak_connection.lock();
|
||||
if (!connection)
|
||||
{
|
||||
// The connection has been destructed
|
||||
callback(std::make_exception_ptr(signalr_exception("the hub connection has been deconstructed")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception)
|
||||
{
|
||||
callback(exception);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
pplx::task<void>(connection->m_handshakeTask).get();
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
auto handshake_exception = std::current_exception();
|
||||
connection->m_connection->stop([callback, handshake_exception](std::exception_ptr)
|
||||
{
|
||||
callback(handshake_exception);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void hub_connection_impl::stop(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
m_callback_manager.clear(json::value::parse(_XPLATSTR("{ \"error\" : \"connection was stopped before invocation result was received\"}")));
|
||||
m_connection->stop(callback);
|
||||
}
|
||||
|
||||
enum MessageType
|
||||
{
|
||||
Invocation = 1,
|
||||
StreamItem,
|
||||
Completion,
|
||||
StreamInvocation,
|
||||
CancelInvocation,
|
||||
Ping,
|
||||
Close,
|
||||
};
|
||||
|
||||
void hub_connection_impl::process_message(const std::string& response)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto pos = response.find('\x1e');
|
||||
std::size_t lastPos = 0;
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
auto message = response.substr(lastPos, pos - lastPos);
|
||||
const auto result = web::json::value::parse(utility::conversions::to_string_t(message));
|
||||
|
||||
if (!result.is_object())
|
||||
{
|
||||
m_logger.log(trace_level::info, std::string("unexpected response received from the server: ")
|
||||
.append(message));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_handshakeReceived)
|
||||
{
|
||||
if (result.has_field(_XPLATSTR("error")))
|
||||
{
|
||||
auto error = utility::conversions::to_utf8string(result.at(_XPLATSTR("error")).as_string());
|
||||
m_logger.log(trace_level::errors, std::string("handshake error: ")
|
||||
.append(error));
|
||||
m_handshakeTask.set_exception(signalr_exception(std::string("Received an error during handshake: ").append(error)));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.has_field(_XPLATSTR("type")))
|
||||
{
|
||||
m_handshakeTask.set_exception(signalr_exception(std::string("Received unexpected message while waiting for the handshake response.")));
|
||||
}
|
||||
m_handshakeReceived = true;
|
||||
m_handshakeTask.set();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto messageType = result.at(_XPLATSTR("type"));
|
||||
switch (messageType.as_integer())
|
||||
{
|
||||
case MessageType::Invocation:
|
||||
{
|
||||
auto method = utility::conversions::to_utf8string(result.at(_XPLATSTR("target")).as_string());
|
||||
auto event = m_subscriptions.find(method);
|
||||
if (event != m_subscriptions.end())
|
||||
{
|
||||
event->second(result.at(_XPLATSTR("arguments")));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MessageType::StreamInvocation:
|
||||
// Sent to server only, should not be received by client
|
||||
throw std::runtime_error("Received unexpected message type 'StreamInvocation'.");
|
||||
case MessageType::StreamItem:
|
||||
// TODO
|
||||
break;
|
||||
case MessageType::Completion:
|
||||
{
|
||||
if (result.has_field(_XPLATSTR("error")) && result.has_field(_XPLATSTR("result")))
|
||||
{
|
||||
// TODO: error
|
||||
}
|
||||
invoke_callback(result);
|
||||
break;
|
||||
}
|
||||
case MessageType::CancelInvocation:
|
||||
// Sent to server only, should not be received by client
|
||||
throw std::runtime_error("Received unexpected message type 'CancelInvocation'.");
|
||||
case MessageType::Ping:
|
||||
// TODO
|
||||
break;
|
||||
case MessageType::Close:
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
lastPos = pos + 1;
|
||||
pos = response.find('\x1e', lastPos);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
m_logger.log(trace_level::errors, std::string("error occured when parsing response: ")
|
||||
.append(e.what())
|
||||
.append(". response: ")
|
||||
.append(response));
|
||||
}
|
||||
}
|
||||
|
||||
bool hub_connection_impl::invoke_callback(const web::json::value& message)
|
||||
{
|
||||
auto id = utility::conversions::to_utf8string(message.at(_XPLATSTR("invocationId")).as_string());
|
||||
if (!m_callback_manager.invoke_callback(id, message, true))
|
||||
{
|
||||
m_logger.log(trace_level::info, std::string("no callback found for id: ").append(id));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hub_connection_impl::invoke(const std::string& method_name, const json::value& arguments, std::function<void(const web::json::value&, std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
_ASSERTE(arguments.is_array());
|
||||
|
||||
const auto callback_id = m_callback_manager.register_callback(
|
||||
create_hub_invocation_callback(m_logger, [callback](const json::value& result) { callback(result, nullptr); },
|
||||
[callback](const std::exception_ptr e) { callback(json::value(), e); }));
|
||||
|
||||
invoke_hub_method(method_name, arguments, callback_id, nullptr,
|
||||
[callback](const std::exception_ptr e){ callback(json::value(), e); });
|
||||
}
|
||||
|
||||
void hub_connection_impl::send(const std::string& method_name, const json::value& arguments, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
_ASSERTE(arguments.is_array());
|
||||
|
||||
invoke_hub_method(method_name, arguments, "",
|
||||
[callback]() { callback(nullptr); },
|
||||
[callback](const std::exception_ptr e){ callback(e); });
|
||||
}
|
||||
|
||||
void hub_connection_impl::invoke_hub_method(const std::string& method_name, const json::value& arguments,
|
||||
const std::string& callback_id, std::function<void()> set_completion, std::function<void(const std::exception_ptr)> set_exception) noexcept
|
||||
{
|
||||
json::value request;
|
||||
request[_XPLATSTR("type")] = json::value(1);
|
||||
if (!callback_id.empty())
|
||||
{
|
||||
request[_XPLATSTR("invocationId")] = json::value::string(utility::conversions::to_string_t(callback_id));
|
||||
}
|
||||
request[_XPLATSTR("target")] = json::value::string(utility::conversions::to_string_t(method_name));
|
||||
request[_XPLATSTR("arguments")] = arguments;
|
||||
|
||||
// weak_ptr prevents a circular dependency leading to memory leak and other problems
|
||||
auto weak_hub_connection = std::weak_ptr<hub_connection_impl>(shared_from_this());
|
||||
|
||||
m_connection->send(utility::conversions::to_utf8string(request.serialize() + _XPLATSTR('\x1e')), [set_completion, set_exception, weak_hub_connection, callback_id](std::exception_ptr exception)
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
set_exception(exception);
|
||||
auto hub_connection = weak_hub_connection.lock();
|
||||
if (hub_connection)
|
||||
{
|
||||
hub_connection->m_callback_manager.remove_callback(callback_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (callback_id.empty())
|
||||
{
|
||||
// complete nonBlocking call
|
||||
set_completion();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connection_state hub_connection_impl::get_connection_state() const noexcept
|
||||
{
|
||||
return m_connection->get_connection_state();
|
||||
}
|
||||
|
||||
std::string hub_connection_impl::get_connection_id() const
|
||||
{
|
||||
return m_connection->get_connection_id();
|
||||
}
|
||||
|
||||
void hub_connection_impl::set_client_config(const signalr_client_config& config)
|
||||
{
|
||||
m_signalr_client_config = config;
|
||||
m_connection->set_client_config(config);
|
||||
}
|
||||
|
||||
void hub_connection_impl::set_disconnected(const std::function<void()>& disconnected)
|
||||
{
|
||||
m_disconnected = disconnected;
|
||||
}
|
||||
|
||||
// unnamed namespace makes it invisble outside this translation unit
|
||||
namespace
|
||||
{
|
||||
static std::function<void(const json::value&)> create_hub_invocation_callback(const logger& logger,
|
||||
const std::function<void(const json::value&)>& set_result,
|
||||
const std::function<void(const std::exception_ptr)>& set_exception)
|
||||
{
|
||||
return [logger, set_result, set_exception](const json::value& message)
|
||||
{
|
||||
if (message.has_field(_XPLATSTR("result")))
|
||||
{
|
||||
set_result(message.at(_XPLATSTR("result")));
|
||||
}
|
||||
else if (message.has_field(_XPLATSTR("error")))
|
||||
{
|
||||
set_exception(
|
||||
std::make_exception_ptr(
|
||||
hub_exception(utility::conversions::to_utf8string(message.at(_XPLATSTR("error")).serialize()))));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_result(json::value::value());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "connection_impl.h"
|
||||
#include "callback_manager.h"
|
||||
#include "case_insensitive_comparison_utils.h"
|
||||
|
||||
using namespace web;
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
// Note:
|
||||
// Factory methods and private constructors prevent from using this class incorrectly. Because this class
|
||||
// derives from `std::enable_shared_from_this` the instance has to be owned by a `std::shared_ptr` whenever
|
||||
// a member method calls `std::shared_from_this()` otherwise the behavior is undefined. Therefore constructors
|
||||
// are private to disallow creating instances directly and factory methods return `std::shared_ptr<connection_impl>`.
|
||||
class hub_connection_impl : public std::enable_shared_from_this<hub_connection_impl>
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<hub_connection_impl> create(const std::string& url, trace_level trace_level,
|
||||
const std::shared_ptr<log_writer>& log_writer);
|
||||
|
||||
static std::shared_ptr<hub_connection_impl> create(const std::string& url, trace_level trace_level,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<http_client> http_client,
|
||||
std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
hub_connection_impl(const hub_connection_impl&) = delete;
|
||||
hub_connection_impl& operator=(const hub_connection_impl&) = delete;
|
||||
|
||||
void on(const std::string& event_name, const std::function<void(const json::value &)>& handler);
|
||||
|
||||
void invoke(const std::string& method_name, const json::value& arguments, std::function<void(const json::value&, std::exception_ptr)> callback) noexcept;
|
||||
void send(const std::string& method_name, const json::value& arguments, std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
|
||||
void start(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
void stop(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
|
||||
connection_state get_connection_state() const noexcept;
|
||||
std::string get_connection_id() const;
|
||||
|
||||
void set_client_config(const signalr_client_config& config);
|
||||
void set_disconnected(const std::function<void()>& disconnected);
|
||||
|
||||
private:
|
||||
hub_connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
std::shared_ptr<connection_impl> m_connection;
|
||||
logger m_logger;
|
||||
callback_manager m_callback_manager;
|
||||
std::unordered_map<std::string, std::function<void(const json::value &)>, case_insensitive_hash, case_insensitive_equals> m_subscriptions;
|
||||
bool m_handshakeReceived;
|
||||
pplx::task_completion_event<void> m_handshakeTask;
|
||||
std::function<void()> m_disconnected;
|
||||
signalr_client_config m_signalr_client_config;
|
||||
|
||||
void initialize();
|
||||
|
||||
void process_message(const std::string& message);
|
||||
|
||||
void invoke_hub_method(const std::string& method_name, const json::value& arguments, const std::string& callback_id,
|
||||
std::function<void()> set_completion, std::function<void(const std::exception_ptr)> set_exception) noexcept;
|
||||
bool invoke_callback(const web::json::value& message);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "logger.h"
|
||||
#include "cpprest/asyncrt_utils.h"
|
||||
#include <iomanip>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
logger::logger(const std::shared_ptr<log_writer>& log_writer, trace_level trace_level) noexcept
|
||||
: m_log_writer(log_writer), m_trace_level(trace_level)
|
||||
{ }
|
||||
|
||||
void logger::log(trace_level level, const std::string& entry) const
|
||||
{
|
||||
if ((level & m_trace_level) != trace_level::none)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::stringstream os;
|
||||
os << utility::conversions::to_utf8string(utility::datetime::utc_now().to_string(utility::datetime::date_format::ISO_8601)) << " ["
|
||||
<< std::left << std::setw(12) << translate_trace_level(level) << "] "<< entry << std::endl;
|
||||
m_log_writer->write(os.str());
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "error occurred when logging: " << e.what()
|
||||
<< std::endl << " entry: " << entry << std::endl;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "unknown error occurred when logging" << std::endl << " entry: " << entry << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string logger::translate_trace_level(trace_level trace_level)
|
||||
{
|
||||
switch (trace_level)
|
||||
{
|
||||
case signalr::trace_level::messages:
|
||||
return "message";
|
||||
case signalr::trace_level::state_changes:
|
||||
return "state change";
|
||||
case signalr::trace_level::events:
|
||||
return "event";
|
||||
case signalr::trace_level::errors:
|
||||
return "error";
|
||||
case signalr::trace_level::info:
|
||||
return "info";
|
||||
default:
|
||||
_ASSERTE(false);
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "signalrclient/trace_level.h"
|
||||
#include "signalrclient/log_writer.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class logger
|
||||
{
|
||||
public:
|
||||
logger(const std::shared_ptr<log_writer>& log_writer, trace_level trace_level) noexcept;
|
||||
|
||||
void log(trace_level level, const std::string& entry) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<log_writer> m_log_writer;
|
||||
trace_level m_trace_level;
|
||||
|
||||
static std::string translate_trace_level(trace_level trace_level);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined (__GNUC__)
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args&&... args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "negotiate.h"
|
||||
#include "url_builder.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace negotiate
|
||||
{
|
||||
pplx::task<negotiation_response> negotiate(http_client& client, const std::string& base_url,
|
||||
const signalr_client_config& config)
|
||||
{
|
||||
auto negotiate_url = url_builder::build_negotiate(base_url);
|
||||
|
||||
pplx::task_completion_event<negotiation_response> tce;
|
||||
|
||||
// TODO: signalr_client_config
|
||||
http_request request;
|
||||
request.method = http_method::POST;
|
||||
|
||||
for (auto& header : config.get_http_headers())
|
||||
{
|
||||
request.headers.insert(std::make_pair(utility::conversions::to_utf8string(header.first), utility::conversions::to_utf8string(header.second)));
|
||||
}
|
||||
|
||||
client.send(negotiate_url, request, [tce](http_response http_response, std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
tce.set_exception(exception);
|
||||
return;
|
||||
}
|
||||
|
||||
if (http_response.status_code != 200)
|
||||
{
|
||||
tce.set_exception(signalr_exception("negotiate failed with status code " + std::to_string(http_response.status_code)));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto negotiation_response_json = web::json::value::parse(utility::conversions::to_string_t(http_response.content));
|
||||
|
||||
negotiation_response response;
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("error")))
|
||||
{
|
||||
response.error = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("error")].as_string());
|
||||
tce.set(std::move(response));
|
||||
return;
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("connectionId")))
|
||||
{
|
||||
response.connectionId = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("connectionId")].as_string());
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("availableTransports")))
|
||||
{
|
||||
for (auto transportData : negotiation_response_json[_XPLATSTR("availableTransports")].as_array())
|
||||
{
|
||||
available_transport transport;
|
||||
transport.transport = utility::conversions::to_utf8string(transportData[_XPLATSTR("transport")].as_string());
|
||||
|
||||
for (auto format : transportData[_XPLATSTR("transferFormats")].as_array())
|
||||
{
|
||||
transport.transfer_formats.push_back(utility::conversions::to_utf8string(format.as_string()));
|
||||
}
|
||||
|
||||
response.availableTransports.push_back(transport);
|
||||
}
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("url")))
|
||||
{
|
||||
response.url = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("url")].as_string());
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("accessToken")))
|
||||
{
|
||||
response.accessToken = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("accessToken")].as_string());
|
||||
}
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("ProtocolVersion")))
|
||||
{
|
||||
tce.set_exception(signalr_exception("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details."));
|
||||
}
|
||||
|
||||
tce.set(std::move(response));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tce.set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
return pplx::create_task(tce);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
#include "signalrclient/transport_type.h"
|
||||
#include "web_request_factory.h"
|
||||
#include "negotiation_response.h"
|
||||
#include "signalrclient/http_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace negotiate
|
||||
{
|
||||
pplx::task<negotiation_response> negotiate(http_client& client, const std::string& base_url,
|
||||
const signalr_client_config& signalr_client_config = signalr::signalr_client_config{});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
struct available_transport
|
||||
{
|
||||
std::string transport;
|
||||
std::vector<std::string> transfer_formats;
|
||||
};
|
||||
|
||||
struct negotiation_response
|
||||
{
|
||||
std::string connectionId;
|
||||
std::vector<available_transport> availableTransports;
|
||||
std::string url;
|
||||
std::string accessToken;
|
||||
std::string error;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
#include "cpprest/http_client.h"
|
||||
#include "cpprest/ws_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
void signalr_client_config::set_proxy(const web::web_proxy &proxy)
|
||||
{
|
||||
m_http_client_config.set_proxy(proxy);
|
||||
m_websocket_client_config.set_proxy(proxy);
|
||||
}
|
||||
|
||||
void signalr_client_config::set_credentials(const web::credentials &credentials)
|
||||
{
|
||||
m_http_client_config.set_credentials(credentials);
|
||||
m_websocket_client_config.set_credentials(credentials);
|
||||
}
|
||||
|
||||
web::http::client::http_client_config signalr_client_config::get_http_client_config() const
|
||||
{
|
||||
return m_http_client_config;
|
||||
}
|
||||
|
||||
void signalr_client_config::set_http_client_config(const web::http::client::http_client_config& http_client_config)
|
||||
{
|
||||
m_http_client_config = http_client_config;
|
||||
}
|
||||
|
||||
web::websockets::client::websocket_client_config signalr_client_config::get_websocket_client_config() const noexcept
|
||||
{
|
||||
return m_websocket_client_config;
|
||||
}
|
||||
|
||||
void signalr_client_config::set_websocket_client_config(const web::websockets::client::websocket_client_config& websocket_client_config)
|
||||
{
|
||||
m_websocket_client_config = websocket_client_config;
|
||||
}
|
||||
|
||||
web::http::http_headers signalr_client_config::get_http_headers() const noexcept
|
||||
{
|
||||
return m_http_headers;
|
||||
}
|
||||
|
||||
void signalr_client_config::set_http_headers(const web::http::http_headers& http_headers)
|
||||
{
|
||||
m_http_headers = http_headers;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32 // used in the default log writer and to build the dll
|
||||
|
||||
// prevents from defining min/max macros that conflict with std::min()/std::max() functions
|
||||
#define NOMINMAX
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include "cpprest/details/basic_types.h"
|
||||
#include "cpprest/json.h"
|
||||
#include "pplx/pplxtasks.h"
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "trace_log_writer.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
void trace_log_writer::write(const std::string &entry)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// OutputDebugString is thread safe
|
||||
OutputDebugStringA(entry.c_str());
|
||||
#else
|
||||
// Note: there is no data race for standard output streams in C++ 11 but the results
|
||||
// might be garbled when the method is called concurrently from multiple threads
|
||||
#ifdef _UTF16_STRINGS
|
||||
std::wclog << entry;
|
||||
#else
|
||||
std::clog << entry;
|
||||
#endif // _UTF16_STRINGS
|
||||
|
||||
#endif // _MS_WINDOWS
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "signalrclient/log_writer.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class trace_log_writer : public log_writer
|
||||
{
|
||||
public:
|
||||
void __cdecl write(const std::string &entry) override;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "transport.h"
|
||||
#include "connection_impl.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
transport::transport(const logger& logger)
|
||||
: m_logger(logger)
|
||||
{}
|
||||
|
||||
// Do NOT remove this destructor. Letting the compiler generate and inline the default dtor may lead to
|
||||
// undefinded behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/
|
||||
transport::~transport()
|
||||
{ }
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pplx/pplxtasks.h"
|
||||
#include "signalrclient/transport_type.h"
|
||||
#include "signalrclient/transfer_format.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class transport
|
||||
{
|
||||
public:
|
||||
virtual transport_type get_transport_type() const = 0;
|
||||
|
||||
virtual ~transport();
|
||||
|
||||
virtual void start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback) noexcept = 0;
|
||||
virtual void stop(std::function<void(std::exception_ptr)> callback) noexcept = 0;
|
||||
virtual void on_close(std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void send(std::string payload, std::function<void(std::exception_ptr)> callback) noexcept = 0;
|
||||
|
||||
virtual void on_receive(std::function<void(std::string, std::exception_ptr)> callback) = 0;
|
||||
|
||||
protected:
|
||||
transport(const logger& logger);
|
||||
|
||||
logger m_logger;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "transport_factory.h"
|
||||
#include "websocket_transport.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
std::shared_ptr<transport> transport_factory::create_transport(transport_type transport_type, const logger& logger,
|
||||
const signalr_client_config& signalr_client_config)
|
||||
{
|
||||
if (transport_type == signalr::transport_type::websockets)
|
||||
{
|
||||
return websocket_transport::create(
|
||||
[signalr_client_config](){ return std::make_shared<default_websocket_client>(signalr_client_config); },
|
||||
logger);
|
||||
}
|
||||
|
||||
throw std::runtime_error("not implemented");
|
||||
}
|
||||
|
||||
transport_factory::~transport_factory()
|
||||
{ }
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
#include "signalrclient/transport_type.h"
|
||||
#include "transport.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class transport_factory
|
||||
{
|
||||
public:
|
||||
virtual std::shared_ptr<transport> create_transport(transport_type transport_type, const logger& logger,
|
||||
const signalr_client_config& signalr_client_config);
|
||||
|
||||
virtual ~transport_factory();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "cpprest/http_client.h"
|
||||
#include "signalrclient/transport_type.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace url_builder
|
||||
{
|
||||
web::uri_builder &convert_to_websocket_url(web::uri_builder &builder, transport_type transport)
|
||||
{
|
||||
if (transport == transport_type::websockets)
|
||||
{
|
||||
if (builder.scheme() == _XPLATSTR("https"))
|
||||
{
|
||||
builder.set_scheme(utility::conversions::to_string_t("wss"));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.set_scheme(utility::conversions::to_string_t("ws"));
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
web::uri_builder build_uri(const std::string& base_url, const std::string& command, const std::string& query_string)
|
||||
{
|
||||
web::uri_builder builder(utility::conversions::to_string_t(base_url));
|
||||
builder.append_path(utility::conversions::to_string_t(command));
|
||||
return builder.append_query(utility::conversions::to_string_t(query_string));
|
||||
}
|
||||
|
||||
web::uri_builder build_uri(const std::string& base_url, const std::string& command)
|
||||
{
|
||||
web::uri_builder builder(utility::conversions::to_string_t(base_url));
|
||||
return builder.append_path(utility::conversions::to_string_t(command));
|
||||
}
|
||||
|
||||
std::string build_negotiate(const std::string& base_url)
|
||||
{
|
||||
return utility::conversions::to_utf8string(build_uri(base_url, "negotiate").to_string());
|
||||
}
|
||||
|
||||
std::string build_connect(const std::string& base_url, transport_type transport, const std::string& query_string)
|
||||
{
|
||||
auto builder = build_uri(base_url, "", query_string);
|
||||
return utility::conversions::to_utf8string(convert_to_websocket_url(builder, transport).to_string());
|
||||
}
|
||||
|
||||
std::string build_start(const std::string& base_url, const std::string &query_string)
|
||||
{
|
||||
return utility::conversions::to_utf8string(build_uri(base_url, "", query_string).to_string());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpprest/http_client.h"
|
||||
#include "signalrclient/transport_type.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace url_builder
|
||||
{
|
||||
std::string build_negotiate(const std::string& base_url);
|
||||
std::string build_connect(const std::string& base_url, transport_type transport, const std::string& query_string);
|
||||
std::string build_start(const std::string& base_url, const std::string& query_string);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "cpprest/http_client.h"
|
||||
#include "web_request.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
web_request::web_request(const std::string& url)
|
||||
: m_url(url)
|
||||
{ }
|
||||
|
||||
void web_request::set_method(const std::string &method)
|
||||
{
|
||||
m_request.set_method(utility::conversions::to_string_t(method));
|
||||
}
|
||||
|
||||
void web_request::set_user_agent(const std::string &user_agent_string)
|
||||
{
|
||||
m_user_agent_string = user_agent_string;
|
||||
}
|
||||
|
||||
void web_request::set_client_config(const signalr_client_config& signalr_client_config)
|
||||
{
|
||||
m_signalr_client_config = signalr_client_config;
|
||||
}
|
||||
|
||||
pplx::task<web_response> web_request::get_response()
|
||||
{
|
||||
web::http::client::http_client client(utility::conversions::to_string_t(m_url), m_signalr_client_config.get_http_client_config());
|
||||
|
||||
m_request.headers() = m_signalr_client_config.get_http_headers();
|
||||
if (!m_user_agent_string.empty())
|
||||
{
|
||||
m_request.headers()[_XPLATSTR("User-Agent")] = utility::conversions::to_string_t(m_user_agent_string);
|
||||
}
|
||||
|
||||
return client.request(m_request)
|
||||
.then([](web::http::http_response response)
|
||||
{
|
||||
return web_response
|
||||
{
|
||||
response.status_code(),
|
||||
utility::conversions::to_utf8string(response.reason_phrase()),
|
||||
response.extract_string().then([](const utility::string_t& body)
|
||||
{
|
||||
return utility::conversions::to_utf8string(body);
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
web_request::~web_request() = default;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "web_response.h"
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class web_request
|
||||
{
|
||||
public:
|
||||
explicit web_request(const std::string& url);
|
||||
|
||||
virtual void set_method(const std::string &method);
|
||||
virtual void set_user_agent(const std::string &user_agent_string);
|
||||
virtual void set_client_config(const signalr_client_config& signalr_client_config);
|
||||
|
||||
virtual pplx::task<web_response> get_response();
|
||||
|
||||
web_request& operator=(const web_request&) = delete;
|
||||
|
||||
virtual ~web_request();
|
||||
|
||||
private:
|
||||
const std::string m_url;
|
||||
web::http::http_request m_request;
|
||||
std::string m_user_agent_string;
|
||||
signalr_client_config m_signalr_client_config;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "web_request_factory.h"
|
||||
#include "make_unique.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
std::unique_ptr<web_request> web_request_factory::create_web_request(const std::string& url)
|
||||
{
|
||||
return std::make_unique<web_request>(url);
|
||||
}
|
||||
|
||||
web_request_factory::~web_request_factory()
|
||||
{}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "web_request.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class web_request_factory
|
||||
{
|
||||
public:
|
||||
virtual std::unique_ptr<web_request> create_web_request(const std::string& url);
|
||||
|
||||
virtual ~web_request_factory();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pplx/pplxtasks.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
struct web_response
|
||||
{
|
||||
unsigned short status_code;
|
||||
std::string reason_phrase;
|
||||
pplx::task<std::string> body;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "websocket_transport.h"
|
||||
#include "logger.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
#include <future>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
std::shared_ptr<transport> websocket_transport::create(const std::function<std::shared_ptr<websocket_client>()>& websocket_client_factory,
|
||||
const logger& logger)
|
||||
{
|
||||
return std::shared_ptr<transport>(
|
||||
new websocket_transport(websocket_client_factory, logger));
|
||||
}
|
||||
|
||||
websocket_transport::websocket_transport(const std::function<std::shared_ptr<websocket_client>()>& websocket_client_factory,
|
||||
const logger& logger)
|
||||
: transport(logger), m_websocket_client_factory(websocket_client_factory), m_close_callback([](std::exception_ptr) {}),
|
||||
m_process_response_callback([](std::string, std::exception_ptr) {})
|
||||
{
|
||||
// we use this cts to check if the receive loop is running so it should be
|
||||
// initially cancelled to indicate that the receive loop is not running
|
||||
m_receive_loop_cts.cancel();
|
||||
}
|
||||
|
||||
websocket_transport::~websocket_transport()
|
||||
{
|
||||
try
|
||||
{
|
||||
pplx::task_completion_event<void> event;
|
||||
stop([event](std::exception_ptr) { event.set(); });
|
||||
pplx::create_task(event).get();
|
||||
}
|
||||
catch (...) // must not throw from the destructor
|
||||
{}
|
||||
}
|
||||
|
||||
transport_type websocket_transport::get_transport_type() const noexcept
|
||||
{
|
||||
return transport_type::websockets;
|
||||
}
|
||||
|
||||
// Note that the connection assumes that the error callback won't be fired when the result is being processed. This
|
||||
// may no longer be true when we replace the `receive_loop` with "on_message_received" and "on_close" events if they
|
||||
// can be fired on different threads in which case we will have to lock before setting groups token and message id.
|
||||
void websocket_transport::receive_loop(pplx::cancellation_token_source cts)
|
||||
{
|
||||
auto this_transport = shared_from_this();
|
||||
auto logger = this_transport->m_logger;
|
||||
|
||||
// Passing the `std::weak_ptr<websocket_transport>` prevents from a memory leak where we would capture the shared_ptr to
|
||||
// the transport in the continuation lambda and as a result as long as the loop runs the ref count would never get to
|
||||
// zero. Now we capture the weak pointer and get the shared pointer only when the continuation runs so the ref count is
|
||||
// incremented when the shared pointer is acquired and then decremented when it goes out of scope of the continuation.
|
||||
auto weak_transport = std::weak_ptr<websocket_transport>(this_transport);
|
||||
|
||||
auto websocket_client = this_transport->safe_get_websocket_client();
|
||||
|
||||
// There are two cases when we exit the loop. The first case is implicit - we pass the cancellation_token
|
||||
// to `then` (note this is after the lambda body) and if the token is cancelled the continuation will not
|
||||
// run at all. The second - explicit - case happens if the token gets cancelled after the continuation has
|
||||
// been started in which case we just stop the loop by not scheduling another receive task.
|
||||
websocket_client->receive([weak_transport, cts, logger, websocket_client](std::string message, std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] error receiving response from websocket: ")
|
||||
.append(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] unknown error occurred when receiving response from websocket"));
|
||||
|
||||
exception = std::make_exception_ptr(signalr_exception("unknown error"));
|
||||
}
|
||||
|
||||
cts.cancel();
|
||||
|
||||
std::promise<void> promise;
|
||||
websocket_client->stop([&promise](std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
promise.set_exception(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.set_value();
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
promise.get_future().get();
|
||||
}
|
||||
// We prefer the outer exception bubbling up to the user
|
||||
// REVIEW: log here?
|
||||
catch (...) {}
|
||||
|
||||
auto transport = weak_transport.lock();
|
||||
if (transport)
|
||||
{
|
||||
transport->m_close_callback(exception);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto transport = weak_transport.lock();
|
||||
if (transport)
|
||||
{
|
||||
transport->m_process_response_callback(message, nullptr);
|
||||
|
||||
if (!cts.get_token().is_canceled())
|
||||
{
|
||||
transport->receive_loop(cts);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<websocket_client> websocket_transport::safe_get_websocket_client()
|
||||
{
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(m_websocket_client_lock);
|
||||
auto websocket_client = m_websocket_client;
|
||||
|
||||
return websocket_client;
|
||||
}
|
||||
}
|
||||
|
||||
void websocket_transport::start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
web::uri uri(utility::conversions::to_string_t(url));
|
||||
_ASSERTE(uri.scheme() == _XPLATSTR("ws") || uri.scheme() == _XPLATSTR("wss"));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> stop_lock(m_start_stop_lock);
|
||||
|
||||
if (!m_receive_loop_cts.get_token().is_canceled())
|
||||
{
|
||||
callback(std::make_exception_ptr(signalr_exception("transport already connected")));
|
||||
return;
|
||||
}
|
||||
|
||||
m_logger.log(trace_level::info,
|
||||
std::string("[websocket transport] connecting to: ")
|
||||
.append(url));
|
||||
|
||||
auto websocket_client = m_websocket_client_factory();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> client_lock(m_websocket_client_lock);
|
||||
m_websocket_client = websocket_client;
|
||||
}
|
||||
|
||||
pplx::cancellation_token_source receive_loop_cts;
|
||||
|
||||
auto transport = shared_from_this();
|
||||
|
||||
websocket_client->start(url, format, [transport, receive_loop_cts, callback](std::exception_ptr exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
transport->receive_loop(receive_loop_cts);
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
transport->m_logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] exception when connecting to the server: ")
|
||||
.append(e.what()));
|
||||
|
||||
receive_loop_cts.cancel();
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
m_receive_loop_cts = receive_loop_cts;
|
||||
}
|
||||
}
|
||||
|
||||
void websocket_transport::stop(std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
std::shared_ptr<websocket_client> websocket_client = nullptr;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_start_stop_lock);
|
||||
|
||||
if (m_receive_loop_cts.get_token().is_canceled())
|
||||
{
|
||||
callback(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
m_receive_loop_cts.cancel();
|
||||
|
||||
websocket_client = safe_get_websocket_client();
|
||||
}
|
||||
|
||||
auto logger = m_logger;
|
||||
auto close_callback = m_close_callback;
|
||||
|
||||
websocket_client->stop([logger, callback, close_callback](std::exception_ptr exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] exception when closing websocket: ")
|
||||
.append(e.what()));
|
||||
|
||||
callback(exception);
|
||||
}
|
||||
|
||||
close_callback(exception);
|
||||
});
|
||||
}
|
||||
|
||||
void websocket_transport::on_close(std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
m_close_callback = callback;
|
||||
}
|
||||
|
||||
void websocket_transport::on_receive(std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
m_process_response_callback = callback;
|
||||
}
|
||||
|
||||
void websocket_transport::send(std::string payload, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
safe_get_websocket_client()->send(payload, [callback](std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
callback(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpprest/ws_client.h"
|
||||
#include "url_builder.h"
|
||||
#include "transport.h"
|
||||
#include "logger.h"
|
||||
#include "default_websocket_client.h"
|
||||
#include "connection_impl.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class websocket_transport : public transport, public std::enable_shared_from_this<websocket_transport>
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<transport> create(const std::function<std::shared_ptr<websocket_client>()>& websocket_client_factory,
|
||||
const logger& logger);
|
||||
|
||||
~websocket_transport();
|
||||
|
||||
websocket_transport(const websocket_transport&) = delete;
|
||||
|
||||
websocket_transport& operator=(const websocket_transport&) = delete;
|
||||
|
||||
transport_type get_transport_type() const noexcept override;
|
||||
|
||||
void start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback) noexcept override;
|
||||
void stop(std::function<void(std::exception_ptr)> callback) noexcept override;
|
||||
void on_close(std::function<void(std::exception_ptr)> callback) override;
|
||||
|
||||
void send(std::string payload, std::function<void(std::exception_ptr)> callback) noexcept override;
|
||||
|
||||
void on_receive(std::function<void(std::string, std::exception_ptr)>) override;
|
||||
|
||||
private:
|
||||
websocket_transport(const std::function<std::shared_ptr<websocket_client>()>& websocket_client_factory,
|
||||
const logger& logger);
|
||||
|
||||
std::function<std::shared_ptr<websocket_client>()> m_websocket_client_factory;
|
||||
std::shared_ptr<websocket_client> m_websocket_client;
|
||||
std::mutex m_websocket_client_lock;
|
||||
std::mutex m_start_stop_lock;
|
||||
std::function<void(std::string, std::exception_ptr)> m_process_response_callback;
|
||||
std::function<void(std::exception_ptr)> m_close_callback;
|
||||
|
||||
pplx::cancellation_token_source m_receive_loop_cts;
|
||||
|
||||
void receive_loop(pplx::cancellation_token_source cts);
|
||||
|
||||
std::shared_ptr<websocket_client> safe_get_websocket_client();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\Build\SignalRClient.Build.Settings" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{18377AE8-E372-40CE-94FD-7F65008D39A3}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>signalrclient</RootNamespace>
|
||||
<ProjectName>signalrclientdll</ProjectName>
|
||||
<TargetName>$(SignalrClientTargetName)</TargetName>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or '$(SolutionDir)' == '*Undefined*'">..\..\..\..\</SolutionDir>
|
||||
<OutDir Condition="'$(OutDir)' == ''">$(SolutionDir)bin\$(SubSystem)\$(Platform)\$(Configuration)\</OutDir>
|
||||
<OutDir>$(OutDir)dll\</OutDir>
|
||||
<IntDir>$(Configuration)\dll\</IntDir>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\Build\Config.Definitions.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions Condition="'$(SubSystem)' != 'UWP'">_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(SubSystem)' == 'UWP'">_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>SIGNALRCLIENT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection_state.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\http_client.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_connection.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\log_writer.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\trace_level.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transfer_format.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transport_type.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\websocket_client.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\web_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\_exports.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\case_insensitive_comparison_utils.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\connection_impl.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\constants.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\default_http_client.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\default_websocket_client.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\http_sender.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\hub_connection_impl.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\callback_manager.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\logger.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\negotiate.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\negotiation_response.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\stdafx.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\trace_log_writer.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\transport.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\transport_factory.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\url_builder.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\websocket_transport.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\web_request.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\web_request_factory.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\web_response.h" />
|
||||
<ClInclude Include="..\..\resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\signalrclient\connection.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\connection_impl.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\default_http_client.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\default_websocket_client.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\http_sender.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\hub_connection.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\hub_connection_impl.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\callback_manager.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\logger.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\negotiate.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\signalr_client_config.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\transport.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\transport_factory.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\url_builder.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\trace_log_writer.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\websocket_transport.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\web_request.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\web_request_factory.cpp" />
|
||||
<ClCompile Include="..\..\dllmain.cpp">
|
||||
<CompileAsManaged>false</CompileAsManaged>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\Resource.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="cpprestsdk" Version="2.9.1.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Target Name="CreateVersionHeader" BeforeTargets="EnsureNuGetPackageBuildImports">
|
||||
<PropertyGroup>
|
||||
<Revision>$(build_number)</Revision>
|
||||
<Revision Condition="'$(Revision)' == ''">0</Revision>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<VersionHeaderContents Include="// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved." />
|
||||
<VersionHeaderContents Include="// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information." />
|
||||
<VersionHeaderContents Include="%0a" />
|
||||
<VersionHeaderContents Include="// This file is auto-generated" />
|
||||
<VersionHeaderContents Include="%0a" />
|
||||
<VersionHeaderContents Include="#define FileVersion $(SignalRClientCppVersionMajor),$(SignalRClientCppVersionMinor),$(SignalRClientCppVersionPatch),$(Revision)" />
|
||||
<VersionHeaderContents Include="#define FileVersionStr "$(SignalRClientCppVersionMajor).$(SignalRClientCppVersionMinor).$(SignalRClientCppVersionPatch).$(Revision)\0"" />
|
||||
<VersionHeaderContents Include="#define ProductVersion $(SignalRClientCppVersionMajor),$(SignalRClientCppVersionMinor),$(SignalRClientCppVersionPatch),$(Revision)" />
|
||||
<VersionHeaderContents Include="#define ProductVersionStr "$(SignalRClientCppVersionString)\0"" />
|
||||
<VersionHeaderContents Include="#define PlatformToolset "$(PlatformToolset)\0"" />
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile File="..\..\version.h" Lines="@(VersionHeaderContents)" OverWrite="true" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,185 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\signalrclient\callback_manager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\connection_impl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\constants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\default_websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\http_sender.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\hub_connection_impl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\logger.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\negotiation_response.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\transport.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\transport_factory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\url_builder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\web_request.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\web_request_factory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\web_response.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\websocket_transport.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\_exports.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection_state.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_exception.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\log_writer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transport_type.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\web_exception.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_connection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\trace_level.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\trace_log_writer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\case_insensitive_comparison_utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\default_http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\negotiate.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transfer_format.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\connection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\connection_impl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\default_websocket_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\http_sender.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\hub_connection_impl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\logger.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\transport.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\transport_factory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\url_builder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\trace_log_writer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\web_request.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\web_request_factory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\websocket_transport.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\hub_connection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\callback_manager.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\signalr_client_config.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\default_http_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\negotiate.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\Resource.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
Двоичный файл не отображается.
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "../signalrclient/stdafx.h"
|
||||
|
||||
#if !defined(__cplusplus_winrt)
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE /*hModule*/,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID /*lpReserved*/
|
||||
)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Resource.rc
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
|
||||
// This file is auto-generated
|
||||
|
||||
|
||||
#define FileVersion 1,0,0,0
|
||||
#define FileVersionStr "1.0.0.0\0"
|
||||
#define ProductVersion 1,0,0,0
|
||||
#define ProductVersionStr "1.0.0-alpha0\0"
|
||||
#define PlatformToolset "v141\0"
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 4e4df226fc197c0dda6e37f5c8c3845ca1e73a49
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d1110ceb7feb527ac58f274239d435e32dca4b84
|
|
@ -0,0 +1,5 @@
|
|||
add_subdirectory (gtest-1.7.0)
|
||||
enable_testing()
|
||||
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||
|
||||
add_subdirectory (signalrclienttests)
|
|
@ -0,0 +1,171 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\submodules\googletest\googletest\src\gtest-all.cc" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{CAC1267B-8778-4257-AAC6-CAF481723B01}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>gtest</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<TargetName>gtestd</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<TargetName>gtest</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<TargetName>gtestd</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<TargetName>gtest</TargetName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
<SourcePath>$(VC_SourcePath);</SourcePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
<SourcePath>$(VC_SourcePath);</SourcePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
<SourcePath>$(VC_SourcePath);</SourcePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
<SourcePath>$(VC_SourcePath);</SourcePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>..\..\submodules\googletest\googletest\include;..\..\submodules\googletest\googletest;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>..\..\submodules\googletest\googletest\include;..\..\submodules\googletest\googletest;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>..\..\submodules\googletest\googletest\include;..\..\submodules\googletest\googletest;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>..\..\submodules\googletest\googletest\include;..\..\submodules\googletest\googletest;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\submodules\googletest\googletest\src\gtest-all.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,102 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\Build\SignalRClient.Build.Settings" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{10376148-BCF4-4B55-98A5-3C98C87FD898}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>signalrclienttests</RootNamespace>
|
||||
<ProjectName>signalrclienttests</ProjectName>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\..\</SolutionDir>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\Build\Config.Definitions.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING;_CONSOLE;_LIB;NO_SIGNALRCLIENT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\submodules\googletest\googletest\include;..\..\..\..\include;..\..\..\..\src\signalrclient;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</TreatWarningAsError>
|
||||
<TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</TreatWarningAsError>
|
||||
<TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatWarningAsError>
|
||||
<TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\memory_log_writer.h" />
|
||||
<ClInclude Include="..\..\stdafx.h" />
|
||||
<ClInclude Include="..\..\targetver.h" />
|
||||
<ClInclude Include="..\..\test_http_client.h" />
|
||||
<ClInclude Include="..\..\test_transport_factory.h" />
|
||||
<ClInclude Include="..\..\test_utils.h" />
|
||||
<ClInclude Include="..\..\test_websocket_client.h" />
|
||||
<ClInclude Include="..\..\test_web_request_factory.h" />
|
||||
<ClInclude Include="..\..\web_request_stub.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\callback_manager_tests.cpp" />
|
||||
<ClCompile Include="..\..\case_insensitive_comparison_utils_tests.cpp" />
|
||||
<ClCompile Include="..\..\connection_impl_tests.cpp" />
|
||||
<ClCompile Include="..\..\http_sender_tests.cpp" />
|
||||
<ClCompile Include="..\..\hub_connection_impl_tests.cpp" />
|
||||
<ClCompile Include="..\..\hub_exception_tests.cpp" />
|
||||
<ClCompile Include="..\..\logger_tests.cpp" />
|
||||
<ClCompile Include="..\..\memory_log_writer.cpp" />
|
||||
<ClCompile Include="..\..\negotiate_tests.cpp" />
|
||||
<ClCompile Include="..\..\signalrclienttests.cpp" />
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_http_client.cpp" />
|
||||
<ClCompile Include="..\..\test_transport_factory.cpp" />
|
||||
<ClCompile Include="..\..\test_utils.cpp" />
|
||||
<ClCompile Include="..\..\test_websocket_client.cpp" />
|
||||
<ClCompile Include="..\..\test_web_request_factory.cpp" />
|
||||
<ClCompile Include="..\..\url_builder_tests.cpp" />
|
||||
<ClCompile Include="..\..\websocket_transport_tests.cpp" />
|
||||
<ClCompile Include="..\..\web_request_stub.cpp" />
|
||||
<ClCompile Include="..\..\web_request_tests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\SignalRClient\Build\VS\SignalRClient.vcxproj">
|
||||
<Project>{87ed3ad4-d820-48cd-8382-a12564213a12}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\gtest\gtest.vcxproj">
|
||||
<Project>{cac1267b-8778-4257-aac6-caf481723b01}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="cpprestsdk" Version="2.9.1.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
<PackageReference Include="cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<PropertyGroup>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<TestProjectSkipReason>Flaky, due to https://github.com/aspnet/AspNetCore/issues/8421</TestProjectSkipReason>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\web_request_stub.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test_web_request_factory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test_websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\memory_log_writer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test_utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test_transport_factory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test_http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\SignalRClientTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\url_builder_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\http_sender_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\web_request_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\websocket_transport_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_web_request_factory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\web_request_stub.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_websocket_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\connection_impl_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\logger_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\memory_log_writer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_transport_factory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\hub_connection_impl_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\callback_manager_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\hub_exception_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\case_insensitive_comparison_utils_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_http_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\negotiate_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
|
||||
set (SOURCES
|
||||
callback_manager_tests.cpp
|
||||
case_insensitive_comparison_utils_tests.cpp
|
||||
connection_impl_tests.cpp
|
||||
http_sender_tests.cpp
|
||||
hub_connection_impl_tests.cpp
|
||||
hub_exception_tests.cpp
|
||||
logger_tests.cpp
|
||||
memory_log_writer.cpp
|
||||
request_sender_tests.cpp
|
||||
signalrclienttests.cpp
|
||||
stdafx.cpp
|
||||
test_transport_factory.cpp
|
||||
test_utils.cpp
|
||||
test_web_request_factory.cpp
|
||||
test_websocket_client.cpp
|
||||
url_builder_tests.cpp
|
||||
web_request_stub.cpp
|
||||
web_request_tests.cpp
|
||||
websocket_transport_tests.cpp
|
||||
)
|
||||
|
||||
include_directories(
|
||||
../../src/signalrclient)
|
||||
|
||||
find_package(Boost COMPONENTS system REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
add_executable (signalrclienttests ${SOURCES})
|
||||
target_link_libraries(signalrclienttests gtest gtest_main signalrclient ${CPPREST_SO} ${Boost_SYSTEM_LIBRARY} ${OPENSSL_LIBRARIES})
|
||||
add_test(signalrclienttests signalrclienttests)
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "callback_manager.h"
|
||||
|
||||
using namespace signalr;
|
||||
using namespace web;
|
||||
|
||||
TEST(callback_manager_register_callback, register_returns_unique_callback_ids)
|
||||
{
|
||||
callback_manager callback_mgr{ json::value::object() };
|
||||
auto callback_id1 = callback_mgr.register_callback([](const json::value&){});
|
||||
auto callback_id2 = callback_mgr.register_callback([](const web::json::value&){});
|
||||
|
||||
ASSERT_NE(callback_id1, callback_id2);
|
||||
}
|
||||
|
||||
TEST(callback_manager_invoke_callback, invoke_callback_invokes_and_removes_callback_if_remove_callback_true)
|
||||
{
|
||||
callback_manager callback_mgr{ json::value::object() };
|
||||
|
||||
std::string callback_argument{ "" };
|
||||
|
||||
auto callback_id = callback_mgr.register_callback(
|
||||
[&callback_argument](const json::value& argument)
|
||||
{
|
||||
callback_argument = utility::conversions::to_utf8string(argument.serialize());
|
||||
});
|
||||
|
||||
auto callback_found = callback_mgr.invoke_callback(callback_id, json::value::number(42), true);
|
||||
|
||||
ASSERT_TRUE(callback_found);
|
||||
ASSERT_EQ("42", callback_argument);
|
||||
ASSERT_FALSE(callback_mgr.remove_callback(callback_id));
|
||||
}
|
||||
|
||||
TEST(callback_manager_invoke_callback, invoke_callback_invokes_and_does_not_remove_callback_if_remove_callback_false)
|
||||
{
|
||||
callback_manager callback_mgr{ json::value::object() };
|
||||
|
||||
std::string callback_argument{ "" };
|
||||
|
||||
auto callback_id = callback_mgr.register_callback(
|
||||
[&callback_argument](const json::value& argument)
|
||||
{
|
||||
callback_argument = utility::conversions::to_utf8string(argument.serialize());
|
||||
});
|
||||
|
||||
auto callback_found = callback_mgr.invoke_callback(callback_id, json::value::number(42), false);
|
||||
|
||||
ASSERT_TRUE(callback_found);
|
||||
ASSERT_EQ("42", callback_argument);
|
||||
ASSERT_TRUE(callback_mgr.remove_callback(callback_id));
|
||||
}
|
||||
|
||||
TEST(callback_manager_ivoke_callback, invoke_callback_returns_false_for_invalid_callback_id)
|
||||
{
|
||||
callback_manager callback_mgr{ json::value::object() };
|
||||
auto callback_found = callback_mgr.invoke_callback("42", json::value::object(), true);
|
||||
|
||||
ASSERT_FALSE(callback_found);
|
||||
}
|
||||
|
||||
TEST(callback_manager_remove, remove_removes_callback_and_returns_true_for_valid_callback_id)
|
||||
{
|
||||
auto callback_called = false;
|
||||
|
||||
{
|
||||
callback_manager callback_mgr{ json::value::object() };
|
||||
|
||||
auto callback_id = callback_mgr.register_callback(
|
||||
[&callback_called](const json::value&)
|
||||
{
|
||||
callback_called = true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(callback_mgr.remove_callback(callback_id));
|
||||
}
|
||||
|
||||
ASSERT_FALSE(callback_called);
|
||||
}
|
||||
|
||||
TEST(callback_manager_remove, remove_returns_false_for_invalid_callback_id)
|
||||
{
|
||||
callback_manager callback_mgr{ json::value::object() };
|
||||
ASSERT_FALSE(callback_mgr.remove_callback("42"));
|
||||
}
|
||||
|
||||
TEST(callback_manager_clear, clear_invokes_all_callbacks)
|
||||
{
|
||||
callback_manager callback_mgr{ json::value::object() };
|
||||
auto invocation_count = 0;
|
||||
|
||||
for (auto i = 0; i < 10; i++)
|
||||
{
|
||||
callback_mgr.register_callback(
|
||||
[&invocation_count](const json::value& argument)
|
||||
{
|
||||
invocation_count++;
|
||||
ASSERT_EQ(_XPLATSTR("42"), argument.serialize());
|
||||
});
|
||||
}
|
||||
|
||||
callback_mgr.clear(json::value::number(42));
|
||||
|
||||
ASSERT_EQ(10, invocation_count);
|
||||
}
|
||||
|
||||
TEST(callback_manager_dtor, clear_invokes_all_callbacks)
|
||||
{
|
||||
auto invocation_count = 0;
|
||||
bool parameter_correct = true;
|
||||
|
||||
{
|
||||
callback_manager callback_mgr{ json::value::number(42) };
|
||||
for (auto i = 0; i < 10; i++)
|
||||
{
|
||||
callback_mgr.register_callback(
|
||||
[&invocation_count, ¶meter_correct](const json::value& argument)
|
||||
{
|
||||
invocation_count++;
|
||||
parameter_correct &= argument.serialize() == _XPLATSTR("42");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(10, invocation_count);
|
||||
ASSERT_TRUE(parameter_correct);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "case_insensitive_comparison_utils.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
TEST(case_insensitive_equals_functor, basic_comparison_tests)
|
||||
{
|
||||
case_insensitive_equals case_insensitive_compare;
|
||||
|
||||
ASSERT_TRUE(case_insensitive_compare("", ""));
|
||||
ASSERT_TRUE(case_insensitive_compare("abc", "ABC"));
|
||||
ASSERT_TRUE(case_insensitive_compare("abc123!@", "ABC123!@"));
|
||||
|
||||
ASSERT_FALSE(case_insensitive_compare("abc", "ABCD"));
|
||||
ASSERT_FALSE(case_insensitive_compare("abce", "ABCD"));
|
||||
}
|
||||
|
||||
TEST(case_insensitive_hash_functor, basic_hash_tests)
|
||||
{
|
||||
case_insensitive_hash case_insensitive_hasher;
|
||||
|
||||
ASSERT_EQ(0U, case_insensitive_hasher(""));
|
||||
|
||||
ASSERT_EQ(case_insensitive_hasher("abc"), case_insensitive_hasher("ABC"));
|
||||
ASSERT_EQ(case_insensitive_hasher("abc123!@"), case_insensitive_hasher("ABC123!@"));
|
||||
ASSERT_NE(case_insensitive_hasher("abcd"), case_insensitive_hasher("ABC"));
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "cpprest/details/basic_types.h"
|
||||
#include "cpprest/asyncrt_utils.h"
|
||||
#include "signalrclient/web_exception.h"
|
||||
#include "http_sender.h"
|
||||
#include "web_request_stub.h"
|
||||
#include "test_web_request_factory.h"
|
||||
|
||||
TEST(http_sender_get_response, request_sent_using_get_method)
|
||||
{
|
||||
std::string response_body{ "response body" };
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([response_body](const std::string &) -> std::unique_ptr<web_request>
|
||||
{
|
||||
auto request = new web_request_stub((unsigned short)200, "OK", response_body);
|
||||
request->on_get_response = [](web_request_stub& request) { ASSERT_EQ("GET", request.m_method); };
|
||||
|
||||
return std::unique_ptr<web_request>(request);
|
||||
});
|
||||
|
||||
ASSERT_EQ(response_body, http_sender::get(*web_request_factory, "url").get());
|
||||
}
|
||||
|
||||
TEST(http_sender_get_response, exception_thrown_if_status_code_not_200)
|
||||
{
|
||||
std::string response_phrase("Custom Not Found");
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([response_phrase](const std::string &) -> std::unique_ptr<web_request>
|
||||
{
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)404, response_phrase));
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
http_sender::get(*web_request_factory, "url").get();
|
||||
ASSERT_TRUE(false); // exception not thrown
|
||||
}
|
||||
catch (const web_exception &e)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
"web exception - 404 " + response_phrase,
|
||||
e.what());
|
||||
ASSERT_EQ(404, e.status_code());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(http_sender_get_response, user_agent_set)
|
||||
{
|
||||
std::string response_body{ "response body" };
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([response_body](const std::string &) -> std::unique_ptr<web_request>
|
||||
{
|
||||
auto request = new web_request_stub((unsigned short)200, "OK", response_body);
|
||||
request->on_get_response = [](web_request_stub& request)
|
||||
{
|
||||
ASSERT_EQ("SignalR.Client.Cpp/0.1.0-alpha0", request.m_user_agent_string);
|
||||
};
|
||||
|
||||
return std::unique_ptr<web_request>(request);
|
||||
});
|
||||
|
||||
ASSERT_EQ(response_body, http_sender::get(*web_request_factory, "url").get());
|
||||
}
|
||||
|
||||
TEST(http_sender_get_response, headers_set)
|
||||
{
|
||||
std::string response_body{ "response body" };
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([response_body](const std::string &) -> std::unique_ptr<web_request>
|
||||
{
|
||||
auto request = new web_request_stub((unsigned short)200, "OK", response_body);
|
||||
request->on_get_response = [](web_request_stub& request)
|
||||
{
|
||||
auto http_headers = request.m_signalr_client_config.get_http_headers();
|
||||
ASSERT_EQ(1U, http_headers.size());
|
||||
ASSERT_EQ(_XPLATSTR("123"), http_headers[_XPLATSTR("abc")]);
|
||||
};
|
||||
|
||||
return std::unique_ptr<web_request>(request);
|
||||
});
|
||||
|
||||
signalr::signalr_client_config signalr_client_config;
|
||||
auto http_headers = signalr_client_config.get_http_headers();
|
||||
http_headers[_XPLATSTR("abc")] = _XPLATSTR("123");
|
||||
signalr_client_config.set_http_headers(http_headers);
|
||||
|
||||
// ensures that web_request.get_response() was invoked
|
||||
ASSERT_EQ(response_body, http_sender::get(*web_request_factory, "url", signalr_client_config).get());
|
||||
}
|
|
@ -0,0 +1,980 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "test_utils.h"
|
||||
#include "test_transport_factory.h"
|
||||
#include "test_http_client.h"
|
||||
#include "hub_connection_impl.h"
|
||||
#include "trace_log_writer.h"
|
||||
#include "memory_log_writer.h"
|
||||
#include "signalrclient/hub_exception.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
std::shared_ptr<hub_connection_impl> create_hub_connection(std::shared_ptr<websocket_client> websocket_client = create_test_websocket_client(),
|
||||
std::shared_ptr<log_writer> log_writer = std::make_shared<trace_log_writer>(), trace_level trace_level = trace_level::all)
|
||||
{
|
||||
return hub_connection_impl::create(create_uri(), trace_level, log_writer,
|
||||
create_test_http_client(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
}
|
||||
|
||||
TEST(url, negotiate_appended_to_url)
|
||||
{
|
||||
std::string base_urls[] = { "http://fakeuri", "http://fakeuri/" };
|
||||
|
||||
for (const auto& base_url : base_urls)
|
||||
{
|
||||
std::string requested_url;
|
||||
auto http_client = std::make_unique<test_http_client>([&requested_url](const std::string& url, http_request)
|
||||
{
|
||||
requested_url = url;
|
||||
return http_response{ 404, "" };
|
||||
});
|
||||
|
||||
auto hub_connection = hub_connection_impl::create(base_url, trace_level::none,
|
||||
std::make_shared<trace_log_writer>(), std::move(http_client),
|
||||
std::make_unique<test_transport_factory>(create_test_websocket_client()));
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
catch (const std::exception&) {}
|
||||
|
||||
ASSERT_EQ("http://fakeuri/negotiate", requested_url);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(start, start_starts_connection)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
ASSERT_EQ(connection_state::connected, hub_connection->get_connection_state());
|
||||
}
|
||||
|
||||
TEST(start, start_sends_handshake)
|
||||
{
|
||||
auto message = std::make_shared<std::string>();
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */ [message](const std::string& msg, std::function<void(std::exception_ptr)> callback) { *message = msg; callback(nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
ASSERT_EQ("{\"protocol\":\"json\",\"version\":1}\x1e", *message);
|
||||
|
||||
ASSERT_EQ(connection_state::connected, hub_connection->get_connection_state());
|
||||
}
|
||||
|
||||
TEST(start, start_waits_for_handshake_response)
|
||||
{
|
||||
pplx::task_completion_event<void> tce;
|
||||
pplx::task_completion_event<void> tceWaitForSend;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [tce, tceWaitForSend](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
tceWaitForSend.set();
|
||||
pplx::task<void>(tce).get();
|
||||
callback("{ }\x1e", nullptr);
|
||||
});
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
auto done = false;
|
||||
hub_connection->start([&mre, &done](std::exception_ptr exception)
|
||||
{
|
||||
done = true;
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
pplx::task<void>(tceWaitForSend).get();
|
||||
ASSERT_FALSE(done);
|
||||
tce.set();
|
||||
mre.get();
|
||||
|
||||
ASSERT_EQ(connection_state::connected, hub_connection->get_connection_state());
|
||||
}
|
||||
|
||||
TEST(start, start_fails_for_handshake_response_with_error)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{\"error\":\"bad things\"}\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
ASSERT_STREQ("Received an error during handshake: bad things", ex.what());
|
||||
}
|
||||
|
||||
ASSERT_EQ(connection_state::disconnected, hub_connection->get_connection_state());
|
||||
}
|
||||
|
||||
TEST(start, start_fails_if_stop_called_before_handshake_response)
|
||||
{
|
||||
pplx::task_completion_event<std::string> tce;
|
||||
pplx::task_completion_event<void> tceWaitForSend;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [tce](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
auto str = pplx::task<std::string>(tce).get();
|
||||
callback(str, nullptr);
|
||||
},
|
||||
/* send function */ [tceWaitForSend](const std::string &, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
tceWaitForSend.set();
|
||||
callback(nullptr);
|
||||
});
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
pplx::task<void>(tceWaitForSend).get();
|
||||
hub_connection->stop([](std::exception_ptr) {});
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
catch (std::exception ex)
|
||||
{
|
||||
ASSERT_STREQ("connection closed while handshake was in progress.", ex.what());
|
||||
}
|
||||
|
||||
ASSERT_EQ(connection_state::disconnected, hub_connection->get_connection_state());
|
||||
}
|
||||
|
||||
TEST(stop, stop_stops_connection)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->stop([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
ASSERT_EQ(connection_state::disconnected, hub_connection->get_connection_state());
|
||||
}
|
||||
|
||||
TEST(stop, disconnected_callback_called_when_hub_connection_stops)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto disconnected_invoked = false;
|
||||
hub_connection->set_disconnected([&disconnected_invoked]() { disconnected_invoked = true; });
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->stop([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
ASSERT_TRUE(disconnected_invoked);
|
||||
}
|
||||
|
||||
TEST(stop, connection_stopped_when_going_out_of_scope)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client, writer, trace_level::state_changes);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
}
|
||||
|
||||
auto memory_writer = std::dynamic_pointer_cast<memory_log_writer>(writer);
|
||||
|
||||
// The underlying connection_impl will be destroyed when the last reference to shared_ptr holding is released. This can happen
|
||||
// on a different thread in which case the dtor will be invoked on a different thread so we need to wait for this
|
||||
// to happen and if it does not the test will fail. There is nothing we can block on.
|
||||
for (int wait_time_ms = 5; wait_time_ms < 100 && memory_writer->get_log_entries().size() < 4; wait_time_ms <<= 1)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(wait_time_ms));
|
||||
}
|
||||
|
||||
auto log_entries = memory_writer->get_log_entries();
|
||||
ASSERT_EQ(4U, log_entries.size()) << dump_vector(log_entries);
|
||||
ASSERT_EQ("[state change] disconnected -> connecting\n", remove_date_from_log_entry(log_entries[0]));
|
||||
ASSERT_EQ("[state change] connecting -> connected\n", remove_date_from_log_entry(log_entries[1]));
|
||||
ASSERT_EQ("[state change] connected -> disconnecting\n", remove_date_from_log_entry(log_entries[2]));
|
||||
ASSERT_EQ("[state change] disconnecting -> disconnected\n", remove_date_from_log_entry(log_entries[3]));
|
||||
}
|
||||
|
||||
TEST(stop, stop_cancels_pending_callbacks)
|
||||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{}"
|
||||
};
|
||||
|
||||
if (call_number < 1)
|
||||
{
|
||||
call_number++;
|
||||
}
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
auto invoke_mre = manual_reset_event<void>();
|
||||
hub_connection->invoke("method", json::value::array(), [&invoke_mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
invoke_mre.set(exception);
|
||||
});
|
||||
|
||||
hub_connection->stop([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
try
|
||||
{
|
||||
invoke_mre.get();
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const signalr_exception& e)
|
||||
{
|
||||
ASSERT_STREQ("\"connection was stopped before invocation result was received\"", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(stop, pending_callbacks_finished_if_hub_connections_goes_out_of_scope)
|
||||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{}"
|
||||
};
|
||||
|
||||
if (call_number < 1)
|
||||
{
|
||||
call_number++;
|
||||
}
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto invoke_mre = manual_reset_event<void>();
|
||||
|
||||
{
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->invoke("method", json::value::array(), [&invoke_mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
invoke_mre.set(exception);
|
||||
});
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
invoke_mre.get();
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const signalr_exception& e)
|
||||
{
|
||||
ASSERT_STREQ("\"connection went out of scope before invocation result was received\"", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(hub_invocation, hub_connection_invokes_users_code_on_hub_invocations)
|
||||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{ \"type\": 1, \"target\": \"BROADcast\", \"arguments\": [ \"message\", 1 ] }\x1e"
|
||||
};
|
||||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto payload = std::make_shared<std::string>();
|
||||
auto on_broadcast_event = std::make_shared<event>();
|
||||
hub_connection->on("broadCAST", [on_broadcast_event, payload](const json::value& message)
|
||||
{
|
||||
*payload = utility::conversions::to_utf8string(message.serialize());
|
||||
on_broadcast_event->set();
|
||||
});
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
ASSERT_FALSE(on_broadcast_event->wait(5000));
|
||||
|
||||
ASSERT_EQ("[\"message\",1]", *payload);
|
||||
}
|
||||
|
||||
TEST(send, creates_correct_payload)
|
||||
{
|
||||
std::string payload;
|
||||
bool handshakeReceived = false;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */[&payload, &handshakeReceived](const std::string& m, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
if (handshakeReceived)
|
||||
{
|
||||
payload = m;
|
||||
callback(nullptr);
|
||||
return;
|
||||
}
|
||||
handshakeReceived = true;
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->send("method", json::value::array(), [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
ASSERT_EQ("{\"arguments\":[],\"target\":\"method\",\"type\":1}\x1e", payload);
|
||||
}
|
||||
|
||||
TEST(send, does_not_wait_for_server_response)
|
||||
{
|
||||
int call_number = -1;
|
||||
pplx::task_completion_event<void> waitForSend;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [waitForSend, call_number](std::function<void(std::string, std::exception_ptr)> callback) mutable
|
||||
{
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{}"
|
||||
};
|
||||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
if (call_number == 1)
|
||||
{
|
||||
pplx::task<void>(waitForSend).get();
|
||||
}
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
// wont block waiting for server response
|
||||
hub_connection->send("method", json::value::array(), [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
mre.get();
|
||||
waitForSend.set();
|
||||
}
|
||||
|
||||
TEST(invoke, creates_correct_payload)
|
||||
{
|
||||
std::string payload;
|
||||
bool handshakeReceived = false;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */[&payload, &handshakeReceived](const std::string& m, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
if (handshakeReceived)
|
||||
{
|
||||
payload = m;
|
||||
callback(std::make_exception_ptr(std::runtime_error("error")));
|
||||
return;
|
||||
}
|
||||
handshakeReceived = true;
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// the invoke is not setup to succeed because it's not needed in this test
|
||||
}
|
||||
|
||||
ASSERT_EQ("{\"arguments\":[],\"invocationId\":\"0\",\"target\":\"method\",\"type\":1}\x1e", payload);
|
||||
}
|
||||
|
||||
TEST(invoke, callback_not_called_if_send_throws)
|
||||
{
|
||||
bool handshakeReceived = false;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */[handshakeReceived](const std::string&, std::function<void(std::exception_ptr)> callback) mutable
|
||||
{
|
||||
if (handshakeReceived)
|
||||
{
|
||||
callback(std::make_exception_ptr(std::runtime_error("error")));
|
||||
return;
|
||||
}
|
||||
handshakeReceived = true;
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
ASSERT_STREQ("error", e.what());
|
||||
}
|
||||
|
||||
// stop completes all outstanding callbacks so if we did not remove a callback when `invoke_void` failed an
|
||||
// unobserved exception exception would be thrown. Note that this would happen on a different thread and would
|
||||
// crash the process
|
||||
hub_connection->stop([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
}
|
||||
|
||||
TEST(invoke, invoke_returns_value_returned_from_the_server)
|
||||
{
|
||||
auto callback_registered_event = std::make_shared<event>();
|
||||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, callback_registered_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{ \"type\": 3, \"invocationId\": \"0\", \"result\": \"abc\" }\x1e"
|
||||
};
|
||||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
if (call_number > 0)
|
||||
{
|
||||
callback_registered_event->wait();
|
||||
}
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
auto invoke_mre = manual_reset_event<json::value>();
|
||||
hub_connection->invoke("method", json::value::array(), [&invoke_mre](const json::value& message, std::exception_ptr exception)
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
invoke_mre.set(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
invoke_mre.set(message);
|
||||
}
|
||||
});
|
||||
|
||||
callback_registered_event->set();
|
||||
|
||||
auto result = invoke_mre.get();
|
||||
|
||||
ASSERT_EQ(_XPLATSTR("\"abc\""), result.serialize());
|
||||
}
|
||||
|
||||
TEST(invoke, invoke_propagates_errors_from_server_as_hub_exceptions)
|
||||
{
|
||||
auto callback_registered_event = std::make_shared<event>();
|
||||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, callback_registered_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{ \"type\": 3, \"invocationId\": \"0\", \"error\": \"Ooops\" }\x1e"
|
||||
};
|
||||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
if (call_number > 0)
|
||||
{
|
||||
callback_registered_event->wait();
|
||||
}
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
callback_registered_event->set();
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const hub_exception& e)
|
||||
{
|
||||
ASSERT_STREQ("\"Ooops\"", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(invoke, unblocks_task_when_server_completes_call)
|
||||
{
|
||||
auto callback_registered_event = std::make_shared<event>();
|
||||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, callback_registered_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{ \"type\": 3, \"invocationId\": \"0\" }\x1e"
|
||||
};
|
||||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
if (call_number > 0)
|
||||
{
|
||||
callback_registered_event->wait();
|
||||
}
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
callback_registered_event->set();
|
||||
|
||||
mre.get();
|
||||
|
||||
// should not block
|
||||
ASSERT_TRUE(true);
|
||||
}
|
||||
|
||||
TEST(receive, logs_if_callback_for_given_id_not_found)
|
||||
{
|
||||
auto message_received_event = std::make_shared<event>();
|
||||
auto handshake_sent = std::make_shared<event>();
|
||||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, message_received_event, handshake_sent](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{ \"type\": 3, \"invocationId\": \"0\" }\x1e",
|
||||
"{}"
|
||||
};
|
||||
|
||||
handshake_sent->wait(1000);
|
||||
|
||||
call_number = std::min(call_number + 1, 2);
|
||||
|
||||
if (call_number > 1)
|
||||
{
|
||||
message_received_event->set();
|
||||
}
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
},
|
||||
[handshake_sent](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
handshake_sent->set();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
auto hub_connection = create_hub_connection(websocket_client, writer, trace_level::info);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
ASSERT_FALSE(message_received_event->wait(5000));
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
ASSERT_TRUE(log_entries.size() > 1);
|
||||
|
||||
auto entry = remove_date_from_log_entry(log_entries[2]);
|
||||
ASSERT_EQ("[info ] no callback found for id: 0\n", entry) << dump_vector(log_entries);
|
||||
}
|
||||
|
||||
TEST(invoke_void, invoke_creates_runtime_error)
|
||||
{
|
||||
auto callback_registered_event = std::make_shared<event>();
|
||||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, callback_registered_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
"{ }\x1e",
|
||||
"{ \"type\": 3, \"invocationId\": \"0\", \"error\": \"Ooops\" }\x1e"
|
||||
};
|
||||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
if (call_number > 0)
|
||||
{
|
||||
callback_registered_event->wait();
|
||||
}
|
||||
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
callback_registered_event->set();
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const hub_exception & e)
|
||||
{
|
||||
ASSERT_STREQ("\"Ooops\"", e.what());
|
||||
ASSERT_FALSE(callback_registered_event->wait(0));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(connection_id, can_get_connection_id)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
ASSERT_EQ("", hub_connection->get_connection_id());
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
auto connection_id = hub_connection->get_connection_id();
|
||||
|
||||
hub_connection->stop([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", connection_id);
|
||||
ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", hub_connection->get_connection_id());
|
||||
}
|
||||
|
||||
TEST(on, event_name_must_not_be_empty_string)
|
||||
{
|
||||
auto hub_connection = create_hub_connection();
|
||||
try
|
||||
{
|
||||
hub_connection->on("", [](const json::value&) {});
|
||||
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
ASSERT_STREQ("event_name cannot be empty", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(on, cannot_register_multiple_handlers_for_event)
|
||||
{
|
||||
auto hub_connection = create_hub_connection();
|
||||
hub_connection->on("ping", [](const json::value&) {});
|
||||
|
||||
try
|
||||
{
|
||||
hub_connection->on("ping", [](const json::value&) {});
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const signalr_exception& e)
|
||||
{
|
||||
ASSERT_STREQ("an action for this event has already been registered. event name: ping", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(on, cannot_register_handler_if_connection_not_in_disconnected_state)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->start([&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
hub_connection->on("myfunc", [](const web::json::value&) {});
|
||||
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const signalr_exception& e)
|
||||
{
|
||||
ASSERT_STREQ("can't register a handler if the connection is in a disconnected state", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(invoke, invoke_throws_when_the_underlying_connection_is_not_valid)
|
||||
{
|
||||
auto hub_connection = create_hub_connection();
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const signalr_exception& e)
|
||||
{
|
||||
ASSERT_STREQ("cannot send data when the connection is not in the connected state. current connection state: disconnected", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(invoke, send_throws_when_the_underlying_connection_is_not_valid)
|
||||
{
|
||||
auto hub_connection = create_hub_connection();
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
ASSERT_TRUE(false); // exception expected but not thrown
|
||||
}
|
||||
catch (const signalr_exception& e)
|
||||
{
|
||||
ASSERT_STREQ("cannot send data when the connection is not in the connected state. current connection state: disconnected", e.what());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "signalrclient/hub_exception.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
TEST(hub_exception_initialization, hub_exception_initialized_correctly)
|
||||
{
|
||||
auto e = hub_exception{ "error" };
|
||||
|
||||
ASSERT_STREQ("error", e.what());
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "test_utils.h"
|
||||
#include "cpprest/asyncrt_utils.h"
|
||||
#include "trace_log_writer.h"
|
||||
#include "logger.h"
|
||||
#include "memory_log_writer.h"
|
||||
|
||||
using namespace signalr;
|
||||
TEST(logger_write, entry_added_if_trace_level_set)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
logger l(writer, trace_level::messages);
|
||||
l.log(trace_level::messages, "message");
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
ASSERT_EQ(1U, log_entries.size());
|
||||
}
|
||||
|
||||
TEST(logger_write, entry_not_added_if_trace_level_not_set)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
logger l(writer, trace_level::messages);
|
||||
l.log(trace_level::events, "event");
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
ASSERT_TRUE(log_entries.empty());
|
||||
}
|
||||
|
||||
TEST(logger_write, entries_added_for_combined_trace_level)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
logger l(writer, trace_level::messages | trace_level::state_changes | trace_level::events | trace_level::errors | trace_level::info);
|
||||
l.log(trace_level::messages, "message");
|
||||
l.log(trace_level::events, "event");
|
||||
l.log(trace_level::state_changes, "state_change");
|
||||
l.log(trace_level::errors, "error");
|
||||
l.log(trace_level::info, "info");
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
ASSERT_EQ(5U, log_entries.size());
|
||||
}
|
||||
|
||||
TEST(logger_write, entries_formatted_correctly)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
logger l(writer, trace_level::all);
|
||||
l.log(trace_level::messages, "message");
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
ASSERT_FALSE(log_entries.empty());
|
||||
|
||||
auto entry = log_entries[0];
|
||||
|
||||
auto date_str = entry.substr(0, entry.find_first_of("Z") + 1);
|
||||
auto date = utility::datetime::from_string(utility::conversions::to_string_t(date_str), utility::datetime::ISO_8601);
|
||||
ASSERT_EQ(date_str, utility::conversions::to_utf8string(date.to_string(utility::datetime::ISO_8601)));
|
||||
|
||||
ASSERT_EQ("[message ] message\n", remove_date_from_log_entry(entry));
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in t
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "memory_log_writer.h"
|
||||
|
||||
void memory_log_writer::write(const std::string& entry)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_entries_lock);
|
||||
m_log_entries.push_back(entry);
|
||||
}
|
||||
|
||||
std::vector<std::string> memory_log_writer::get_log_entries()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_entries_lock);
|
||||
return m_log_entries;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include "signalrclient/log_writer.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
class memory_log_writer : public log_writer
|
||||
{
|
||||
public:
|
||||
void __cdecl write(const std::string &entry) override;
|
||||
std::vector<std::string> get_log_entries();
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_log_entries;
|
||||
std::mutex m_entries_lock;
|
||||
};
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "negotiate.h"
|
||||
#include "test_http_client.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
TEST(negotiate, request_created_with_correct_url)
|
||||
{
|
||||
std::string requested_url;
|
||||
auto http_client = test_http_client([&requested_url](const std::string& url, http_request request)
|
||||
{
|
||||
std::string response_body(
|
||||
"{ \"connectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", "
|
||||
"\"availableTransports\" : [] }");
|
||||
|
||||
requested_url = url;
|
||||
return http_response(200, response_body);
|
||||
});
|
||||
|
||||
negotiate::negotiate(http_client, "http://fake/signalr").get();
|
||||
|
||||
ASSERT_EQ("http://fake/signalr/negotiate", requested_url);
|
||||
}
|
||||
|
||||
TEST(negotiate, negotiation_request_sent_and_response_serialized)
|
||||
{
|
||||
auto request_factory = test_http_client([](const std::string&, http_request request)
|
||||
{
|
||||
std::string response_body(
|
||||
"{\"connectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", "
|
||||
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] },"
|
||||
"{ \"transport\": \"ServerSentEvents\", \"transferFormats\": [ \"Text\" ] } ] }");
|
||||
|
||||
return http_response(200, response_body);
|
||||
});
|
||||
|
||||
auto response = negotiate::negotiate(request_factory, "http://fake/signalr").get();
|
||||
|
||||
ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", response.connectionId);
|
||||
ASSERT_EQ(2u, response.availableTransports.size());
|
||||
ASSERT_EQ(2u, response.availableTransports[0].transfer_formats.size());
|
||||
ASSERT_EQ("Text", response.availableTransports[0].transfer_formats[0]);
|
||||
ASSERT_EQ("Binary", response.availableTransports[0].transfer_formats[1]);
|
||||
ASSERT_EQ(1u, response.availableTransports[1].transfer_formats.size());
|
||||
ASSERT_EQ("Text", response.availableTransports[1].transfer_formats[0]);
|
||||
}
|
||||
|
||||
TEST(negotiate, negotiation_response_with_redirect)
|
||||
{
|
||||
auto request_factory = test_http_client([](const std::string&, http_request request)
|
||||
{
|
||||
std::string response_body(
|
||||
"{\"url\" : \"http://redirect\", "
|
||||
"\"accessToken\" : \"secret\" }");
|
||||
|
||||
return http_response(200, response_body);
|
||||
});
|
||||
|
||||
auto response = negotiate::negotiate(request_factory, "http://fake/signalr").get();
|
||||
|
||||
ASSERT_EQ("http://redirect", response.url);
|
||||
ASSERT_EQ("secret", response.accessToken);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
int wmain(int argc, wchar_t* argv[])
|
||||
#else
|
||||
int main(int argc, char* argv[])
|
||||
#endif
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
// prevents from defining min/max macros that conflict with std::min()/std::max() functions
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "../../src/signalrclient/event.h"
|
||||
#include "../../src/signalrclient/make_unique.h"
|
||||
#include <thread>
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SDKDDKVer.h>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче