This commit is contained in:
moozzyk 2016-07-18 13:59:04 -07:00
Родитель 58a65fab10 9cd0db8ae2
Коммит b196f43231
343 изменённых файлов: 168643 добавлений и 4 удалений

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

@ -0,0 +1 @@
*.rc diff

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

@ -12,4 +12,12 @@ packages/*
Debug/
Release/
ipch/
*.vcxproj.user
*.vcxproj.user
*.exe
artifacts/
*.aps
src/signalrclientdll/version.h
/.vs/
build.*
signalrclient.VC.VC.opendb
signalrclient.VC.db

6
.nuget/NuGet.Config Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
</configuration>

144
.nuget/NuGet.targets Normal file
Просмотреть файл

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
<!--
<PackageSource Include="https://www.nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
</PropertyGroup>
<PropertyGroup>
<PackagesProjectConfig Condition=" '$(OS)' == 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config</PackagesProjectConfig>
<PackagesProjectConfig Condition=" '$(OS)' != 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config</PackagesProjectConfig>
</PropertyGroup>
<PropertyGroup>
<PackagesConfig Condition="Exists('$(MSBuildProjectDirectory)\packages.config')">$(MSBuildProjectDirectory)\packages.config</PackagesConfig>
<PackagesConfig Condition="Exists('$(PackagesProjectConfig)')">$(PackagesProjectConfig)</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(NuGetExePath)"</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

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

@ -0,0 +1,15 @@
<?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=" '$(VisualStudioVersion)' == '12.0'">v120</PlatformToolset>
<PlatformToolset Condition=" '$(VisualStudioVersion)' == '14.0'">v140</PlatformToolset>
<PlatformToolset Condition=" '$(PlatformToolset)' == ''">v120</PlatformToolset>
<OutputPath Condition="'$(OutputPath)' == ''">$(SolutionDir)bin\$(Platform)\$(Configuration)\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
<SignalrClientTargetName>signalrclient</SignalrClientTargetName>
</PropertyGroup>
</Project>

82
Build/Common.tasks Normal file
Просмотреть файл

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="ExecAsync" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
<ParameterGroup>
<Executable ParameterType="System.String" Required="true" />
<Arguments ParameterType="System.String" Required="false" />
</ParameterGroup>
<Task>
<Using Namespace="System.IO" />
<Using Namespace="System.Diagnostics" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Log.LogMessage("Executable {0}...", Executable);
var name = System.IO.Path.GetFileNameWithoutExtension(Executable);
Log.LogMessage("Starting {0}...", name);
var processStartInfo = new ProcessStartInfo(Executable, Arguments) { UseShellExecute = true };
Process.Start(processStartInfo);
Log.LogMessage("Finished starting process {0}.", name);
]]>
</Code>
</Task>
</UsingTask>
<UsingTask TaskName="Sleep" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
<ParameterGroup>
<TimeoutMs ParameterType="System.Int32" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[System.Threading.Thread.Sleep(TimeoutMs);]]>
</Code>
</Task>
</UsingTask>
<UsingTask TaskName="ZipDir" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
<ParameterGroup>
<InputDir ParameterType="System.String" Required="true" />
<OutputFileName ParameterType="System.String" Required="true" />
<IncludeBaseDir ParameterType="System.Boolean" Required="false" />
</ParameterGroup>
<Task>
<Reference Include="System.IO.Compression.FileSystem" />
<Using Namespace="System.IO.Compression" />
<Code Type="Fragment" Language="cs">
<![CDATA[ ZipFile.CreateFromDirectory(InputDir, OutputFileName, CompressionLevel.Optimal, IncludeBaseDir); ]]>
</Code>
</Task>
</UsingTask>
<UsingTask TaskName="RegexReplaceInFile" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
<ParameterGroup>
<InputFilename ParameterType="System.String" Required="true" />
<OutputFilename ParameterType="System.String" Required="true" />
<Pattern ParameterType="System.String" Required="true" />
<Replacement ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System.IO" />
<Using Namespace="System.Linq" />
<Using Namespace="System.Text.RegularExpressions" />
<Code Type="Fragment" Language="cs">
<![CDATA[
string contents;
using (var input = new StreamReader(InputFilename))
{
contents = input.ReadToEnd();
}
contents = new Regex(Pattern, RegexOptions.Compiled | RegexOptions.Multiline)
.Replace(contents, Replacement);
using (var output = new StreamWriter(OutputFilename))
{
output.Write(contents);
}
]]>
</Code>
</Task>
</UsingTask>
</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,81 @@
<?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>
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError Condition="'$(TreatWarningsAsErrors)' != ''">true</TreatWarningAsError>
<SDLCheck>true</SDLCheck>
<StringPooling>true</StringPooling>
</ClCompile>
<Link>
<SubSystem>Windows</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>-beta1</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>

123
Build/build.msbuild Normal file
Просмотреть файл

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildThisFileDirectory)\Common.tasks" />
<Import Project="$(MSBuildThisFileDirectory)\Common.Build.Settings" />
<ItemGroup>
<Projects Include="$(SolutionDir)src\signalrclientdll\Build\VS\signalrclientdll.vcxproj" />
</ItemGroup>
<ItemGroup>
<TestProjects Include="$(SolutionDir)src\signalrclient\Build\VS\signalrclient.vcxproj" />
<TestProjects Include="$(SolutionDir)test\signalrclienttests\Build\VS\signalrclienttests.vcxproj" />
<TestProjects Include="$(SolutionDir)test\signalrclient-e2e-tests\Build\VS\signalrclient-e2e-tests.vcxproj" />
</ItemGroup>
<ItemGroup>
<ManagedProjects Include="$(SolutionDir)test\signalrclient-testhost\signalrclient-testhost.csproj" />
<!--
<ManagedProjects Include="$(SolutionDir)\samples\SignalRServer\SignalRServer.csproj" />
-->
</ItemGroup>
<ItemGroup>
<!--
<SampleProjects Include="$(SolutionDir)\samples\PersistentConnectionSample\PersistentConnectionSample.vcxproj" />
<SampleProjects Include="$(SolutionDir)\samples\HubConnectionSample\HubConnectionSample.vcxproj" />
-->
</ItemGroup>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<Target Name="Build">
<MSBuild Targets="RestorePackages" Projects="@(Projects)" />
<MSBuild Targets="RestorePackages" Projects="@(SampleProjects)" />
<MSBuild Targets="RestorePackages" Projects="@(ManagedProjects)" />
<MSBuild Targets="$(BuildTargets)"
Projects="@(Projects)"
Properties="Configuration=$(Configuration);Platform=$(Platform);PlatformToolset=$(PlatformToolset)" />
<MSBuild Targets="$(BuildTargets)"
Projects="@(TestProjects)"
Properties="Configuration=$(Configuration);Platform=$(Platform);PlatformToolset=$(PlatformToolset)" />
<MSBuild Targets="$(BuildTargets)"
Projects="@(SampleProjects)"
Properties="Configuration=$(Configuration);Platform=$(Platform);PlatformToolset=$(PlatformToolset)" />
<MSBuild Targets="$(BuildTargets)"
Projects="@(ManagedProjects)"
Properties="Configuration=$(Configuration)" />
</Target>
<Target Name="Clean">
<MSBuild Targets="Clean"
Projects="@(Projects)" />
<MSBuild Targets="Clean"
Projects="@(TestProjects)" />
<MSBuild Targets="Clean"
Projects="@(SampleProjects)" />
<MSBuild Targets="Clean"
Projects="@(ManagedProjects)" />
</Target>
<Target Name="Rebuild">
<MSBuild Targets="Clean;Build"
Projects="$(MSBuildProjectFile)"
Properties="BuildTargets=Rebuild;Configuration=$(Configuration);Platform=$(Platform);PlatformToolset=$(PlatformToolset)"/>
</Target>
<Target Name="Test" DependsOnTargets="Build">
<Exec Command="$(OutDir)\signalrclienttests.exe --gtest_output=xml:$(OutDir)test_results.xml" />
<ExecAsync Executable="$(SolutionDir)test\signalrclient-testhost\bin\$(Configuration)\signalrclient-testhost.exe" Arguments="60000" />
<!-- give the host some time to open the port otherwise the first tests may fail on slow machines-->
<Sleep TimeoutMs="3000" />
<Exec Command="$(OutDir)\signalrclient-e2e-tests.exe --gtest_output=xml:$(OutDir)e2e_test_results.xml" />
<Exec Command="taskkill /IM signalrclient-testhost.exe /F" ContinueOnError="true" />
</Target>
<Import Project="Config.Definitions.Props" />
<Target Name="CreatePackage">
<PropertyGroup>
<PlatformToolset Condition="'$(PlatformToolset)' == ''">v120</PlatformToolset>
<PackageSource>$(SolutionDir)bin\Package\$(PlatformToolset)\</PackageSource>
<PackageSourceNative>$(PackageSource)build\native\</PackageSourceNative>
<NuGetArtifactsPath>$(MSBuildThisFileDirectory)..\NuGet\</NuGetArtifactsPath>
<NuSpecTemplatePath>$(NuGetArtifactsPath)signalrclientcpp.nuspec.template</NuSpecTemplatePath>
<PackageOutputDir>$(SolutionDir)artifacts\build</PackageOutputDir>
<PrivateSymbols>$(SolutionDir)bin\Symbols\$(PlatformToolset)\</PrivateSymbols>
</PropertyGroup>
<ItemGroup>
<Include Include="$(SolutionDir)include\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(Include)" DestinationFolder="$(PackageSourceNative)include\%(RecursiveDir)" />
<MakeDir Directories="$(PackageSource)" />
<RegexReplaceInFile InputFileName="$(NuSpecTemplatePath)" OutputFileName="$(PackageSource)signalrclientcpp.nuspec" Pattern="#Toolset#" Replacement="$(PlatformToolset)" />
<RegexReplaceInFile InputFileName="$(NuGetArtifactsPath)\Microsoft.AspNet.SignalR.Client.Cpp.WinDesktop.targets.template"
OutputFileName="$(PackageSourceNative)Microsoft.AspNet.SignalR.Client.Cpp.$(PlatformToolset).WinDesktop.targets" Pattern="#Toolset#" Replacement="$(PlatformToolset)" />
<MSBuild Targets="RestorePackages" Projects="@(Projects)" />
<MSBuild Targets="BuildForNuget"
Projects="$(MSBuildThisFile)" Properties="PackageSourceNative=$(PackageSourceNative);PrivateSymbols=$(PrivateSymbols);Configuration=%(ProjectConfiguration.Configuration);Platform=%(ProjectConfiguration.Platform);PlatformToolset=$(PlatformToolset);SignalrClientTargetName=$(SignalrClientTargetName)" />
<MakeDir Directories="$(PackageOutputDir)" />
<Exec Command="$(NuGetCommand) pack $(PackageSource)signalrclientcpp.nuspec -BasePath $(PackageSource) -OutputDirectory $(PackageOutputDir) -Version $(SignalRClientCppVersionString)" LogStandardErrorAsError="true" />
<ZipDir InputDir="$(PrivateSymbols)" OutputFileName="$(PackageOutputDir)\Symbols_$(PlatformToolset).zip" IncludeBaseDir="false"/>
</Target>
<Target Name="BuildForNuget">
<MSBuild Targets="$(BuildTargets)"
Projects="@(Projects)"
Properties="Configuration=$(Configuration);Platform=$(Platform);PlatformToolset=$(PlatformToolset)" />
<Copy SourceFiles="$(OutputPath)dll\$(SignalrClientTargetName).lib" DestinationFolder="$(PackageSourceNative)lib\$(Platform)\$(PlatformToolset)\$(Configuration)" />
<Copy SourceFiles="$(OutputPath)dll\$(SignalrClientTargetName).dll" DestinationFolder="$(PackageSourceNative)dll\$(Platform)\$(PlatformToolset)\$(Configuration)" />
<Copy SourceFiles="$(OutputPath)dll\$(SignalrClientTargetName).pub.pdb" DestinationFiles="$(PackageSourceNative)dll\$(Platform)\$(PlatformToolset)\$(Configuration)\$(SignalrClientTargetName).pdb" />
<Copy SourceFiles="$(OutputPath)dll\$(SignalrClientTargetName).pdb" DestinationFiles="$(PrivateSymbols)$(Platform)\$(PlatformToolset)\$(Configuration)\$(SignalrClientTargetName).pdb" />
</Target>
</Project>

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)

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

@ -1,5 +1,4 @@
Contributing
======
Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo.
Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/dev/CONTRIBUTING.md) in the Home repo.

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

@ -1,4 +1,4 @@
Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
Copyright (c) .NET Foundation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
these files except in compliance with the License. You may obtain a copy of the

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

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" InitialTargets="signalrclient_inittarget">
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ResourceCompile>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Label="x64 and #Toolset# and Release" Condition="'$(Platform.ToLower())' == 'x64' And '$(PlatformToolset.ToLower())' == '#Toolset#' And $(Configuration.ToLower().IndexOf('debug')) == -1">
<Link>
<AdditionalDependencies>$(MSBuildThisFileDirectory)lib\x64\#Toolset#\Release\signalrclient.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Label="x64 and #Toolset# and Debug" Condition="'$(Platform.ToLower())' == 'x64' And '$(PlatformToolset.ToLower())' == '#Toolset#'">
<Link>
<AdditionalDependencies>$(MSBuildThisFileDirectory)lib\x64\#Toolset#\Debug\signalrclient.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Label="Win32 and #Toolset# and Release" Condition="'$(Platform.ToLower())' == 'Win32' And '$(PlatformToolset.ToLower())' == '#Toolset#' And $(Configuration.ToLower().IndexOf('debug')) == -1">
<Link>
<AdditionalDependencies>$(MSBuildThisFileDirectory)lib\Win32\#Toolset#\Release\signalrclient.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Label="Win32 and #Toolset# and Debug" Condition="'$(Platform.ToLower())' == 'Win32' And '$(PlatformToolset.ToLower())' == '#Toolset#'">
<Link>
<AdditionalDependencies>$(MSBuildThisFileDirectory)lib\Win32\#Toolset#\Debug\signalrclient.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Target Name="signalrclient_AfterBuild" AfterTargets="AfterBuild">
<Copy DestinationFolder="$(TargetDir)" SourceFiles="@(CopyToOutput)" SkipUnchangedFiles="true" UseHardlinksIfPossible="true">
<Output TaskParameter="DestinationFiles" PropertyName="DestinationFiles" />
<Output TaskParameter="DestinationFiles" ItemName="DestinationFiles" />
<Output TaskParameter="CopiedFiles" PropertyName="CopiedFiles" />
<Output TaskParameter="CopiedFiles" ItemName="CopiedFiles" />
</Copy>
</Target>
<Target Name="signalrclient_inittarget">
<ItemGroup Label="x64 and #Toolset# and Debug" Condition="'$(Platform.ToLower())' == 'x64' And '$(PlatformToolset.ToLower())' == '#Toolset#' And $(Configuration.ToLower().IndexOf('debug')) &gt; -1">
<CopyToOutput Include="$(MSBuildThisFileDirectory)dll\x64\#Toolset#\Debug\signalrclient.dll" />
<CopyToOutput Include="$(MSBuildThisFileDirectory)dll\x64\#Toolset#\Debug\signalrclient.pdb" />
<None Include="$(MSBuildThisFileDirectory)dll\x64\#Toolset#\Debug\signalrclient.dll">
<DeploymentContent>true</DeploymentContent>
</None>
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)dll\x64\#Toolset#\Debug\signalrclient.dll" />
</ItemGroup>
<ItemGroup Label="x64 and #Toolset# and Release" Condition="'$(Platform.ToLower())' == 'x64' And '$(PlatformToolset.ToLower())' == '#Toolset#' And $(Configuration.ToLower().IndexOf('debug')) == -1">
<CopyToOutput Include="$(MSBuildThisFileDirectory)dll\x64\#Toolset#\Release\signalrclient.dll" />
<CopyToOutput Include="$(MSBuildThisFileDirectory)dll\x64\#Toolset#\Release\signalrclient.pdb" />
<None Include="$(MSBuildThisFileDirectory)dll\x64\#Toolset#\Release\signalrclient.dll">
<DeploymentContent>true</DeploymentContent>
</None>
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)dll\x64\#Toolset#\Debug\signalrclient.dll" />
</ItemGroup>
<ItemGroup Label="Win32 and #Toolset# and Debug" Condition="'$(Platform.ToLower())' == 'Win32' And '$(PlatformToolset.ToLower())' == '#Toolset#' And $(Configuration.ToLower().IndexOf('debug')) &gt; -1">
<CopyToOutput Include="$(MSBuildThisFileDirectory)dll\Win32\#Toolset#\Debug\signalrclient.dll" />
<CopyToOutput Include="$(MSBuildThisFileDirectory)dll\Win32\#Toolset#\Debug\signalrclient.pdb" />
<None Include="$(MSBuildThisFileDirectory)dll\Win32\#Toolset#\Debug\signalrclient.dll">
<DeploymentContent>true</DeploymentContent>
</None>
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)dll\Win32\#Toolset#\Debug\signalrclient.dll" />
</ItemGroup>
<ItemGroup Label="Win32 and #Toolset# and Release" Condition="'$(Platform.ToLower())' == 'Win32' And '$(PlatformToolset.ToLower())' == '#Toolset#' And $(Configuration.ToLower().IndexOf('debug')) == -1">
<CopyToOutput Include="$(MSBuildThisFileDirectory)dll\Win32\#Toolset#\Release\signalrclient.dll" />
<CopyToOutput Include="$(MSBuildThisFileDirectory)dll\Win32\#Toolset#\Release\signalrclient.pdb" />
<None Include="$(MSBuildThisFileDirectory)dll\Win32\#Toolset#\Release\signalrclient.dll">
<DeploymentContent>true</DeploymentContent>
</None>
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)dll\Win32\#Toolset#\Debug\signalrclient.dll" />
</ItemGroup>
</Target>
</Project>

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

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<package>
<metadata>
<id>Microsoft.AspNet.SignalR.Client.Cpp.#Toolset#.WinDesktop</id>
<title>Microsoft ASP.NET SignalR C++ Client</title>
<version>0.0.0</version>
<authors>Microsoft</authors>
<owners>Microsoft</owners>
<licenseUrl>http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm</licenseUrl>
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
<projectUrl>http://www.asp.net/signalr</projectUrl>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>C++ client for ASP.NET SignalR.</description>
<language>en-US</language>
<tags>Microsoft AspNet SignalR AspNetSignalR Client C++ native</tags>
<releaseNotes>https://github.com/SignalR/SignalR/releases</releaseNotes>
<dependencies>
<dependency id="cpprestsdk.#Toolset#.windesktop.msvcstl.dyn.rt-dyn" version="2.7.0" />
</dependencies>
</metadata>
</package>

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

@ -1,7 +1,85 @@
<<<<<<< HEAD
ASP.NET SignalR C++ Client
========
ASP.NET SignalR is a is a new library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to your applications. What is "real-time web" functionality? It's the ability to have your server-side code push content to the connected clients as it happens, in real-time.
This project is part of ASP.NET vNext. You can find samples, documentation and getting started instructions for ASP.NET vNext at the [Home](https://github.com/aspnet/home) repo.
=======
ASP.NET SignalR C++ Client
========
SignalR C++ Client is a native client for the [ASP.NET SignalR](https://github.com/SignalR/SignalR/).
###Supported platforms
The bits that ship on NuGet currently can be used in Win32/x64 native windows desktop applications built with Visual Studio 2013 or Visual Studio 2015. Note that you need to download the package that matches your Visual Studio version. If you work with Visual Studio 2013 the matching package is Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop if you use Visual Studio 2015 the matching package is Microsoft.AspNet.SignalR.Client.Cpp.v140.WinDesktop.
###Get it on NuGet
`Install-Package Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop -Pre` (version for Visual Studio 2013)
`Install-Package Microsoft.AspNet.SignalR.Client.Cpp.v140.WinDesktop -Pre` (version for Visual Studio 2015)
###Use it
The repo contains a separate solution (samples_VS2013.sln) with sample projects showing how to use the client to communicate with a SignalR server using Persistent Connections and Hubs.
###Nightly builds
Signed nigthly builds are available on a separate feed. You can find them [here](https://www.myget.org/gallery/aspnetvnext)
###Building the Code
* Clone the repo:
`git clone https://github.com/aspnet/SignalR-Client-Cpp.git`
####Building for Windows
* Building from Visual Studio:
Open the signalrclient.sln in Visual Studio 2013 or Visual Studio 2015 and build.
* Building from command line:
* Open the Developer Command Prompt for Visual Studio 2013 or Visual Studio 2015
* Run:
* `build.cmd /t:Build` to build the code
* `build.cmd` to build the code and run tests
* `build.cmd /t:CreatePackage` to build the code and create a private NuGet package for the Visual Studio version the package was built with. The package will be placed in the `artifacts\build` directory.
####Building for Linux (Linux support is currently only experimental)
* Clone C++ REST SDK code
* Sync the code to a tag - e.g. `git checkout v2.7.0` (optional)
* Build C++ REST SDK code as described [here](https://github.com/Microsoft/cpprestsdk/wiki/How-to-build-for-Linux)
* From the root of SignalR C++ Client repo:
* `mkdir build.release`
* `cd build.release`
* `CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Release -DCPPREST_INCLUDE_DIR={C++ REST SDK include directory} -DCPPREST_LIB_DIR={C++ REST SDK lib directory}`
replace `{C++ REST SDK include directory}` and `{C++ REST SDK lib directory}` with paths to corresponding C++ REST SDK folders - e.g.:
`CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Release -DCPPREST_INCLUDE_DIR=~/source/casablanca/Release/include/ -DCPPREST_LIB_DIR=~/source/casablanca/Release/build.release/Binaries`
* `make`
* The binaries will be placed in the `bin` folder
###Running tests
####Running tests on Windows
* From Visual Studio
* to run unit tests select signalrclienttests as a start project and run. Alternatively you can install the Google Test runner extension for Visual Studio and run the tests from the test explorer.
* to run end-to-end test start the test host by selecting the signalrclient-testhost project as a start project and then select the signalrclient-e2e-tests as a start project and run.
* From command line
* Open the Developer Command Prompt for Visual Studio 2013 or Visual Studio 2015
* run `build.cmd`
####Running tests on Linux
* Build the code
* Run `signalrclienttests`
* Known issues:
* A few tests randomly fail (needs investigation)
* Oftentimes the process does not exit even though all tests appear to have completed (needs investigation)
>>>>>>> release

1
build.cmd Normal file
Просмотреть файл

@ -0,0 +1 @@
msbuild "%~dp0\Build\build.msbuild" /v:minimal /maxcpucount /nodeReuse:false %*

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

@ -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,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.
#pragma once
#include "_exports.h"
#include <memory>
#include <functional>
#include "pplx/pplxtasks.h"
#include "connection_state.h"
#include "trace_level.h"
#include "log_writer.h"
#include <unordered_map>
namespace signalr
{
class connection_impl;
class connection
{
public:
typedef std::function<void __cdecl(const utility::string_t&)> message_received_handler;
SIGNALRCLIENT_API explicit connection(const utility::string_t& url, const utility::string_t& query_string = _XPLATSTR(""),
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 pplx::task<void> __cdecl start();
SIGNALRCLIENT_API pplx::task<void> __cdecl send(const utility::string_t& data);
SIGNALRCLIENT_API void __cdecl set_message_received(const message_received_handler& message_received_callback);
SIGNALRCLIENT_API void __cdecl set_reconnecting(const std::function<void __cdecl()>& reconnecting_callback);
SIGNALRCLIENT_API void __cdecl set_reconnected(const std::function<void __cdecl()>& reconnected_callback);
SIGNALRCLIENT_API void __cdecl set_disconnected(const std::function<void __cdecl()>& disconnected_callback);
SIGNALRCLIENT_API void __cdecl set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers);
SIGNALRCLIENT_API pplx::task<void> __cdecl stop();
SIGNALRCLIENT_API connection_state __cdecl get_connection_state() 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,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
namespace signalr
{
enum class connection_state
{
connecting,
connected,
reconnecting,
disconnecting,
disconnected
};
}

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

@ -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 "_exports.h"
#include <memory>
#include <functional>
#include "pplx/pplxtasks.h"
#include "cpprest/json.h"
#include "connection_state.h"
#include "trace_level.h"
#include "log_writer.h"
#include "hub_proxy.h"
namespace signalr
{
class hub_connection_impl;
class hub_connection
{
public:
SIGNALRCLIENT_API explicit hub_connection(const utility::string_t& url, const utility::string_t& query_string = U(""),
trace_level trace_level = trace_level::all, std::shared_ptr<log_writer> log_writer = nullptr, bool use_default_url = true);
SIGNALRCLIENT_API ~hub_connection();
hub_connection(const hub_connection&) = delete;
hub_connection& operator=(const hub_connection&) = delete;
SIGNALRCLIENT_API pplx::task<void> __cdecl start();
SIGNALRCLIENT_API pplx::task<void> __cdecl stop();
SIGNALRCLIENT_API hub_proxy __cdecl create_hub_proxy(const utility::string_t& hub_name);
SIGNALRCLIENT_API connection_state __cdecl get_connection_state() const;
SIGNALRCLIENT_API void __cdecl set_reconnecting(const std::function<void __cdecl()>& reconnecting_callback);
SIGNALRCLIENT_API void __cdecl set_reconnected(const std::function<void __cdecl()>& reconnected_callback);
SIGNALRCLIENT_API void __cdecl set_disconnected(const std::function<void __cdecl()>& disconnected_callback);
SIGNALRCLIENT_API void __cdecl set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers);
private:
std::shared_ptr<hub_connection_impl> m_pImpl;
pplx::task<web::json::value> invoke_json(const utility::string_t& hub_name, const utility::string_t& method_name, const web::json::value& arguments,
const std::function<void(const web::json::value&)>& on_progress);
};
}

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

@ -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
#include <stdexcept>
#include "cpprest/details/basic_types.h"
#include "cpprest/json.h"
#include "cpprest/asyncrt_utils.h"
namespace signalr
{
class hub_exception : public std::runtime_error
{
public:
hub_exception(const utility::string_t &what, const web::json::value& error_data)
: runtime_error(utility::conversions::to_utf8string(what)), m_error_data(error_data)
{}
web::json::value error_data() const
{
return m_error_data;
}
private:
web::json::value m_error_data;
};
}

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

@ -0,0 +1,77 @@
// 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 <memory>
#include <functional>
#include "pplx/pplxtasks.h"
#include "cpprest/details/basic_types.h"
#include "cpprest/json.h"
namespace signalr
{
class internal_hub_proxy;
class hub_proxy
{
public:
typedef std::function<void __cdecl (const web::json::value&)> method_invoked_handler;
typedef std::function<void __cdecl (const web::json::value&)> on_progress_handler;
explicit hub_proxy(const std::shared_ptr<internal_hub_proxy>& proxy);
SIGNALRCLIENT_API hub_proxy();
SIGNALRCLIENT_API hub_proxy(const hub_proxy& other);
SIGNALRCLIENT_API hub_proxy(const hub_proxy&& other);
SIGNALRCLIENT_API ~hub_proxy();
SIGNALRCLIENT_API hub_proxy& __cdecl operator=(const hub_proxy& other);
SIGNALRCLIENT_API hub_proxy& __cdecl operator=(const hub_proxy&& other);
SIGNALRCLIENT_API utility::string_t __cdecl get_hub_name() const;
SIGNALRCLIENT_API void __cdecl on(const utility::string_t& event_name, const method_invoked_handler& handler);
template<typename T>
pplx::task<T> invoke(const utility::string_t& method_name, const on_progress_handler& on_progress = [](const web::json::value&){})
{
static_assert(std::is_same<web::json::value, T>::value, "only web::json::value allowed");
return invoke_json(method_name, web::json::value().array(), on_progress);
}
template<typename T>
pplx::task<T> invoke(const utility::string_t& method_name, const web::json::value& arguments,
const on_progress_handler& on_progress = [](const web::json::value&){})
{
static_assert(std::is_same<web::json::value, T>::value, "only web::json::value allowed");
return invoke_json(method_name, arguments, on_progress);
}
private:
std::shared_ptr<internal_hub_proxy> m_pImpl;
SIGNALRCLIENT_API pplx::task<web::json::value> __cdecl invoke_json(const utility::string_t& method_name, const web::json::value& arguments,
const on_progress_handler& on_progress);
SIGNALRCLIENT_API pplx::task<void> __cdecl invoke_void(const utility::string_t& method_name, const web::json::value& arguments,
const on_progress_handler& on_progress);
};
template<>
inline pplx::task<void> hub_proxy::invoke<void>(const utility::string_t& method_name, const on_progress_handler& on_progress)
{
return invoke_void(method_name, web::json::value().array(), on_progress);
}
template<>
inline pplx::task<void> hub_proxy::invoke<void>(const utility::string_t& method_name, const web::json::value& arguments,
const on_progress_handler& on_progress)
{
return invoke_void(method_name, arguments, on_progress);
}
}

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

@ -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 "cpprest/details/basic_types.h"
namespace signalr
{
class log_writer
{
public:
// NOTE: the caller does not enforce thread safety of this call
virtual void __cdecl write(const utility::string_t &entry) = 0;
};
}

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

@ -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)
{
return static_cast<trace_level>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
inline trace_level operator&(trace_level lhs, trace_level rhs)
{
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 transport_type
{
long_polling,
websockets
};
}

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

@ -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 <stdexcept>
#include "cpprest/details/basic_types.h"
#include "cpprest/asyncrt_utils.h"
namespace signalr
{
class web_exception : public std::runtime_error
{
public:
explicit web_exception(const utility::string_t &what)
: runtime_error(utility::conversions::to_utf8string(what))
{}
};
}

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

@ -0,0 +1,63 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PersistentConnectionSample", "samples\PersistentConnectionSample\PersistentConnectionSample.vcxproj", "{BD075706-11E9-403B-A2E3-A5E1397E53EF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HubConnectionSample", "samples\HubConnectionSample\HubConnectionSample.vcxproj", "{3C9BD092-18E6-4C6E-A887-CDFC80ACB206}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalRServer", "samples\SignalRServer\SignalRServer.csproj", "{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{4AA7C02A-A2E9-4E22-A026-B43D623C272F}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
.nuget\NuGet.targets = .nuget\NuGet.targets
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|Win32 = Debug|Win32
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Debug|Any CPU.ActiveCfg = Debug|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Debug|Win32.ActiveCfg = Debug|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Debug|Win32.Build.0 = Debug|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Release|Any CPU.ActiveCfg = Release|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Release|Mixed Platforms.Build.0 = Release|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Release|Win32.ActiveCfg = Release|Win32
{BD075706-11E9-403B-A2E3-A5E1397E53EF}.Release|Win32.Build.0 = Release|Win32
{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}.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
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Debug|Win32.ActiveCfg = Debug|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Release|Any CPU.Build.0 = Release|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}.Release|Win32.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

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

@ -0,0 +1,81 @@
#include "stdafx.h"
#include <iostream>
#include <sstream>
#include "signalrclient\hub_connection.h"
void send_message(signalr::hub_proxy proxy, const utility::string_t& name, const utility::string_t& message)
{
web::json::value args{};
args[0] = web::json::value::string(name);
args[1] = web::json::value(message);
// if you get an internal compiler error uncomment the lambda below or install VS Update 4
proxy.invoke<void>(U("send"), args/*, [](const web::json::value&){}*/)
.then([](pplx::task<void> invoke_task) // fire and forget but we need to observe exceptions
{
try
{
invoke_task.get();
}
catch (const std::exception &e)
{
ucout << U("Error while sending data: ") << e.what();
}
});
}
void chat(const utility::string_t& name)
{
signalr::hub_connection connection{U("http://localhost:34281")};
auto proxy = connection.create_hub_proxy(U("ChatHub"));
proxy.on(U("broadcastMessage"), [](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: ");
});
connection.start()
.then([proxy, name]()
{
ucout << U("Enter your message:");
for (;;)
{
utility::string_t message;
std::getline(ucin, message);
if (message == U(":q"))
{
break;
}
send_message(proxy, name, message);
}
})
.then([&connection]() // fine to capture by reference - we are blocking so it is guaranteed to be valid
{
return connection.stop();
})
.then([](pplx::task<void> stop_task)
{
try
{
stop_task.get();
ucout << U("connection stopped successfully") << std::endl;
}
catch (const std::exception &e)
{
ucout << U("exception when starting or stopping connection: ") << e.what() << std::endl;
}
}).get();
}
int main()
{
ucout << U("Enter your name: ");
utility::string_t name;
std::getline(ucin, name);
chat(name);
return 0;
}

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

@ -0,0 +1,110 @@
<?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="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>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
<Import Project="..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.5.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.5.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets')" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Import Project="..\..\packages\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.1.0.0-alpha-10017\build\native\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.targets" Condition="Exists('..\..\packages\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.1.0.0-alpha-10017\build\native\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.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">
<NuGetPackageImportStamp>7fba3d7a</NuGetPackageImportStamp>
</PropertyGroup>
<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>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</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>
</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">
<SubType>Designer</SubType>
</None>
</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.v120.windesktop.msvcstl.dyn.rt-dyn.2.5.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.5.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets'))" />
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.1.0.0-alpha-10017\build\native\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.1.0.0-alpha-10017\build\native\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.targets'))" />
</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,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn" version="2.5.0" targetFramework="Native" />
<package id="Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop" version="1.0.0-alpha-10017" targetFramework="Native" />
</packages>

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

@ -0,0 +1 @@
#include "stdafx.h"

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

@ -0,0 +1,2 @@
#pragma once

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

@ -0,0 +1,61 @@
#include "stdafx.h"
#include "signalrclient\connection.h"
#include <iostream>
void send_message(signalr::connection &connection, const utility::string_t& message)
{
connection.send(message)
.then([](pplx::task<void> send_task) // fire and forget but we need to observe exceptions
{
try
{
send_task.get();
}
catch (const std::exception &e)
{
ucout << U("Error while sending data: ") << e.what();
}
});
}
int main()
{
signalr::connection connection{ U("http://localhost:34281/echo") };
connection.set_message_received([](const utility::string_t& m)
{
ucout << U("Message received:") << m << std::endl << U("Enter message: ");
});
connection.start()
.then([&connection]() // fine to capture by reference - we are blocking so it is guaranteed to be valid
{
for (;;)
{
utility::string_t message;
std::getline(ucin, message);
if (message == U(":q"))
{
break;
}
send_message(connection, message);
}
return connection.stop();
})
.then([](pplx::task<void> stop_task)
{
try
{
stop_task.get();
ucout << U("connection stopped successfully") << std::endl;
}
catch (const std::exception &e)
{
ucout << U("exception when starting or closing connection: ") << e.what() << std::endl;
}
}).get();
return 0;
}

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

@ -0,0 +1,110 @@
<?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="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{BD075706-11E9-403B-A2E3-A5E1397E53EF}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>PersistentConnectionSample</RootNamespace>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">true</DownloadNuGetExe>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</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">
<NuGetPackageImportStamp>2a57ca92</NuGetPackageImportStamp>
</PropertyGroup>
<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>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</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>
</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="PersistentConnectionSample.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>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.5.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.5.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets')" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Import Project="..\..\packages\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.1.0.0-alpha-10017\build\native\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.targets" Condition="Exists('..\..\packages\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.1.0.0-alpha-10017\build\native\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.targets')" />
</ImportGroup>
<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.v120.windesktop.msvcstl.dyn.rt-dyn.2.5.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.5.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets'))" />
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.1.0.0-alpha-10017\build\native\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.1.0.0-alpha-10017\build\native\Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop.targets'))" />
</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="PersistentConnectionSample.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

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

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn" version="2.5.0" targetFramework="Native" />
<package id="Microsoft.AspNet.SignalR.Client.Cpp.v120.WinDesktop" version="1.0.0-alpha-10017" targetFramework="Native" />
</packages>

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

@ -0,0 +1 @@
#include "stdafx.h"

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

@ -0,0 +1 @@
#pragma once

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

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
namespace SignalRServer
{
public class ChatHub : Hub
{
public void Send(string name, string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(name, message);
}
}
}

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

@ -0,0 +1,18 @@
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
namespace SignalRServer
{
public class EchoConnection : PersistentConnection
{
protected override Task OnConnected(IRequest request, string connectionId)
{
return Connection.Send(connectionId, "Welcome!");
}
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
return Connection.Broadcast(data);
}
}
}

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

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SignalRServer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SignalRServer")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("64b40dbb-f6c1-45b4-8fc4-764526e9f16c")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

2671
samples/SignalRServer/Scripts/jquery-1.10.2.intellisense.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

9803
samples/SignalRServer/Scripts/jquery-1.10.2.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

23
samples/SignalRServer/Scripts/jquery-1.10.2.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

8
samples/SignalRServer/Scripts/jquery.signalR-2.2.0.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{A6782DC4-7435-4DB2-9E34-3F0390BC3FDE}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SignalRServer</RootNamespace>
<AssemblyName>SignalRServer</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<UseIISExpress>true</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">true</DownloadNuGetExe>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNet.SignalR.Core">
<HintPath>..\..\packages\Microsoft.AspNet.SignalR.Core.2.2.0\lib\net45\Microsoft.AspNet.SignalR.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.SignalR.SystemWeb">
<HintPath>..\..\packages\Microsoft.AspNet.SignalR.SystemWeb.2.2.0\lib\net45\Microsoft.AspNet.SignalR.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb">
<HintPath>..\..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security">
<HintPath>..\..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Owin">
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web.Services" />
<Reference Include="System.EnterpriseServices" />
</ItemGroup>
<ItemGroup>
<Content Include="packages.config" />
<Content Include="Scripts\jquery-1.10.2.min.map" />
<None Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</None>
<None Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</None>
</ItemGroup>
<ItemGroup>
<None Include="Scripts\jquery-1.10.2.intellisense.js" />
<Content Include="index.html" />
<Content Include="Scripts\jquery-1.10.2.js" />
<Content Include="Scripts\jquery-1.10.2.min.js" />
<Content Include="Scripts\jquery.signalR-2.2.0.js" />
<Content Include="Scripts\jquery.signalR-2.2.0.min.js" />
<Content Include="Web.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="ChatHub.cs" />
<Compile Include="EchoConnection.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Startup.cs" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>34281</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:34281/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.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('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

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

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<StartPageUrl>index.html</StartPageUrl>
<StartAction>SpecificPage</StartAction>
<AspNetDebugging>True</AspNetDebugging>
<SilverlightDebugging>False</SilverlightDebugging>
<NativeDebugging>False</NativeDebugging>
<SQLDebugging>False</SQLDebugging>
<ExternalProgram>
</ExternalProgram>
<StartExternalURL>
</StartExternalURL>
<StartCmdLineArguments>
</StartCmdLineArguments>
<StartWorkingDirectory>
</StartWorkingDirectory>
<EnableENC>True</EnableENC>
<AlwaysStartWebServerOnDebug>True</AlwaysStartWebServerOnDebug>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

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

@ -0,0 +1,18 @@
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(SignalRServer.Startup))]
namespace SignalRServer
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
app.MapSignalR<EchoConnection>("/echo");
}
}
}

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

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

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

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

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

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

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

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<title>SignalR Simple Chat</title>
<style type="text/css">
.container {
background-color: #99CCFF;
border: thick solid #808080;
padding: 20px;
margin: 20px;
}
</style>
</head>
<body>
<div class="container">
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" />
<input type="hidden" id="displayname" />
<ul id="discussion"></ul>
</div>
<!--Script references. -->
<!--Reference the jQuery library. -->
<script src="Scripts/jquery-1.10.2.min.js"></script>
<!--Reference the SignalR library. -->
<script src="Scripts/jquery.signalR-2.2.0.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="signalr/hubs"></script>
<!--Add script to update the page and send messages.-->
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (name, message) {
// Html encode display name and message.
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li><strong>' + encodedName
+ '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
</script>
</body>
</html>

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

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="jQuery" version="1.10.2" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR" version="2.2.0" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.Core" version="2.2.0" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.JS" version="2.2.0" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.SystemWeb" version="2.2.0" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
</packages>

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

@ -0,0 +1,131 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
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}") = "gtest", "test\gtest-1.7.0\msvc\gtest.vcxproj", "{2AF210A9-5BDC-45E8-95DD-07B5A2616493}"
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}") = ".nuget", ".nuget", "{063421D3-4E32-4BE5-874A-2E784B450858}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
.nuget\NuGet.targets = .nuget\NuGet.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{AABF08B1-12A4-4D06-A188-F01FBF8A9658}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "signalrclient-e2e-tests", "test\signalrclient-e2e-tests\Build\VS\signalrclient-e2e-tests.vcxproj", "{6006C96A-29F0-4B18-8DDD-764DC3419E2F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "signalrclient-testhost", "test\signalrclient-testhost\signalrclient-testhost.csproj", "{11848039-1F13-4047-9539-8F9F45930788}"
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
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Debug|Any CPU.ActiveCfg = Debug|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Debug|Win32.ActiveCfg = Debug|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Debug|Win32.Build.0 = Debug|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Debug|x64.ActiveCfg = Debug|x64
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Debug|x64.Build.0 = Debug|x64
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Release|Any CPU.ActiveCfg = Release|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Release|Mixed Platforms.Build.0 = Release|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Release|Win32.ActiveCfg = Release|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Release|Win32.Build.0 = Release|Win32
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.Release|x64.ActiveCfg = Release|x64
{2AF210A9-5BDC-45E8-95DD-07B5A2616493}.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
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Debug|Any CPU.ActiveCfg = Debug|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Debug|Win32.ActiveCfg = Debug|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Debug|Win32.Build.0 = Debug|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Debug|x64.ActiveCfg = Debug|x64
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Debug|x64.Build.0 = Debug|x64
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Release|Any CPU.ActiveCfg = Release|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Release|Mixed Platforms.Build.0 = Release|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Release|Win32.ActiveCfg = Release|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Release|Win32.Build.0 = Release|Win32
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Release|x64.ActiveCfg = Release|x64
{6006C96A-29F0-4B18-8DDD-764DC3419E2F}.Release|x64.Build.0 = Release|x64
{11848039-1F13-4047-9539-8F9F45930788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Debug|Win32.ActiveCfg = Debug|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Debug|x64.ActiveCfg = Debug|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Release|Any CPU.Build.0 = Release|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Release|Win32.ActiveCfg = Release|Any CPU
{11848039-1F13-4047-9539-8F9F45930788}.Release|x64.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{10376148-BCF4-4B55-98A5-3C98C87FD898} = {AABF08B1-12A4-4D06-A188-F01FBF8A9658}
{2AF210A9-5BDC-45E8-95DD-07B5A2616493} = {AABF08B1-12A4-4D06-A188-F01FBF8A9658}
{6006C96A-29F0-4B18-8DDD-764DC3419E2F} = {AABF08B1-12A4-4D06-A188-F01FBF8A9658}
{11848039-1F13-4047-9539-8F9F45930788} = {AABF08B1-12A4-4D06-A188-F01FBF8A9658}
EndGlobalSection
EndGlobal

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

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="cpprestsdk" version="2.7.0" targetFramework="Native" />
<package id="cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn" version="2.7.0" targetFramework="Native" />
<package id="cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn" version="2.7.0" targetFramework="Native" />
<package id="cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn" version="2.7.0" targetFramework="Native" />
<package id="cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn" version="2.7.0" targetFramework="Native" />
<package id="cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn" version="2.7.0" targetFramework="Native" />
<package id="cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn" version="2.7.0" targetFramework="Native" />
<package id="cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn" version="2.7.0" targetFramework="Native" />
</packages>

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

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.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>
</PropertyGroup>
<Import Project="..\..\..\..\Build\Config.Definitions.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<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">
<NuGetPackageImportStamp>57b32df2</NuGetPackageImportStamp>
</PropertyGroup>
<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\hub_connection.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\hub_exception.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\hub_proxy.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\log_writer.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\trace_level.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\transport_type.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_websocket_client.h" />
<ClInclude Include="..\..\event.h" />
<ClInclude Include="..\..\http_sender.h" />
<ClInclude Include="..\..\hub_connection_impl.h" />
<ClInclude Include="..\..\internal_hub_proxy.h" />
<ClInclude Include="..\..\callback_manager.h" />
<ClInclude Include="..\..\logger.h" />
<ClInclude Include="..\..\negotiation_response.h" />
<ClInclude Include="..\..\request_sender.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_client.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="..\..\http_sender.cpp" />
<ClCompile Include="..\..\hub_connection.cpp" />
<ClCompile Include="..\..\hub_connection_impl.cpp" />
<ClCompile Include="..\..\hub_proxy.cpp" />
<ClCompile Include="..\..\internal_hub_proxy.cpp" />
<ClCompile Include="..\..\callback_manager.cpp" />
<ClCompile Include="..\..\logger.cpp" />
<ClCompile Include="..\..\request_sender.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>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Import Project="..\..\..\..\packages\cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\..\..\packages\cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn.targets')" />
<Import Project="..\..\..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets')" />
<Import Project="..\..\..\..\packages\cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\..\..\packages\cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn.targets')" />
<Import Project="..\..\..\..\packages\cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\..\..\packages\cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn.targets')" />
<Import Project="..\..\..\..\packages\cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\..\..\packages\cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn.targets')" />
<Import Project="..\..\..\..\packages\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\..\..\packages\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.targets')" />
<Import Project="..\..\..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets')" />
</ImportGroup>
<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('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winapp.msvcstl.dyn.rt-dyn.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.windesktop.msvcstl.dyn.rt-dyn.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winphone.msvcstl.dyn.rt-dyn.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winphonesl.msvcstl.dyn.rt-dyn.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v120.winxp.msvcstl.dyn.rt-dyn.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.7.0\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets'))" />
</Target>
</Project>

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

@ -0,0 +1,177 @@
<?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="..\..\request_sender.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="..\..\websocket_client.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="..\..\internal_hub_proxy.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="..\..\..\..\include\signalrclient\hub_proxy.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>
</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="..\..\request_sender.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="..\..\internal_hub_proxy.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="..\..\hub_proxy.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\trace_log_writer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

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

@ -0,0 +1,27 @@
set (SOURCES
callback_manager.cpp
connection.cpp
connection_impl.cpp
default_websocket_client.cpp
http_sender.cpp
hub_connection.cpp
hub_connection_impl.cpp
hub_proxy.cpp
internal_hub_proxy.cpp
logger.cpp
request_sender.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
utility::string_t 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 utility::string_t& 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 utility::string_t& 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();
}
}
utility::string_t callback_manager::get_callback_id()
{
auto callback_id = m_id++;
utility::stringstream_t 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;
utility::string_t register_callback(const std::function<void(const web::json::value&)>& callback);
bool invoke_callback(const utility::string_t& callback_id, const web::json::value& arguments, bool remove_callback);
bool remove_callback(const utility::string_t& callback_id);
void clear(const web::json::value& arguments);
private:
std::atomic<int> m_id { 0 };
std::unordered_map<utility::string_t, std::function<void(const web::json::value&)>> m_callbacks;
std::mutex m_map_lock;
const web::json::value m_dtor_clear_arguments;
utility::string_t 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<utility::string_t, utility::string_t, bool>
{
bool operator()(const utility::string_t& s1, const utility::string_t& 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<utility::string_t, std::size_t>
{
std::size_t operator()(const utility::string_t& s) const
{
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,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.
#include "stdafx.h"
#include "signalrclient/connection.h"
#include "signalrclient/transport_type.h"
#include "connection_impl.h"
namespace signalr
{
connection::connection(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level, std::shared_ptr<log_writer> log_writer)
: m_pImpl(connection_impl::create(url, query_string, 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;
pplx::task<void> connection::start()
{
return m_pImpl->start();
}
pplx::task<void> connection::send(const utility::string_t& data)
{
return m_pImpl->send(data);
}
void connection::set_message_received(const message_received_handler& message_received_callback)
{
m_pImpl->set_message_received_string(message_received_callback);
}
void connection::set_reconnecting(const std::function<void()>& reconnecting_callback)
{
m_pImpl->set_reconnecting(reconnecting_callback);
}
void connection::set_reconnected(const std::function<void()>& reconnected_callback)
{
m_pImpl->set_reconnected(reconnected_callback);
}
void connection::set_disconnected(const std::function<void()>& disconnected_callback)
{
m_pImpl->set_disconnected(disconnected_callback);
}
void connection::set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
m_pImpl->set_headers(headers);
}
pplx::task<void> connection::stop()
{
return m_pImpl->stop();
}
connection_state connection::get_connection_state() const
{
return m_pImpl->get_connection_state();
}
}

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

@ -0,0 +1,855 @@
// 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 "cpprest/asyncrt_utils.h"
#include "constants.h"
#include "connection_impl.h"
#include "request_sender.h"
#include "url_builder.h"
#include "trace_log_writer.h"
#include "make_unique.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 utility::string_t& entry);
}
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, const utility::string_t& query_string,
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer)
{
return connection_impl::create(url, query_string, trace_level, log_writer, std::make_unique<web_request_factory>(), std::make_unique<transport_factory>());
}
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level,
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
{
return std::shared_ptr<connection_impl>(new connection_impl(url, query_string, trace_level,
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move(web_request_factory), std::move(transport_factory)));
}
connection_impl::connection_impl(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
: m_base_url(url), m_query_string(query_string), m_connection_state(connection_state::disconnected), m_reconnect_delay(2000),
m_logger(log_writer, trace_level), m_transport(nullptr), m_web_request_factory(std::move(web_request_factory)),
m_transport_factory(std::move(transport_factory)), m_message_received([](const web::json::value&){}),
m_reconnecting([](){}), m_reconnected([](){}), m_disconnected([](){})
{ }
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.
// This for instance happens when the connection goes out of scope while a reconnect is in progress. In this
// case the reconnect logic will not be able to acquire the connection instance from the weak_pointer to
// signal the event so this dtor would hang indefinitely. Using a shared_ptr to the connection in reconnect
// is not a good idea since it would prevent from invoking this dtor until the connection is reconnected or
// reconnection fails even if the instance actually went out of scope.
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);
}
pplx::task<void> connection_impl::start()
{
{
std::lock_guard<std::mutex> lock(m_stop_lock);
if (!change_state(connection_state::disconnected, connection_state::connecting))
{
return pplx::task_from_exception<void>(
std::runtime_error("cannot start a connection that is not in the disconnected state"));
}
// 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_message_id = m_groups_token = _XPLATSTR("");
}
pplx::task_completion_event<void> start_tce;
auto connection = shared_from_this();
pplx::task_from_result()
.then([connection]()
{
return request_sender::negotiate(*connection->m_web_request_factory, connection->m_base_url,
connection->m_connection_data, connection->m_query_string, connection->m_headers);
}, m_disconnect_cts.get_token())
.then([connection](negotiation_response negotiation_response)
{
if (negotiation_response.protocol_version != PROTOCOL)
{
return pplx::task_from_exception<void>(
std::runtime_error(std::string{ "incompatible protocol version. client protocol version: " }
.append(utility::conversions::to_utf8string(PROTOCOL)
.append(", server protocol version: ")
.append(utility::conversions::to_utf8string(negotiation_response.protocol_version)))));
}
return connection->start_transport(negotiation_response)
.then([connection, negotiation_response](std::shared_ptr<transport> transport)
{
connection->m_connection_token = negotiation_response.connection_token;
connection->m_reconnect_window =
negotiation_response.disconnect_timeout + std::max(negotiation_response.keep_alive_timeout, 0);
connection->m_transport = transport;
});
}, m_disconnect_cts.get_token())
.then([connection]()
{
return request_sender::start(*connection->m_web_request_factory, connection->m_base_url,
connection->m_transport->get_transport_type(), connection->m_connection_token,
connection->m_connection_data, connection->m_query_string, connection->m_headers);
}, m_disconnect_cts.get_token())
.then([start_tce, connection](pplx::task<void> previous_task)
{
try
{
previous_task.get();
if (!connection->change_state(connection_state::connecting, connection_state::connected))
{
connection->m_logger.log(trace_level::errors,
utility::string_t(_XPLATSTR("internal error - transition from an unexpected state. expected state: connecting, actual state: "))
.append(translate_connection_state(connection->get_connection_state())));
_ASSERTE(false);
}
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,
_XPLATSTR("starting the connection has been cancelled."));
}
else
{
connection->m_logger.log(trace_level::errors,
utility::string_t(_XPLATSTR("connection could not be started due to: "))
.append(utility::conversions::to_string_t(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::create_task(start_tce);
}
pplx::task<std::shared_ptr<transport>> connection_impl::start_transport(negotiation_response negotiation_response)
{
if (!negotiation_response.try_websockets)
{
return pplx::task_from_exception<std::shared_ptr<transport>>(
std::runtime_error("websockets not supported on the server and there is no fallback transport"));
}
auto connection = shared_from_this();
pplx::task_completion_event<void> connect_request_tce;
auto weak_connection = std::weak_ptr<connection_impl>(connection);
auto& disconnect_cts = m_disconnect_cts;
auto& logger = m_logger;
auto process_response_callback =
[weak_connection, connect_request_tce, disconnect_cts, logger](const utility::string_t& response) mutable
{
// 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 message 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,
utility::string_t(_XPLATSTR("ignoring stray message received after connection was restarted. message: "))
.append(response));
return;
}
auto connection = weak_connection.lock();
if (connection)
{
connection->process_response(response, connect_request_tce);
}
};
auto error_callback =
[weak_connection, connect_request_tce, disconnect_cts, logger](const std::exception &e) mutable
{
// 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,
utility::string_t(_XPLATSTR("ignoring stray error received after connection was restarted. error: "))
.append(utility::conversions::to_string_t(e.what())));
return;
}
// no op after connection started successfully
connect_request_tce.set_exception(e);
auto connection = weak_connection.lock();
if (connection)
{
connection->reconnect();
}
};
auto transport = connection->m_transport_factory->create_transport(
transport_type::websockets, connection->m_logger, connection->m_headers, process_response_callback, error_callback);
pplx::create_task([negotiation_response, connect_request_tce, disconnect_cts, weak_connection]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(negotiation_response.transport_connect_timeout));
// if the disconnect_cts is cancelled 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(std::runtime_error("transport timed out when trying to connect"));
}
});
return connection->send_connect_request(transport, negotiation_response.connection_token, 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 utility::string_t& connection_token, const pplx::task_completion_event<void>& connect_request_tce)
{
auto logger = m_logger;
auto connect_url = url_builder::build_connect(m_base_url, transport->get_transport_type(),
connection_token, m_connection_data, m_query_string);
transport->connect(connect_url)
.then([connect_request_tce, logger](pplx::task<void> connect_task)
mutable {
try
{
connect_task.get();
}
catch (const std::exception& e)
{
logger.log(
trace_level::errors,
utility::string_t(_XPLATSTR("transport could not connect due to: "))
.append(utility::conversions::to_string_t(e.what())));
connect_request_tce.set_exception(std::current_exception());
}
});
return pplx::create_task(connect_request_tce);
}
void connection_impl::process_response(const utility::string_t& response, const pplx::task_completion_event<void>& connect_request_tce)
{
m_logger.log(trace_level::messages,
utility::string_t(_XPLATSTR("processing message: ")).append(response));
try
{
const auto result = web::json::value::parse(response);
if (!result.is_object())
{
m_logger.log(trace_level::info, utility::string_t(_XPLATSTR("unexpected response received from the server: "))
.append(response));
return;
}
if (result.has_field(_XPLATSTR("I")))
{
invoke_message_received(result);
return;
}
// The assumption is that we cannot start reconnecting when a message is being processed otherwise there
// a data race occurs - we could read `m_groups_token` and `m_message_id` while they are being set below.
// This can't happen right now since in the `websocket_transport.receive_loop` we either process the message
// or effectively invoke reconnect.
if (result.has_field(_XPLATSTR("G")) && result.at(_XPLATSTR("G")).is_string())
{
m_groups_token = result.at(_XPLATSTR("G")).as_string();
}
if (result.has_field(_XPLATSTR("M")) && result.at(_XPLATSTR("M")).is_array())
{
_ASSERTE(result.has_field(_XPLATSTR("C")));
_ASSERTE(result.at(_XPLATSTR("C")).is_string());
m_message_id = result.at(_XPLATSTR("C")).as_string();
if (result.has_field(_XPLATSTR("S")) && result.at(_XPLATSTR("S")).is_integer() && result.at(_XPLATSTR("S")).as_integer() == 1)
{
connect_request_tce.set();
}
for (auto& m : result.at(_XPLATSTR("M")).as_array())
{
invoke_message_received(m);
}
}
}
catch (const std::exception &e)
{
m_logger.log(trace_level::errors, utility::string_t(_XPLATSTR("error occured when parsing response: "))
.append(utility::conversions::to_string_t(e.what()))
.append(_XPLATSTR(". response: "))
.append(response));
}
}
void connection_impl::invoke_message_received(const web::json::value& message)
{
try
{
m_message_received(message);
}
catch (const std::exception &e)
{
m_logger.log(
trace_level::errors,
utility::string_t(_XPLATSTR("message_received callback threw an exception: "))
.append(utility::conversions::to_string_t(e.what())));
}
catch (...)
{
m_logger.log(trace_level::errors, _XPLATSTR("message_received callback threw an unknown exception"));
}
}
pplx::task<void> connection_impl::send(const utility::string_t& data)
{
// 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;
auto connection_state = get_connection_state();
if (connection_state != signalr::connection_state::connected || !transport)
{
return pplx::task_from_exception<void>(std::runtime_error(
std::string{ "cannot send data when the connection is not in the connected state. current connection state: " }
.append(utility::conversions::to_utf8string(translate_connection_state(connection_state)))));
}
auto logger = m_logger;
logger.log(trace_level::info, utility::string_t(_XPLATSTR("sending data: ")).append(data));
return transport->send(data)
.then([logger](pplx::task<void> send_task)
mutable {
try
{
send_task.get();
}
catch (const std::exception &e)
{
logger.log(
trace_level::errors,
utility::string_t(_XPLATSTR("error sending data: "))
.append(utility::conversions::to_string_t(e.what())));
throw;
}
});
}
pplx::task<void> connection_impl::stop()
{
m_logger.log(trace_level::info, _XPLATSTR("stopping connection"));
auto connection = shared_from_this();
return shutdown()
.then([connection]()
{
{
// 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,
utility::string_t(_XPLATSTR("disconnected callback threw an exception: "))
.append(utility::conversions::to_string_t(e.what())));
}
catch (...)
{
connection->m_logger.log(
trace_level::errors,
utility::string_t(_XPLATSTR("disconnected callback threw an unknown exception")));
}
});
}
// 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, _XPLATSTR("acquired lock in shutdown()"));
auto current_state = get_connection_state();
if (current_state == connection_state::disconnected)
{
return pplx::task_from_result();
}
if (current_state == connection_state::disconnecting)
{
// cancelled 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([](){}, cts.get_token());
}
// we request a cancellation of the ongoing start or reconnect request (if any) and wait until it is cancelled
m_disconnect_cts.cancel();
while (m_start_completed_event.wait(60000) != 0)
{
m_logger.log(trace_level::errors,
_XPLATSTR("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, reconnecting 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 || m_connection_state == connection_state::reconnecting);
change_state(connection_state::disconnecting);
}
// This is fire and forget because we don't really care about the result
request_sender::abort(*m_web_request_factory, m_base_url, m_transport->get_transport_type(), m_connection_token, m_connection_data, m_query_string, m_headers)
.then([](pplx::task<utility::string_t> abort_task)
{
try
{
abort_task.get();
}
catch (...)
{
// We don't care about the result and even if the request failed there is not much we can do. We do
// need to observe the exception though to prevent from a crash due to unobserved exception exception.
}
});
return m_transport->disconnect();
}
void connection_impl::reconnect()
{
m_logger.log(trace_level::info, _XPLATSTR("connection lost - trying to re-establish connection"));
pplx::cancellation_token_source disconnect_cts;
{
std::lock_guard<std::mutex> lock(m_stop_lock);
m_logger.log(trace_level::info, _XPLATSTR("acquired lock before invoking reconnecting callback"));
// reconnect might be called when starting the connection has not finished yet so wait until it is done
// before actually trying to reconnect
while (m_start_completed_event.wait(60000) != 0)
{
m_logger.log(trace_level::errors,
_XPLATSTR("internal error - reconnect is still waiting for the start operation to finish which should have already finished or timed out"));
}
// exit if starting the connection has not completed successfully or there is an ongoing stop request
if (!change_state(connection_state::connected, connection_state::reconnecting))
{
m_logger.log(trace_level::info,
_XPLATSTR("reconnecting cancelled - connection is not in the connected state"));
return;
}
disconnect_cts = m_disconnect_cts;
}
try
{
m_logger.log(trace_level::info, _XPLATSTR("invoking reconnecting callback"));
m_reconnecting();
m_logger.log(trace_level::info, _XPLATSTR("reconnecting callback returned without error"));
}
catch (const std::exception &e)
{
m_logger.log(
trace_level::errors,
utility::string_t(_XPLATSTR("reconnecting callback threw an exception: "))
.append(utility::conversions::to_string_t(e.what())));
}
catch (...)
{
m_logger.log(
trace_level::errors,
utility::string_t(_XPLATSTR("reconnecting callback threw an unknown exception")));
}
{
std::lock_guard<std::mutex> lock(m_stop_lock);
m_logger.log(trace_level::info, _XPLATSTR("acquired lock before starting reconnect logic"));
// This is to prevent a case where a connection was stopped (and possibly restarted and got into a reconnecting
// state) after we changed the state to reconnecting in the original reconnecting request. In this case we have
// the original cts which would have been cancelled by the stop request and we can use it to stop the original
// reconnecting request
if (disconnect_cts.get_token().is_canceled())
{
m_logger.log(trace_level::info,
_XPLATSTR("reconnecting cancelled - connection was stopped and restarted after reconnecting started"));
return;
}
// we set the connection to the reconnecting before we invoked the reconnecting callback. If the connection
// state changed from the reconnecting state the user might have stopped/restarted the connection in the
// reconnecting callback or there might have started stopping the connection on the main thread and we should
// not try to continue the reconnect
if (m_connection_state != connection_state::reconnecting)
{
m_logger.log(trace_level::info,
_XPLATSTR("reconnecting cancelled - connection is no longer in the reconnecting state"));
return;
}
// re-using the start completed event is safe because you cannot start the connection if it is not in the
// disconnected state. It also make it easier to handle stopping the connection when it is reconnecting.
m_start_completed_event.reset();
}
auto reconnect_url = url_builder::build_reconnect(m_base_url, m_transport->get_transport_type(),
m_connection_token, m_connection_data, m_message_id, m_groups_token, m_query_string);
auto weak_connection = std::weak_ptr<connection_impl>(shared_from_this());
// this is non-blocking
try_reconnect(reconnect_url, utility::datetime::utc_now().to_interval(), m_reconnect_window, m_reconnect_delay, disconnect_cts)
.then([weak_connection](pplx::task<bool> reconnect_task)
{
// try reconnect does not throw
auto reconnected = reconnect_task.get();
auto connection = weak_connection.lock();
if (!connection)
{
// connection instance went away - nothing to be done
return pplx::task_from_result();
}
if (reconnected)
{
if (!connection->change_state(connection_state::reconnecting, connection_state::connected))
{
connection->m_logger.log(trace_level::errors,
utility::string_t(_XPLATSTR("internal error - transition from an unexpected state. expected state: reconnecting, actual state: "))
.append(translate_connection_state(connection->get_connection_state())));
_ASSERTE(false);
}
// we must set the event before calling into the user code to prevent a deadlock that would happen
// if the user called stop() from the handler
connection->m_start_completed_event.set();
try
{
connection->m_logger.log(trace_level::info, _XPLATSTR("invoking reconnected callback"));
connection->m_reconnected();
connection->m_logger.log(trace_level::info, _XPLATSTR("reconnected callback returned without error"));
}
catch (const std::exception &e)
{
connection->m_logger.log(
trace_level::errors,
utility::string_t(_XPLATSTR("reconnected callback threw an exception: "))
.append(utility::conversions::to_string_t(e.what())));
}
catch (...)
{
connection->m_logger.log(
trace_level::errors, _XPLATSTR("reconnected callback threw an unknown exception"));
}
return pplx::task_from_result();
}
connection->m_start_completed_event.set();
return connection->stop();
});
}
// the assumption is that this function won't throw
pplx::task<bool> connection_impl::try_reconnect(const web::uri& reconnect_url, const utility::datetime::interval_type reconnect_start_time,
int reconnect_window /*milliseconds*/, int reconnect_delay /*milliseconds*/, pplx::cancellation_token_source disconnect_cts)
{
if (disconnect_cts.get_token().is_canceled())
{
log(m_logger, trace_level::info, utility::string_t(_XPLATSTR("reconnecting cancelled - connection is being stopped. line: "))
.append(utility::conversions::to_string_t(std::to_string(__LINE__))));
return pplx::task_from_result<bool>(false);
}
auto weak_connection = std::weak_ptr<connection_impl>(shared_from_this());
auto& logger = m_logger;
return m_transport->connect(reconnect_url)
.then([weak_connection, reconnect_url, reconnect_start_time, reconnect_window, reconnect_delay, logger, disconnect_cts]
(pplx::task<void> reconnect_task)
{
try
{
log(logger, trace_level::info, _XPLATSTR("reconnect attempt starting"));
reconnect_task.get();
log(logger, trace_level::info, _XPLATSTR("reconnect attempt completed successfully"));
return pplx::task_from_result<bool>(true);
}
catch (const std::exception& e)
{
log(logger, trace_level::info, utility::string_t(_XPLATSTR("reconnect attempt failed due to: "))
.append(utility::conversions::to_string_t(e.what())));
}
if (disconnect_cts.get_token().is_canceled())
{
log(logger, trace_level::info, utility::string_t(_XPLATSTR("reconnecting cancelled - connection is being stopped. line: "))
.append(utility::conversions::to_string_t(std::to_string(__LINE__))));
return pplx::task_from_result<bool>(false);
}
auto reconnect_window_end = reconnect_start_time + utility::datetime::from_milliseconds(reconnect_window);
if (utility::datetime::utc_now().to_interval() + utility::datetime::from_milliseconds(reconnect_delay) > reconnect_window_end)
{
utility::ostringstream_t oss;
oss << _XPLATSTR("connection could not be re-established within the configured timeout of ")
<< reconnect_window << _XPLATSTR(" milliseconds");
log(logger, trace_level::info, oss.str());
return pplx::task_from_result<bool>(false);
}
std::this_thread::sleep_for(std::chrono::milliseconds(reconnect_delay));
if (disconnect_cts.get_token().is_canceled())
{
log(logger, trace_level::info, utility::string_t(_XPLATSTR("reconnecting cancelled - connection is being stopped. line: "))
.append(utility::conversions::to_string_t(std::to_string(__LINE__))));
return pplx::task_from_result<bool>(false);
}
auto connection = weak_connection.lock();
if (connection)
{
return connection->try_reconnect(reconnect_url, reconnect_start_time, reconnect_window, reconnect_delay, disconnect_cts);
}
log(logger, trace_level::info, _XPLATSTR("reconnecting cancelled - connection no longer valid."));
return pplx::task_from_result<bool>(false);
});
}
connection_state connection_impl::get_connection_state() const
{
return m_connection_state.load();
}
void connection_impl::set_message_received_string(const std::function<void(const utility::string_t&)>& message_received)
{
set_message_received_json([message_received](const web::json::value& payload)
{
message_received(payload.serialize());
});
}
void connection_impl::set_message_received_json(const std::function<void(const web::json::value&)>& 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_connection_data(const utility::string_t& connection_data)
{
_ASSERTE(get_connection_state() == connection_state::disconnected);
m_connection_data = connection_data;
}
void connection_impl::set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
ensure_disconnected("cannot set headers when the connection is not in the disconnected state. ");
m_headers = headers;
}
void connection_impl::set_reconnecting(const std::function<void()>& reconnecting)
{
ensure_disconnected("cannot set the reconnecting callback when the connection is not in the disconnected state. ");
m_reconnecting = reconnecting;
}
void connection_impl::set_reconnected(const std::function<void()>& reconnected)
{
ensure_disconnected("cannot set the reconnected callback when the connection is not in the disconnected state. ");
m_reconnected = reconnected;
}
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::set_reconnect_delay(const int reconnect_delay)
{
ensure_disconnected("cannot set reconnect delay when the connection is not in the disconnected state. ");
m_reconnect_delay = reconnect_delay;
}
void connection_impl::ensure_disconnected(const std::string& error_message)
{
auto state = get_connection_state();
if (state != connection_state::disconnected)
{
throw std::runtime_error(error_message + std::string{"current connection state: "}
.append(utility::conversions::to_utf8string(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(_XPLATSTR(" -> "))
.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."
}
utility::string_t connection_impl::translate_connection_state(connection_state state)
{
switch (state)
{
case connection_state::connecting:
return _XPLATSTR("connecting");
case connection_state::connected:
return _XPLATSTR("connected");
case connection_state::reconnecting:
return _XPLATSTR("reconnecting");
case connection_state::disconnecting:
return _XPLATSTR("disconnecting");
case connection_state::disconnected:
return _XPLATSTR("disconnected");
default:
_ASSERTE(false);
return _XPLATSTR("(unknown)");
}
}
namespace
{
// this is a workaround for the VS2013 compiler bug where mutable lambdas won't compile sometimes
static void log(const logger& logger, trace_level level, const utility::string_t& entry)
{
const_cast<signalr::logger &>(logger).log(level, entry);
}
}
}

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

@ -0,0 +1,102 @@
// 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 "cpprest/http_client.h"
#include "signalrclient/trace_level.h"
#include "signalrclient/connection_state.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 utility::string_t& url, const utility::string_t& query_string,
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer);
static std::shared_ptr<connection_impl> create(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level,
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
connection_impl(const connection_impl&) = delete;
connection_impl& operator=(const connection_impl&) = delete;
~connection_impl();
pplx::task<void> start();
pplx::task<void> send(const utility::string_t &data);
pplx::task<void> stop();
connection_state get_connection_state() const;
void set_message_received_string(const std::function<void(const utility::string_t&)>& message_received);
void set_message_received_json(const std::function<void(const web::json::value&)>& message_received);
void set_reconnecting(const std::function<void()>& reconnecting);
void set_reconnected(const std::function<void()>& reconnected);
void set_disconnected(const std::function<void()>& disconnected);
void set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers);
void set_reconnect_delay(const int reconnect_delay /*milliseconds*/);
void set_connection_data(const utility::string_t& connection_data);
private:
web::uri m_base_url;
utility::string_t m_query_string;
std::atomic<connection_state> m_connection_state;
logger m_logger;
std::shared_ptr<transport> m_transport;
std::unique_ptr<web_request_factory> m_web_request_factory;
std::unique_ptr<transport_factory> m_transport_factory;
std::function<void(const web::json::value&)> m_message_received;
std::function<void()> m_reconnecting;
std::function<void()> m_reconnected;
std::function<void()> m_disconnected;
std::unordered_map<utility::string_t, utility::string_t> m_headers;
pplx::cancellation_token_source m_disconnect_cts;
std::mutex m_stop_lock;
event m_start_completed_event;
utility::string_t m_connection_token;
utility::string_t m_connection_data;
int m_reconnect_window; // in milliseconds
int m_reconnect_delay; // in milliseconds
utility::string_t m_message_id;
utility::string_t m_groups_token;
connection_impl(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
pplx::task<std::shared_ptr<transport>> start_transport(negotiation_response negotiation_response);
pplx::task<void> send_connect_request(const std::shared_ptr<transport>& transport, const utility::string_t& connection_token,
const pplx::task_completion_event<void>& connect_request_tce);
void process_response(const utility::string_t& response, const pplx::task_completion_event<void>& connect_request_tce);
pplx::task<void> shutdown();
void reconnect();
pplx::task<bool> try_reconnect(const web::uri& reconnect_url, const utility::datetime::interval_type reconnect_start_time,
int reconnect_window, int reconnect_delay, pplx::cancellation_token_source disconnect_cts);
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 web::json::value& message);
static utility::string_t translate_connection_state(connection_state state);
void ensure_disconnected(const std::string& error_message);
};
}

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

@ -0,0 +1,10 @@
// 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/details/basic_types.h"
#define SIGNALR_VERSION _XPLATSTR("1.0.0-beta1")
#define PROTOCOL _XPLATSTR("1.4")
#define USER_AGENT _XPLATSTR("SignalR.Client.Cpp/") SIGNALR_VERSION

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

@ -0,0 +1,53 @@
// 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 std::unordered_map<utility::string_t, utility::string_t>& headers)
{
web::websockets::client::websocket_client_config config;
for (auto &header : headers)
{
config.headers()[header.first] = header.second;
}
return config;
}
}
default_websocket_client::default_websocket_client(const std::unordered_map<utility::string_t, utility::string_t>& headers)
: m_underlying_client(create_client_config(headers))
{ }
pplx::task<void> default_websocket_client::connect(const web::uri &url)
{
return m_underlying_client.connect(url);
}
pplx::task<void> default_websocket_client::send(const utility::string_t &message)
{
web::websockets::client::websocket_outgoing_message msg;
msg.set_utf8_message(utility::conversions::to_utf8string(message));
return m_underlying_client.send(msg);
}
pplx::task<std::string> default_websocket_client::receive()
{
// the caller is responsible for observing exceptions
return m_underlying_client.receive()
.then([](web::websockets::client::websocket_incoming_message msg)
{
return msg.extract_string();
});
}
pplx::task<void> default_websocket_client::close()
{
return m_underlying_client.close();
}
}

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

@ -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
#include <unordered_map>
#include "cpprest/ws_client.h"
#include "websocket_client.h"
namespace signalr
{
class default_websocket_client : public websocket_client
{
public:
explicit default_websocket_client(const std::unordered_map<utility::string_t, utility::string_t>& headers);
pplx::task<void> connect(const web::uri &url) override;
pplx::task<void> send(const utility::string_t &message) override;
pplx::task<std::string> receive() override;
pplx::task<void> close() 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()
: 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]() { return m_signaled; });
return 0;
}
else
{
std::chrono::milliseconds period(timeout);
auto status = m_condition.wait_for(lock, period, [this]() { 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,35 @@
// 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/web_exception.h"
#include "web_request_factory.h"
#include "constants.h"
namespace signalr
{
namespace http_sender
{
pplx::task<utility::string_t> get(web_request_factory& request_factory, const web::uri& url, const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
auto request = request_factory.create_web_request(url);
request->set_method(web::http::methods::GET);
request->set_headers(headers);
request->set_user_agent(USER_AGENT);
return request->get_response().then([](web_response response)
{
if (response.status_code != 200)
{
utility::ostringstream_t oss;
oss << _XPLATSTR("web exception - ") << response.status_code << _XPLATSTR(" ") << response.reason_phrase;
throw web_exception(oss.str());
}
return response.body;
});
}
}
}

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

@ -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 "pplx/pplxtasks.h"
#include "cpprest/details/basic_types.h"
#include "web_request_factory.h"
namespace signalr
{
namespace http_sender
{
pplx::task<utility::string_t> get(web_request_factory& request_factory, const web::uri& url,
const std::unordered_map<utility::string_t, utility::string_t>& headers);
}
}

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

@ -0,0 +1,59 @@
// 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"
namespace signalr
{
hub_connection::hub_connection(const utility::string_t& url, const utility::string_t& query_string,
trace_level trace_level, std::shared_ptr<log_writer> log_writer, bool use_default_url)
: m_pImpl(hub_connection_impl::create(url, query_string, trace_level, std::move(log_writer), use_default_url))
{}
// 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;
pplx::task<void> hub_connection::start()
{
return m_pImpl->start();
}
pplx::task<void> hub_connection::stop()
{
return m_pImpl->stop();
}
hub_proxy hub_connection::create_hub_proxy(const utility::string_t& hub_name)
{
auto internal_proxy = m_pImpl->create_hub_proxy(hub_name);
return hub_proxy(internal_proxy);
}
connection_state hub_connection::get_connection_state() const
{
return m_pImpl->get_connection_state();
}
void hub_connection::set_reconnecting(const std::function<void()>& reconnecting_callback)
{
m_pImpl->set_reconnecting(reconnecting_callback);
}
void hub_connection::set_reconnected(const std::function<void()>& reconnected_callback)
{
m_pImpl->set_reconnected(reconnected_callback);
}
void hub_connection::set_disconnected(const std::function<void()>& disconnected_callback)
{
m_pImpl->set_disconnected(disconnected_callback);
}
void hub_connection::set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
m_pImpl->set_headers(headers);
}
}

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

@ -0,0 +1,356 @@
// 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"
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,
const std::function<void(const json::value&)>& on_progress);
static utility::string_t adapt_url(const utility::string_t& url, bool use_default_url);
}
std::shared_ptr<hub_connection_impl> hub_connection_impl::create(const utility::string_t& url, const utility::string_t& query_string,
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer, bool use_default_url)
{
return hub_connection_impl::create(url, query_string, trace_level, log_writer, use_default_url,
std::make_unique<web_request_factory>(), std::make_unique<transport_factory>());
}
std::shared_ptr<hub_connection_impl> hub_connection_impl::create(const utility::string_t& url, const utility::string_t& query_string,
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer, bool use_default_url,
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
{
auto connection = std::shared_ptr<hub_connection_impl>(new hub_connection_impl(url, query_string, trace_level,
log_writer ? log_writer : std::make_shared<trace_log_writer>(), use_default_url,
std::move(web_request_factory), std::move(transport_factory)));
connection->initialize();
return connection;
}
hub_connection_impl::hub_connection_impl(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level,
const std::shared_ptr<log_writer>& log_writer, bool use_default_url, std::unique_ptr<web_request_factory> web_request_factory,
std::unique_ptr<transport_factory> transport_factory)
: m_connection(connection_impl::create(adapt_url(url, use_default_url), query_string, trace_level, log_writer,
std::move(web_request_factory), std::move(transport_factory))),m_logger(log_writer, trace_level),
m_callback_manager(json::value::parse(_XPLATSTR("{ \"E\" : \"connection went out of scope before invocation result was received\"}")))
{ }
void hub_connection_impl::initialize()
{
auto this_hub_connection = shared_from_this();
// weak_ptr prevents a circular dependency leading to memory leak and other problems
auto weak_hub_connection = std::weak_ptr<hub_connection_impl>(this_hub_connection);
m_connection->set_message_received_json([weak_hub_connection](const web::json::value& message)
{
auto connection = weak_hub_connection.lock();
if (connection)
{
connection->process_message(message);
}
});
set_reconnecting([](){});
}
std::shared_ptr<internal_hub_proxy> hub_connection_impl::create_hub_proxy(const utility::string_t& hub_name)
{
if (hub_name.length() == 0)
{
throw std::invalid_argument("hub name cannot be empty");
}
if (get_connection_state() != connection_state::disconnected)
{
throw std::runtime_error("hub proxies cannot be created when the connection is not in the disconnected state");
}
auto iter = m_proxies.find(hub_name);
if (iter != m_proxies.end())
{
return iter->second;
}
auto weak_connection = std::weak_ptr<hub_connection_impl>(shared_from_this());
auto proxy = std::make_shared<internal_hub_proxy>(weak_connection, hub_name, m_logger);
m_proxies.insert(std::make_pair(hub_name, proxy));
return proxy;
}
pplx::task<void> hub_connection_impl::start()
{
if (m_proxies.size() > 0)
{
json::value connection_data;
auto index = 0;
for (auto kvp : m_proxies)
{
json::value hub;
hub[_XPLATSTR("Name")] = json::value::string(kvp.first);
connection_data[index++] = hub;
}
m_connection->set_connection_data(connection_data.serialize());
}
else
{
m_logger.log(trace_level::info, _XPLATSTR("no hub proxies exist for this hub connection"));
}
return m_connection->start();
}
pplx::task<void> hub_connection_impl::stop()
{
m_callback_manager.clear(json::value::parse(_XPLATSTR("{ \"E\" : \"connection was stopped before invocation result was received\"}")));
return m_connection->stop();
}
void hub_connection_impl::process_message(const web::json::value& message)
{
if (message.is_object())
{
// note this handles both - invocation returns and progress updates
if (message.has_field(_XPLATSTR("I")))
{
if (invoke_callback(message))
{
return;
}
}
if (message.has_field(_XPLATSTR("H")) && message.has_field(_XPLATSTR("M")) && message.has_field(_XPLATSTR("A")))
{
auto hub_name = message.at(_XPLATSTR("H")).as_string();
auto method = message.at(_XPLATSTR("M")).as_string();
auto iter = m_proxies.find(hub_name);
if (iter != m_proxies.end())
{
iter->second->invoke_event(method, message.at(_XPLATSTR("A")));
}
else
{
m_logger.log(trace_level::info,
utility::string_t(_XPLATSTR("no proxy found for hub invocation. hub: "))
.append(hub_name).append(_XPLATSTR(", method: ")).append(method));
}
return;
}
}
m_logger.log(trace_level::info, utility::string_t(_XPLATSTR("non-hub message received and will be discarded. message: "))
.append(message.serialize()));
}
bool hub_connection_impl::invoke_callback(const web::json::value& message)
{
auto is_progress = message.has_field(_XPLATSTR("P"));
auto id_source = is_progress ? message.at(_XPLATSTR("P")) : message;
if (id_source.has_field(_XPLATSTR("I")) && id_source.at(_XPLATSTR("I")).is_string())
{
auto callback_id = id_source.at(_XPLATSTR("I")).as_string();
// callbacks must not be removed for progress updates
if (!m_callback_manager.invoke_callback(callback_id, message, /*remove_callback*/ !is_progress))
{
m_logger.log(trace_level::info, utility::string_t(_XPLATSTR("no callback found for id: ")).append(callback_id));
}
return true;
}
return false;
}
pplx::task<json::value> hub_connection_impl::invoke_json(const utility::string_t& hub_name, const utility::string_t& method_name,
const json::value& arguments, const std::function<void(const json::value&)>& on_progress)
{
_ASSERTE(arguments.is_array());
pplx::task_completion_event<json::value> tce;
const auto callback_id = m_callback_manager.register_callback(
create_hub_invocation_callback(m_logger, [tce](const json::value& result) { tce.set(result); },
[tce](const std::exception_ptr e) { tce.set_exception(e); }, on_progress));
invoke_hub_method(hub_name, method_name, arguments, callback_id,
[tce](const std::exception_ptr e){tce.set_exception(e); });
return pplx::create_task(tce);
}
pplx::task<void> hub_connection_impl::invoke_void(const utility::string_t& hub_name, const utility::string_t& method_name,
const json::value& arguments, const std::function<void(const json::value&)>& on_progress)
{
_ASSERTE(arguments.is_array());
pplx::task_completion_event<void> tce;
const auto callback_id = m_callback_manager.register_callback(
create_hub_invocation_callback(m_logger, [tce](const json::value&) { tce.set(); },
[tce](const std::exception_ptr e){ tce.set_exception(e); }, on_progress));
invoke_hub_method(hub_name, method_name, arguments, callback_id,
[tce](const std::exception_ptr e){tce.set_exception(e); });
return pplx::create_task(tce);
}
void hub_connection_impl::invoke_hub_method(const utility::string_t& hub_name, const utility::string_t& method_name,
const json::value& arguments, const utility::string_t& callback_id, std::function<void(const std::exception_ptr)> set_exception)
{
json::value request;
request[_XPLATSTR("H")] = json::value::string(hub_name);
request[_XPLATSTR("M")] = json::value::string(method_name);
request[_XPLATSTR("A")] = arguments;
request[_XPLATSTR("I")] = json::value::string(callback_id);
auto this_hub_connection = shared_from_this();
// weak_ptr prevents a circular dependency leading to memory leak and other problems
auto weak_hub_connection = std::weak_ptr<hub_connection_impl>(this_hub_connection);
m_connection->send(request.serialize())
.then([set_exception, weak_hub_connection, callback_id](pplx::task<void> send_task)
{
try
{
send_task.get();
}
catch (const std::exception&)
{
set_exception(std::current_exception());
auto hub_connection = weak_hub_connection.lock();
if (hub_connection)
{
hub_connection->m_callback_manager.remove_callback(callback_id);
}
}
});
}
connection_state hub_connection_impl::get_connection_state() const
{
return m_connection->get_connection_state();
}
void hub_connection_impl::set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
m_connection->set_headers(headers);
}
void hub_connection_impl::set_reconnecting(const std::function<void()>& reconnecting)
{
// 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->set_reconnecting([weak_hub_connection, reconnecting]()
{
auto hub_connection = weak_hub_connection.lock();
if (hub_connection)
{
hub_connection->m_callback_manager.clear(
json::value::parse(_XPLATSTR("{ \"E\" : \"connection has been lost\"}")));
}
reconnecting();
});
}
void hub_connection_impl::set_reconnected(const std::function<void()>& reconnected)
{
m_connection->set_reconnected(reconnected);
}
void hub_connection_impl::set_disconnected(const std::function<void()>& disconnected)
{
m_connection->set_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,
const std::function<void(const json::value&)>& on_progress)
{
return[logger, set_result, set_exception, on_progress](const json::value& message)
{
if (message.has_field(_XPLATSTR("R")))
{
set_result(message.at(_XPLATSTR("R")));
return;
}
if (message.has_field(_XPLATSTR("P")))
{
auto progress_message = message.at(_XPLATSTR("P"));
auto data = progress_message.has_field(_XPLATSTR("D"))
? progress_message.at(_XPLATSTR("D"))
: json::value::null();
on_progress(data);
return;
}
if (message.has_field(_XPLATSTR("E")))
{
if (message.has_field(_XPLATSTR("H")) && message.at(_XPLATSTR("H")).is_boolean() && message.at(_XPLATSTR("H")).as_bool())
{
set_exception(
std::make_exception_ptr(
hub_exception(
message.at(_XPLATSTR("E")).serialize(),
message.has_field(_XPLATSTR("D")) ? message.at(_XPLATSTR("D")): json::value())));
}
else
{
set_exception(
std::make_exception_ptr(
std::runtime_error(utility::conversions::to_utf8string(message.at(_XPLATSTR("E")).serialize()))));
}
return;
}
set_result(json::value::null());
};
}
static utility::string_t adapt_url(const utility::string_t& url, bool use_default_url)
{
if (use_default_url)
{
auto new_url = url;
if (new_url.back() != _XPLATSTR('/'))
{
new_url.append(_XPLATSTR("/"));
}
new_url.append(_XPLATSTR("signalr"));
return new_url;
}
return url;
}
}
}

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

@ -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 "cpprest/details/basic_types.h"
#include "connection_impl.h"
#include "internal_hub_proxy.h"
#include "callback_manager.h"
#include "case_insensitive_comparison_utils.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 hub_connection_impl : public std::enable_shared_from_this<hub_connection_impl>
{
public:
static std::shared_ptr<hub_connection_impl> create(const utility::string_t& url, const utility::string_t& query_string,
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer, bool use_default_url);
static std::shared_ptr<hub_connection_impl> create(const utility::string_t& url, const utility::string_t& query_string,
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer, bool use_default_url,
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
hub_connection_impl(const hub_connection_impl&) = delete;
hub_connection_impl& operator=(const hub_connection_impl&) = delete;
std::shared_ptr<internal_hub_proxy> create_hub_proxy(const utility::string_t& hub_name);
pplx::task<json::value> invoke_json(const utility::string_t& hub_name, const utility::string_t& method_name, const json::value& arguments,
const std::function<void(const json::value&)>& on_progress = [](const json::value&){});
pplx::task<void> invoke_void(const utility::string_t& hub_name, const utility::string_t& method_name, const json::value& arguments,
const std::function<void(const json::value&)>& on_progress = [](const json::value&){});
pplx::task<void> start();
pplx::task<void> stop();
connection_state get_connection_state() const;
void set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers);
void set_reconnecting(const std::function<void()>& reconnecting);
void set_reconnected(const std::function<void()>& reconnected);
void set_disconnected(const std::function<void()>& disconnected);
private:
hub_connection_impl(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level,
const std::shared_ptr<log_writer>& log_writer, bool use_default_url,
std::unique_ptr<web_request_factory> web_request_factory, 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<utility::string_t, std::shared_ptr<internal_hub_proxy>, case_insensitive_hash, case_insensitive_equals> m_proxies;
void initialize();
void process_message(const web::json::value& message);
void invoke_hub_method(const utility::string_t& hub_name, const utility::string_t& method_name,
const json::value& arguments, const utility::string_t& callback_id, std::function<void(const std::exception_ptr)> set_exception);
bool invoke_callback(const web::json::value& message);
};
}

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

@ -0,0 +1,90 @@
// 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_proxy.h"
#include "internal_hub_proxy.h"
namespace signalr
{
hub_proxy::hub_proxy()
{ }
hub_proxy::hub_proxy(const hub_proxy& other)
: m_pImpl(other.m_pImpl)
{ }
hub_proxy::hub_proxy(const hub_proxy && other)
: m_pImpl(std::move(other.m_pImpl))
{ }
hub_proxy::hub_proxy(const std::shared_ptr<internal_hub_proxy>& proxy)
: m_pImpl(proxy)
{ }
// 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_proxy::~hub_proxy() = default;
utility::string_t hub_proxy::get_hub_name() const
{
if (!m_pImpl)
{
throw std::runtime_error("get_hub_name() cannot be called on uninitialized hub_proxy instance");
}
return m_pImpl->get_hub_name();
}
void hub_proxy::on(const utility::string_t& event_name, const method_invoked_handler& handler)
{
if (!m_pImpl)
{
throw std::runtime_error("on() cannot be called on uninitialized hub_proxy instance");
}
return m_pImpl->on(event_name, handler);
}
pplx::task<web::json::value> hub_proxy::invoke_json(const utility::string_t& method_name, const web::json::value& arguments,
const on_progress_handler& on_progress)
{
if (!m_pImpl)
{
throw std::runtime_error("invoke() cannot be called on uninitialized hub_proxy instance");
}
return m_pImpl->invoke_json(method_name, arguments, on_progress);
}
pplx::task<void> hub_proxy::invoke_void(const utility::string_t& method_name, const web::json::value& arguments,
const on_progress_handler& on_progress)
{
if (!m_pImpl)
{
throw std::runtime_error("invoke() cannot be called on uninitialized hub_proxy instance");
}
return m_pImpl->invoke_void(method_name, arguments, on_progress);
}
hub_proxy& hub_proxy::operator=(const hub_proxy& other)
{
if (this != &other)
{
m_pImpl = other.m_pImpl;
}
return *this;
}
hub_proxy& hub_proxy::operator=(const hub_proxy&& other)
{
if (this != &other)
{
m_pImpl = std::move(other.m_pImpl);
}
return *this;
}
}

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

@ -0,0 +1,81 @@
// 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 "internal_hub_proxy.h"
#include "hub_connection_impl.h"
namespace signalr
{
internal_hub_proxy::internal_hub_proxy(const std::weak_ptr<hub_connection_impl>& hub_connection, const utility::string_t& hub_name, const logger& logger)
: m_hub_connection(hub_connection), m_hub_name(hub_name), m_logger(logger)
{ }
utility::string_t internal_hub_proxy::get_hub_name() const
{
return m_hub_name;
}
void internal_hub_proxy::on(const utility::string_t& 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 connection = m_hub_connection.lock();
if (connection && connection->get_connection_state() != connection_state::disconnected)
{
throw std::runtime_error("can't register a handler if the connection is in a disconnected state");
}
if (m_subscriptions.find(event_name) != m_subscriptions.end())
{
throw std::runtime_error(std::string("an action for this event has already been registered. event name: ")
.append(utility::conversions::to_utf8string(event_name)));
}
m_subscriptions.insert(std::pair<utility::string_t, std::function<void(const json::value &)>> {event_name, handler});
}
void internal_hub_proxy::invoke_event(const utility::string_t& event_name, const json::value& arguments)
{
auto handler = m_subscriptions.find(event_name);
if (handler != m_subscriptions.end())
{
handler->second(arguments);
}
else
{
m_logger.log(trace_level::info,
utility::string_t(_XPLATSTR("no handler found for event. hub name: "))
.append(m_hub_name).append(_XPLATSTR(", event name: ")).append(event_name));
}
}
pplx::task<json::value> internal_hub_proxy::invoke_json(const utility::string_t& method_name, const json::value& arguments,
const std::function<void(const json::value&)>& on_progress)
{
auto connection = m_hub_connection.lock();
if (!connection)
{
return pplx::task_from_exception<json::value>(
std::runtime_error("the connection for which this hub proxy was created is no longer valid - it was either destroyed or went out of scope"));
}
return connection->invoke_json(get_hub_name(), method_name, arguments, on_progress);
}
pplx::task<void> internal_hub_proxy::invoke_void(const utility::string_t& method_name, const json::value& arguments,
const std::function<void(const json::value&)>& on_progress)
{
auto connection = m_hub_connection.lock();
if (!connection)
{
return pplx::task_from_exception<void>(
std::runtime_error("the connection for which this hub proxy was created is no longer valid - it was either destroyed or went out of scope"));
}
return connection->invoke_void(get_hub_name(), method_name, arguments, on_progress);
}
}

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

@ -0,0 +1,42 @@
// 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 "cpprest/details/basic_types.h"
#include "cpprest/json.h"
#include "logger.h"
#include "case_insensitive_comparison_utils.h"
using namespace web;
namespace signalr
{
class hub_connection_impl;
class internal_hub_proxy
{
public:
internal_hub_proxy(const std::weak_ptr<hub_connection_impl>& hub_connection, const utility::string_t& hub_name, const logger& logger);
internal_hub_proxy(const internal_hub_proxy&) = delete;
internal_hub_proxy& operator=(const internal_hub_proxy&) = delete;
utility::string_t get_hub_name() const;
void on(const utility::string_t& event_name, const std::function<void(const json::value &)>& handler);
void invoke_event(const utility::string_t& event_name, const json::value& arguments);
pplx::task<json::value> invoke_json(const utility::string_t& method_name, const json::value& arguments,
const std::function<void(const json::value&)>& on_progress = [](const json::value&){});
pplx::task<void> invoke_void(const utility::string_t& method_name, const json::value& arguments,
const std::function<void(const json::value&)>& on_progress = [](const json::value&){});
private:
std::weak_ptr<hub_connection_impl> m_hub_connection;
const utility::string_t m_hub_name;
logger m_logger;
std::unordered_map<utility::string_t, std::function<void(const json::value &)>, case_insensitive_hash, case_insensitive_equals> m_subscriptions;
};
}

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

@ -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)
: m_log_writer(log_writer), m_trace_level(trace_level)
{ }
void logger::log(trace_level level, const utility::string_t& entry)
{
if ((level & m_trace_level) != trace_level::none)
{
try
{
utility::ostringstream_t os;
os << utility::datetime::utc_now().to_string(utility::datetime::date_format::ISO_8601) << _XPLATSTR(" [")
<< std::left << std::setw(12) << translate_trace_level(level) << "] "<< entry << std::endl;
m_log_writer->write(os.str());
}
catch (const std::exception &e)
{
ucerr << _XPLATSTR("error occurred when logging: ") << utility::conversions::to_string_t(e.what())
<< std::endl << _XPLATSTR(" entry: ") << entry << std::endl;
}
catch (...)
{
ucerr << _XPLATSTR("unknown error occurred when logging") << std::endl << _XPLATSTR(" entry: ") << entry << std::endl;
}
}
}
utility::string_t logger::translate_trace_level(trace_level trace_level)
{
switch (trace_level)
{
case signalr::trace_level::messages:
return _XPLATSTR("message");
case signalr::trace_level::state_changes:
return _XPLATSTR("state change");
case signalr::trace_level::events:
return _XPLATSTR("event");
case signalr::trace_level::errors:
return _XPLATSTR("error");
case signalr::trace_level::info:
return _XPLATSTR("info");
default:
_ASSERTE(false);
return _XPLATSTR("(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);
void log(trace_level level, const utility::string_t& entry);
private:
std::shared_ptr<log_writer> m_log_writer;
trace_level m_trace_level;
static utility::string_t 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,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 "cpprest/details/basic_types.h"
namespace signalr
{
struct negotiation_response
{
utility::string_t connection_id;
utility::string_t connection_token;
int disconnect_timeout; // in milliseconds
int keep_alive_timeout; // in milliseconds
utility::string_t protocol_version;
bool try_websockets;
int transport_connect_timeout; // in milliseconds
};
}

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

@ -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.
#include "stdafx.h"
#include "request_sender.h"
#include "http_sender.h"
#include "url_builder.h"
namespace signalr
{
namespace request_sender
{
pplx::task<negotiation_response> negotiate(web_request_factory& request_factory, const web::uri& base_url,
const utility::string_t& connection_data, const utility::string_t& query_string,
const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
auto negotiate_url = url_builder::build_negotiate(base_url, connection_data, query_string);
return http_sender::get(request_factory, negotiate_url, headers)
.then([](utility::string_t body)
{
auto negotiation_response_json = web::json::value::parse(body);
negotiation_response response{
negotiation_response_json[_XPLATSTR("ConnectionId")].as_string(),
negotiation_response_json[_XPLATSTR("ConnectionToken")].as_string(),
(int)(negotiation_response_json[_XPLATSTR("DisconnectTimeout")].as_double() * 1000),
!negotiation_response_json[_XPLATSTR("KeepAliveTimeout")].is_null()
? (int)(negotiation_response_json[_XPLATSTR("KeepAliveTimeout")].as_double() * 1000)
: -1,
negotiation_response_json[_XPLATSTR("ProtocolVersion")].as_string(),
negotiation_response_json[_XPLATSTR("TryWebSockets")].as_bool(),
(int)(negotiation_response_json[_XPLATSTR("TransportConnectTimeout")].as_double() * 1000)
};
return response;
});
}
pplx::task<void> start(web_request_factory& request_factory, const web::uri &base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data,
const utility::string_t &query_string, const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
auto start_url = url_builder::build_start(base_url, transport, connection_token, connection_data, query_string);
return http_sender::get(request_factory, start_url, headers)
.then([](utility::string_t body)
{
auto start_response_json = web::json::value::parse(body);
if (start_response_json[_XPLATSTR("Response")].is_null() ||
start_response_json[_XPLATSTR("Response")].as_string() != _XPLATSTR("started"))
{
throw std::runtime_error(std::string("start request failed due to unexpected response from the server: ")
.append(utility::conversions::to_utf8string(body)));
}
});
}
pplx::task<utility::string_t> abort(web_request_factory& request_factory, const web::uri &base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data, const utility::string_t &query_string,
const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
auto abort_url = url_builder::build_abort(base_url, transport, connection_token, connection_data, query_string);
return http_sender::get(request_factory, abort_url, headers);
}
}
}

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

@ -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 "cpprest/base_uri.h"
#include "signalrclient/transport_type.h"
#include "web_request_factory.h"
#include "negotiation_response.h"
namespace signalr
{
namespace request_sender
{
pplx::task<negotiation_response> negotiate(web_request_factory& request_factory, const web::uri &base_url,
const utility::string_t& connection_data, const utility::string_t& query_string,
const std::unordered_map<utility::string_t, utility::string_t> &headers);
pplx::task<void> start(web_request_factory& request_factory, const web::uri& base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data, const utility::string_t& query_string,
const std::unordered_map<utility::string_t, utility::string_t>& headers);
pplx::task<utility::string_t> abort(web_request_factory& request_factory, const web::uri& base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data, const utility::string_t& query_string,
const std::unordered_map<utility::string_t, utility::string_t>& 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 utility::string_t &entry)
{
#ifdef _WIN32
// OutputDebugString is thread safe
OutputDebugString(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 utility::string_t &entry) override;
};
}

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

@ -0,0 +1,29 @@
// 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, const std::function<void(const utility::string_t&)>& process_response_callback,
std::function<void(const std::exception&)> error_callback)
: m_logger(logger), m_process_response_callback(process_response_callback), m_error_callback(error_callback)
{}
// 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()
{ }
void transport::process_response(const utility::string_t &message)
{
m_process_response_callback(message);
}
void transport::error(const std::exception& e)
{
m_error_callback(e);
}
}

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

@ -0,0 +1,40 @@
// 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 "cpprest/base_uri.h"
#include "signalrclient/transport_type.h"
#include "logger.h"
namespace signalr
{
class transport
{
public:
virtual pplx::task<void> connect(const web::uri &url) = 0;
virtual pplx::task<void> send(const utility::string_t &data) = 0;
virtual pplx::task<void> disconnect() = 0;
virtual transport_type get_transport_type() const = 0;
virtual ~transport();
protected:
transport(const logger& logger, const std::function<void(const utility::string_t &)>& process_response_callback,
std::function<void(const std::exception&)> error_callback);
void process_response(const utility::string_t &message);
void error(const std::exception &e);
logger m_logger;
private:
std::function<void(const utility::string_t &)> m_process_response_callback;
std::function<void(const std::exception&)> m_error_callback;
};
}

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

@ -0,0 +1,26 @@
// 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 std::unordered_map<utility::string_t, utility::string_t>& headers,
std::function<void(const utility::string_t&)> process_response_callback,
std::function<void(const std::exception&)> error_callback)
{
if (transport_type == signalr::transport_type::websockets)
{
return websocket_transport::create([headers](){ return std::make_shared<default_websocket_client>(headers); },
logger, process_response_callback, error_callback);
}
throw std::runtime_error("not implemented");
}
transport_factory::~transport_factory()
{ }
}

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

@ -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 <memory>
#include <unordered_map>
#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 std::unordered_map<utility::string_t, utility::string_t>& headers,
std::function<void(const utility::string_t&)> process_response_callback,
std::function<void(const std::exception&)> error_callback);
virtual ~transport_factory();
};
}

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

@ -0,0 +1,136 @@
// 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 "constants.h"
#include "cpprest/http_client.h"
#include "signalrclient/transport_type.h"
namespace signalr
{
namespace url_builder
{
utility::string_t get_transport_name(transport_type transport)
{
_ASSERTE(transport == transport_type::websockets || transport == transport_type::long_polling);
return transport == transport_type::websockets
? _XPLATSTR("webSockets")
: _XPLATSTR("longPolling");
}
void append_transport(web::uri_builder &builder, transport_type transport)
{
if (transport > static_cast<transport_type>(-1))
{
builder.append_query(_XPLATSTR("transport"), get_transport_name(transport));
}
}
void append_connection_token(web::uri_builder &builder, const utility::string_t &connection_token)
{
if (connection_token.length() > 0)
{
builder.append_query(_XPLATSTR("connectionToken"), connection_token, /* do_encoding */ true);
}
}
void append_connection_data(web::uri_builder& builder, const utility::string_t& connection_data)
{
if (connection_data.length() > 0)
{
builder.append_query(_XPLATSTR("connectionData"), connection_data, /* do_encoding */ true);
}
}
void append_message_id(web::uri_builder& builder, const utility::string_t& message_id)
{
if (message_id.length() > 0)
{
builder.append_query(_XPLATSTR("messageId"), message_id, /* do_encoding */ true);
}
}
void append_groups_token(web::uri_builder& builder, const utility::string_t& groups_token)
{
if (groups_token.length() > 0)
{
builder.append_query(_XPLATSTR("groupsToken"), groups_token, /* do_encoding */ true);
}
}
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::string_t(_XPLATSTR("wss")));
}
else
{
builder.set_scheme(utility::string_t(_XPLATSTR("ws")));
}
}
return builder;
}
web::uri_builder build_uri(const web::uri& base_url, const utility::string_t& command, transport_type transport,
const utility::string_t &connection_token, const utility::string_t& connection_data, const utility::string_t& query_string,
const utility::string_t& last_message_id = _XPLATSTR(""), const utility::string_t& groups_token = _XPLATSTR(""))
{
_ASSERTE(command == _XPLATSTR("reconnect") || (last_message_id.length() == 0 && groups_token.length() == 0));
web::uri_builder builder(base_url);
builder.append_path(command);
append_transport(builder, transport);
builder.append_query(_XPLATSTR("clientProtocol"), PROTOCOL);
append_connection_token(builder, connection_token);
append_connection_data(builder, connection_data);
append_message_id(builder, last_message_id);
append_groups_token(builder, groups_token);
return builder.append_query(query_string);
}
web::uri build_negotiate(const web::uri& base_url, const utility::string_t& connection_data, const utility::string_t& query_string)
{
return build_uri(base_url, _XPLATSTR("negotiate"), static_cast<transport_type>(-1),
/*connection_token*/ _XPLATSTR(""), connection_data, query_string).to_uri();
}
web::uri build_connect(const web::uri& base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data, const utility::string_t& query_string)
{
_ASSERTE(connection_token.length() > 0);
auto builder = build_uri(base_url, _XPLATSTR("connect"), transport, connection_token, connection_data, query_string);
return convert_to_websocket_url(builder, transport).to_uri();
}
web::uri build_reconnect(const web::uri& base_url, transport_type transport, const utility::string_t& connection_token,
const utility::string_t& connection_data, const utility::string_t& last_message_id, const utility::string_t& groups_token,
const utility::string_t& query_string)
{
_ASSERTE(connection_token.length() > 0);
auto builder = build_uri(base_url, _XPLATSTR("reconnect"), transport, connection_token, connection_data, query_string, last_message_id, groups_token);
return convert_to_websocket_url(builder, transport).to_uri();
}
web::uri build_start(const web::uri &base_url, transport_type transport,
const utility::string_t &connection_token, const utility::string_t& connection_data, const utility::string_t &query_string)
{
_ASSERTE(connection_token.length() > 0);
return build_uri(base_url, _XPLATSTR("start"), transport, connection_token, connection_data, query_string).to_uri();
}
web::uri build_abort(const web::uri &base_url, transport_type transport,
const utility::string_t &connection_token, const utility::string_t& connection_data, const utility::string_t &query_string)
{
_ASSERTE(connection_token.length() > 0);
return build_uri(base_url, _XPLATSTR("abort"), transport, connection_token, connection_data, query_string).to_uri();
}
}
}

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

@ -0,0 +1,29 @@
// 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
{
web::uri build_negotiate(const web::uri& base_url, const utility::string_t& connection_data,
const utility::string_t& query_string);
web::uri build_connect(const web::uri& base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data,
const utility::string_t& query_string);
web::uri build_reconnect(const web::uri& base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data,
const utility::string_t& last_message_id, const utility::string_t& groups_token,
const utility::string_t& query_string);
web::uri build_start(const web::uri& base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data,
const utility::string_t& query_string);
web::uri build_abort(const web::uri &base_url, transport_type transport,
const utility::string_t& connection_token, const utility::string_t& connection_data,
const utility::string_t& query_string);
}
}

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

@ -0,0 +1,49 @@
// 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 web::uri &url)
: m_url(url)
{ }
void web_request::set_method(const utility::string_t &method)
{
m_request.set_method(method);
}
void web_request::set_user_agent(const utility::string_t &user_agent_string)
{
m_request.headers()[_XPLATSTR("User-Agent")] = user_agent_string;
}
void web_request::set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers)
{
for (auto &header : headers)
{
m_request.headers()[header.first] = header.second;
}
}
pplx::task<web_response> web_request::get_response()
{
web::http::client::http_client client(m_url);
return client.request(m_request)
.then([](web::http::http_response response)
{
return web_response
{
response.status_code(),
response.reason_phrase(),
response.extract_string()
};
});
}
web_request::~web_request() = default;
}

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

@ -0,0 +1,31 @@
// 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 "cpprest/http_msg.h"
#include <unordered_map>
namespace signalr
{
class web_request
{
public:
explicit web_request(const web::uri &url);
virtual void set_method(const utility::string_t &method);
virtual void set_user_agent(const utility::string_t &user_agent_string);
virtual void set_headers(const std::unordered_map<utility::string_t, utility::string_t>& headers);
virtual pplx::task<web_response> get_response();
web_request& operator=(const web_request&) = delete;
virtual ~web_request();
private:
const web::uri m_url;
web::http::http_request m_request;
};
}

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

@ -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 web::uri &url)
{
return std::make_unique<web_request>(url);
}
web_request_factory::~web_request_factory()
{}
}

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

@ -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 "cpprest/base_uri.h"
#include "web_request.h"
namespace signalr
{
class web_request_factory
{
public:
virtual std::unique_ptr<web_request> create_web_request(const web::uri &url);
virtual ~web_request_factory();
};
}

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