This commit is contained in:
BrennanConroy 2019-04-02 12:14:15 -07:00 коммит произвёл GitHub
Родитель b946468a0d
Коммит 4d26f135c9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
115 изменённых файлов: 9436 добавлений и 0 удалений

4
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
packages/
Debug/
Release/
.vs/

6
.gitmodules поставляемый Normal file
Просмотреть файл

@ -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

19
CMakeLists.txt Normal file
Просмотреть файл

@ -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>

13
build/Version.props Normal file
Просмотреть файл

@ -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 &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\dll\$(SignalrClientTargetName).dll&quot; &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\$(SignalrClientTargetName).dll&quot;" />
<Exec Command="copy /y &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\dll\$(SignalrClientTargetName).pdb&quot; &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\$(SignalrClientTargetName).pdb&quot;" />
</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

112
signalrclient.sln Normal file
Просмотреть файл

@ -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;
};
}

63
src/signalrclient/event.h Normal file
Просмотреть файл

@ -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 &quot;$(SignalRClientCppVersionMajor).$(SignalRClientCppVersionMinor).$(SignalRClientCppVersionPatch).$(Revision)\0&quot;" />
<VersionHeaderContents Include="#define ProductVersion $(SignalRClientCppVersionMajor),$(SignalRClientCppVersionMinor),$(SignalRClientCppVersionPatch),$(Revision)" />
<VersionHeaderContents Include="#define ProductVersionStr &quot;$(SignalRClientCppVersionString)\0&quot;" />
<VersionHeaderContents Include="#define PlatformToolset &quot;$(PlatformToolset)\0&quot;" />
</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>

Двоичные данные
src/signalrclientdll/Resource.rc Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -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"

1
submodules/googletest Submodule

@ -0,0 +1 @@
Subproject commit 4e4df226fc197c0dda6e37f5c8c3845ca1e73a49

1
submodules/vcpkg Submodule

@ -0,0 +1 @@
Subproject commit d1110ceb7feb527ac58f274239d435e32dca4b84

5
test/CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,5 @@
add_subdirectory (gtest-1.7.0)
enable_testing()
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
add_subdirectory (signalrclienttests)

171
test/gtest/gtest.vcxproj Normal file
Просмотреть файл

@ -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, &parameter_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>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше