Target to .NET 4.6.1, Update SignalR, Owin, Nancy (#84)

* Target .net 4.6.1

* Update to SignalR 2.2.2

* Fix Socks Reference in NZBDrone.Common

* UI Fixes, Build Fixes

* Update Nancy to 1.4.4

* Upgrade Microsoft Owin to 3.1.0

* Delete npm-shrinkwrap.json

* Fix SignalR Messages
pull/94/head
Qstick 7 years ago committed by GitHub
parent fbe6bfc78e
commit f460f630c3

@ -241,7 +241,7 @@
operator-assignment: ["off", "never"],
operator-linebreak: ["error", "after"],
padded-blocks: ["error", "never"],
quote-props: ["error", "consistent"],
quote-props: ["error", "as-needed"],
quotes: ["error", "single"],
require-jsdoc: "off",
semi: "error",

@ -41,9 +41,11 @@ const config = {
resolve: {
root: [
root,
path.join(root, 'Shims'),
path.join(root, 'JsLibraries')
]
path.join(root, 'Shims')
],
alias: {
jquery: 'jquery/src/jquery'
}
},
output: {
filename: path.join('_output', uiFolder, '[name].js'),

@ -23,7 +23,6 @@ function AppUpdatedModal(props) {
AppUpdatedModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
version: PropTypes.string.isRequired,
onModalClose: PropTypes.func.isRequired
};

@ -1,18 +1,6 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import AppUpdatedModal from './AppUpdatedModal';
function createMapStateToProps() {
return createSelector(
(state) => state.app.version,
(version) => {
return {
version
};
}
);
}
function createMapDispatchToProps(dispatch, props) {
return {
onModalClose() {
@ -21,4 +9,4 @@ function createMapDispatchToProps(dispatch, props) {
};
}
export default connect(createMapStateToProps, createMapDispatchToProps)(AppUpdatedModal);
export default connect(null, createMapDispatchToProps)(AppUpdatedModal);

@ -1,7 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import { kinds } from 'Helpers/Props';
import FieldSet from 'Components/FieldSet';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Button from 'Components/Link/Button';
import ModalContent from 'Components/Modal/ModalContent';
@ -88,10 +87,9 @@ function AppUpdatedModalContent(props) {
}
AppUpdatedModalContent.propTypes = {
isOpen: PropTypes.bool.isRequired,
version: PropTypes.string.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
onSeeChangesPress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired

@ -43,10 +43,16 @@ class AppUpdatedModalContentConnector extends Component {
//
// Lifecycle
componentDidUpdate() {
componentDidMount() {
this.props.dispatchFetchUpdates();
}
componentDidUpdate(prevProps) {
if (prevProps.version !== this.props.version) {
this.props.dispatchFetchUpdates();
}
}
//
// Render
@ -63,6 +69,7 @@ class AppUpdatedModalContentConnector extends Component {
}
AppUpdatedModalContentConnector.propTypes = {
version: PropTypes.string.isRequired,
dispatchFetchUpdates: PropTypes.func.isRequired
};

@ -1,4 +1,5 @@
import $ from 'jquery';
import 'signalr';
import PropTypes from 'prop-types';
import { Component } from 'react';
import { connect } from 'react-redux';
@ -8,7 +9,6 @@ import { setAppValue, setVersion } from 'Store/Actions/appActions';
import { update, updateItem, removeItem } from 'Store/Actions/baseActions';
import { fetchHealth } from 'Store/Actions/systemActions';
import { fetchQueue, fetchQueueDetails } from 'Store/Actions/queueActions';
require('signalR');
function getState(status) {
switch (status) {
@ -59,7 +59,7 @@ class SignalRConnector extends Component {
constructor(props, context) {
super(props, context);
this.signalRconnectionOptions = { transport: ['longPolling'] };
this.signalRconnectionOptions = { transport: ['webSockets', 'longPolling'] };
this.signalRconnection = null;
this.retryInterval = 5;
this.retryTimeoutId = null;
@ -230,7 +230,7 @@ class SignalRConnector extends Component {
}
handleVersion = (body) => {
const version = body.version;
const version = body.Version;
this.props.setVersion({ version });
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,8 +1,8 @@
var jquery = require('JsLibraries/jquery');
var ajax = require('jQuery/jquery.ajax');
import $ from 'jquery';
import ajax from 'jQuery/jquery.ajax';
ajax(jquery);
ajax($);
window.$ = jquery;
window.jQuery = jquery;
module.exports = jquery;
window.$ = $;
window.jQuery = $;
export default $;

@ -1,4 +0,0 @@
require('jquery');
const signalR = require('JsLibraries/jquery.signalR');
module.exports = signalR;

@ -1,4 +1,4 @@
const $ = require('JsLibraries/jquery');
import $ from 'jquery';
const absUrlRegex = /^(https?:)?\/\//i;
const apiRoot = window.Sonarr.apiRoot;
@ -34,9 +34,9 @@ function addApiKey(xhr) {
xhr.headers['X-Api-Key'] = window.Sonarr.apiKey;
}
module.exports = function(jQuery) {
const originalAjax = jQuery.ajax;
jQuery.ajax = function(xhr) {
export default function() {
const originalAjax = $.ajax;
$.ajax = function(xhr) {
if (xhr && isRelative(xhr)) {
moveBodyToQuery(xhr);
addRootUrl(xhr);
@ -44,4 +44,4 @@ module.exports = function(jQuery) {
}
return originalAjax.apply(this, arguments);
};
};
}

@ -1 +1,3 @@
import 'Shims/jquery';
__webpack_public_path__ = `${window.Sonarr.urlBase}/`;

5242
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

8409
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -51,6 +51,7 @@
"gulp-wrap": "0.13.0",
"history": "4.6.3",
"jdu": "1.0.0",
"jquery": "2.2.4",
"lodash": "4.17.4",
"mobile-detect": "1.3.6",
"moment": "2.17.1",
@ -92,6 +93,7 @@
"require-nocache": "1.0.0",
"reselect": "3.0.1",
"run-sequence": "1.2.0",
"signalr": "2.2.2",
"streamqueue": "1.1.1",
"style-loader": "0.13.1",
"stylelint": "7.3.1",

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>
@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Lidarr.Api.V3</RootNamespace>
<AssemblyName>Lidarr.Api.V3</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
@ -28,6 +28,7 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>..\..\_output\</OutputPath>
@ -38,6 +39,7 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="antlr.runtime, Version=2.7.6.2, Culture=neutral, processorArchitecture=MSIL">
@ -53,9 +55,8 @@
<Reference Include="Ical.Net.Collections, Version=2.1.0.18775, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net40\Ical.Net.Collections.dll</HintPath>
</Reference>
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
<Private>True</Private>
<Reference Include="Nancy, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.1.4.4\lib\net40\Nancy.dll</HintPath>
</Reference>
<Reference Include="Nancy.Authentication.Basic, Version=1.4.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.Authentication.Basic.1.4.1\lib\net40\Nancy.Authentication.Basic.dll</HintPath>
@ -213,22 +214,22 @@
<Project>{F6FC6BE7-0847-4817-A1ED-223DC647C3D7}</Project>
<Name>Marr.Data</Name>
</ProjectReference>
<ProjectReference Include="..\Lidarr.Http\Lidarr.Http.csproj">
<Project>{5370BFF7-1BD7-46BC-AF06-7D9EA5CDA1D6}</Project>
<Name>Lidarr.Http</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.Common\NzbDrone.Common.csproj">
<Project>{F2BE0FDF-6E47-4827-A420-DD4EF82407F8}</Project>
<Project>{f2be0fdf-6e47-4827-a420-dd4ef82407f8}</Project>
<Name>NzbDrone.Common</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.Core\NzbDrone.Core.csproj">
<Project>{FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}</Project>
<Project>{ff5ee3b6-913b-47ce-9ceb-11c51b4e1205}</Project>
<Name>NzbDrone.Core</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.SignalR\NzbDrone.SignalR.csproj">
<Project>{7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}</Project>
<Project>{7c2cc69f-5ca0-4e5c-85cb-983f9f6c3b36}</Project>
<Name>NzbDrone.SignalR</Name>
</ProjectReference>
<ProjectReference Include="..\Lidarr.Http\Lidarr.Http.csproj">
<Project>{5370BFF7-1BD7-46BC-AF06-7D9EA5CDA1D6}</Project>
<Name>Lidarr.Http</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

@ -10,6 +10,14 @@
<assemblyIdentity name="FluentMigrator" publicKeyToken="aacfc7de5acabf05" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /></startup></configuration>

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentValidation" version="6.2.1.0" targetFramework="net40" />
<package id="Ical.Net" version="2.2.32" targetFramework="net40" />
<package id="Nancy" version="1.4.3" targetFramework="net40" />
<package id="Nancy.Authentication.Basic" version="1.4.1" targetFramework="net40" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net40" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
<package id="NLog" version="4.4.3" targetFramework="net40" />
<package id="FluentValidation" version="6.2.1.0" targetFramework="net461" />
<package id="Ical.Net" version="2.2.32" targetFramework="net461" />
<package id="Nancy" version="1.4.4" targetFramework="net461" />
<package id="Nancy.Authentication.Basic" version="1.4.1" targetFramework="net461" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net461" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
<package id="NLog" version="4.4.3" targetFramework="net461" />
</packages>

@ -9,8 +9,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Lidarr.Http</RootNamespace>
<AssemblyName>Lidarr.Http</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
@ -20,6 +21,7 @@
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
@ -29,15 +31,15 @@
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\FluentValidation.6.2.1.0\lib\portable-net40+sl50+wp80+win8+wpa81\FluentValidation.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
<Private>True</Private>
<Reference Include="Nancy, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.1.4.4\lib\net40\Nancy.dll</HintPath>
</Reference>
<Reference Include="Nancy.Authentication.Basic, Version=1.4.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.Authentication.Basic.1.4.1\lib\net40\Nancy.Authentication.Basic.dll</HintPath>

@ -6,6 +6,14 @@
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /></startup></configuration>

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentValidation" version="6.2.1.0" targetFramework="net40" />
<package id="Nancy" version="1.4.3" targetFramework="net40" />
<package id="Nancy.Authentication.Basic" version="1.4.1" targetFramework="net40" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net40" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
<package id="NLog" version="4.4.3" targetFramework="net40" />
<package id="FluentValidation" version="6.2.1.0" targetFramework="net461" />
<package id="Nancy" version="1.4.4" targetFramework="net461" />
<package id="Nancy.Authentication.Basic" version="1.4.1" targetFramework="net461" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net461" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
<package id="NLog" version="4.4.3" targetFramework="net461" />
</packages>

@ -61,10 +61,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.SignalR", "NzbDron
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "External", "External", "{F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.SignalR.Core", "Microsoft.AspNet.SignalR.Core\Microsoft.AspNet.SignalR.Core.csproj", "{1B9A82C4-BCA1-4834-A33E-226F17BE070B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.SignalR.Owin", "Microsoft.AspNet.SignalR.Owin\Microsoft.AspNet.SignalR.Owin.csproj", "{2B8C6DAD-4D85-41B1-83FD-248D9F347522}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marr.Data", "Marr.Data\Marr.Data.csproj", "{F6FC6BE7-0847-4817-A1ED-223DC647C3D7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Mono", "NzbDrone.Mono\NzbDrone.Mono.csproj", "{15AD7579-A314-4626-B556-663F51D97CD1}"
@ -218,18 +214,6 @@ Global
{7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Mono|x86.Build.0 = Debug|x86
{7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Release|x86.ActiveCfg = Release|x86
{7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Release|x86.Build.0 = Release|x86
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|x86.ActiveCfg = Debug|x86
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|x86.Build.0 = Debug|x86
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Mono|x86.ActiveCfg = Debug|x86
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Mono|x86.Build.0 = Debug|x86
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|x86.ActiveCfg = Release|x86
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|x86.Build.0 = Release|x86
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Debug|x86.ActiveCfg = Debug|x86
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Debug|x86.Build.0 = Debug|x86
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Mono|x86.ActiveCfg = Release|x86
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Mono|x86.Build.0 = Release|x86
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Release|x86.ActiveCfg = Release|x86
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Release|x86.Build.0 = Release|x86
{F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Debug|x86.ActiveCfg = Debug|x86
{F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Debug|x86.Build.0 = Debug|x86
{F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Mono|x86.ActiveCfg = Release|x86
@ -313,8 +297,6 @@ Global
{3DCA7B58-B8B3-49AC-9D9E-56F4A0460976} = {486ADF86-DD89-4E19-B805-9D94F19800D9}
{95C11A9E-56ED-456A-8447-2C89C1139266} = {486ADF86-DD89-4E19-B805-9D94F19800D9}
{D12F7F2F-8A3C-415F-88FA-6DD061A84869} = {486ADF86-DD89-4E19-B805-9D94F19800D9}
{1B9A82C4-BCA1-4834-A33E-226F17BE070B} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{2B8C6DAD-4D85-41B1-83FD-248D9F347522} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{F6FC6BE7-0847-4817-A1ED-223DC647C3D7} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{15AD7579-A314-4626-B556-663F51D97CD1} = {0F0D4998-8F5D-4467-A909-BB192C4B3B4B}
{911284D3-F130-459E-836C-2430B6FBF21D} = {0F0D4998-8F5D-4467-A909-BB192C4B3B4B}
@ -327,8 +309,8 @@ Global
{74420A79-CC16-442C-8B1E-7C1B913844F0} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2C047BC5-490F-4DCE-962F-141370D23765}
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35;packages\Unity.2.1.505.2\lib\NET35
SolutionGuid = {2C047BC5-490F-4DCE-962F-141370D23765}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = NzbDrone.Console\NzbDrone.Console.csproj

@ -51,6 +51,9 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.1.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core" />

@ -1,160 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Principal;
using Microsoft.AspNet.SignalR.Hubs;
namespace Microsoft.AspNet.SignalR
{
/// <summary>
/// Apply to Hubs and Hub methods to authorize client connections to Hubs and authorize client invocations of Hub methods.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "MVC and WebAPI don't seal their AuthorizeAttributes")]
public class AuthorizeAttribute : Attribute, IAuthorizeHubConnection, IAuthorizeHubMethodInvocation
{
private string _roles;
private string[] _rolesSplit = new string[0];
private string _users;
private string[] _usersSplit = new string[0];
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Already somewhat represented by set-only RequiredOutgoing property.")]
protected bool? _requireOutgoing;
/// <summary>
/// Set to false to apply authorization only to the invocations of any of the Hub's server-side methods.
/// This property only affects attributes applied to the Hub class.
/// This property cannot be read.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Must be property because this is an attribute parameter.")]
public bool RequireOutgoing
{
// It is impossible to tell here whether the attribute is being applied to a method or class. This makes
// it impossible to determine whether the value should be true or false when _requireOutgoing is null.
// It is also impossible to have a Nullable attribute parameter type.
get { throw new NotImplementedException(Resources.Error_DoNotReadRequireOutgoing); }
set { _requireOutgoing = value; }
}
/// <summary>
/// Gets or sets the user roles.
/// </summary>
public string Roles
{
get { return _roles ?? String.Empty; }
set
{
_roles = value;
_rolesSplit = SplitString(value);
}
}
/// <summary>
/// Gets or sets the authorized users.
/// </summary>
public string Users
{
get { return _users ?? String.Empty; }
set
{
_users = value;
_usersSplit = SplitString(value);
}
}
/// <summary>
/// Determines whether client is authorized to connect to <see cref="IHub"/>.
/// </summary>
/// <param name="hubDescriptor">Description of the hub client is attempting to connect to.</param>
/// <param name="request">The (re)connect request from the client.</param>
/// <returns>true if the caller is authorized to connect to the hub; otherwise, false.</returns>
public virtual bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
// If RequireOutgoing is explicitly set to false, authorize all connections.
if (_requireOutgoing.HasValue && !_requireOutgoing.Value)
{
return true;
}
return UserAuthorized(request.User);
}
/// <summary>
/// Determines whether client is authorized to invoke the <see cref="IHub"/> method.
/// </summary>
/// <param name="hubIncomingInvokerContext">An <see cref="IHubIncomingInvokerContext"/> providing details regarding the <see cref="IHub"/> method invocation.</param>
/// <param name="appliesToMethod">Indicates whether the interface instance is an attribute applied directly to a method.</param>
/// <returns>true if the caller is authorized to invoke the <see cref="IHub"/> method; otherwise, false.</returns>
public virtual bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
if (hubIncomingInvokerContext == null)
{
throw new ArgumentNullException("hubIncomingInvokerContext");
}
// It is impossible to require outgoing auth at the method level with SignalR's current design.
// Even though this isn't the stage at which outgoing auth would be applied, we want to throw a runtime error
// to indicate when the attribute is being used with obviously incorrect expectations.
// We must explicitly check if _requireOutgoing is true since it is a Nullable type.
if (appliesToMethod && (_requireOutgoing == true))
{
throw new ArgumentException(Resources.Error_MethodLevelOutgoingAuthorization);
}
return UserAuthorized(hubIncomingInvokerContext.Hub.Context.User);
}
/// <summary>
/// When overridden, provides an entry point for custom authorization checks.
/// Called by <see cref="AuthorizeAttribute.AuthorizeHubConnection"/> and <see cref="AuthorizeAttribute.AuthorizeHubMethodInvocation"/>.
/// </summary>
/// <param name="user">The <see cref="System.Security.Principal.IPrincipal"/> for the client being authorize</param>
/// <returns>true if the user is authorized, otherwise, false</returns>
protected virtual bool UserAuthorized(IPrincipal user)
{
if (user == null)
{
return false;
}
if (!user.Identity.IsAuthenticated)
{
return false;
}
if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
{
return false;
}
return true;
}
private static string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
}

@ -1,61 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Configuration
{
internal static class ConfigurationExtensions
{
public const int MissedTimeoutsBeforeClientReconnect = 2;
public const int HeartBeatsPerKeepAlive = 2;
public const int HeartBeatsPerDisconnectTimeout = 6;
/// <summary>
/// The amount of time the client should wait without seeing a keep alive before trying to reconnect.
/// </summary>
public static TimeSpan? KeepAliveTimeout(this IConfigurationManager config)
{
if (config.KeepAlive != null)
{
return TimeSpan.FromTicks(config.KeepAlive.Value.Ticks * MissedTimeoutsBeforeClientReconnect);
}
else
{
return null;
}
}
/// <summary>
/// The interval between successively checking connection states.
/// </summary>
public static TimeSpan HeartbeatInterval(this IConfigurationManager config)
{
if (config.KeepAlive != null)
{
return TimeSpan.FromTicks(config.KeepAlive.Value.Ticks / HeartBeatsPerKeepAlive);
}
else
{
// If KeepAlives are disabled, have the heartbeat run at the same rate it would if the KeepAlive was
// kept at the default value.
return TimeSpan.FromTicks(config.DisconnectTimeout.Ticks / HeartBeatsPerDisconnectTimeout);
}
}
/// <summary>
/// The amount of time a Topic should stay in memory after its last subscriber is removed.
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
public static TimeSpan TopicTtl(this IConfigurationManager config)
{
// If the deep-alive is disabled, don't take it into account when calculating the topic TTL.
var keepAliveTimeout = config.KeepAliveTimeout() ?? TimeSpan.Zero;
// Keep topics alive for twice as long as we let connections to reconnect. (The DisconnectTimeout)
// Also add twice the keep-alive timeout since clients might take a while to notice they are disconnected.
// This should be a very conservative estimate for how long we must wait before considering a topic dead.
return TimeSpan.FromTicks((config.DisconnectTimeout.Ticks + keepAliveTimeout.Ticks) * 2);
}
}
}

@ -1,89 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Configuration
{
public class DefaultConfigurationManager : IConfigurationManager
{
// The below effectively sets the minimum heartbeat to once per second.
// if _minimumKeepAlive != 2 seconds, update the ArguementOutOfRanceExceptionMessage below
private static readonly TimeSpan _minimumKeepAlive = TimeSpan.FromSeconds(2);
// if _minimumKeepAlivesPerDisconnectTimeout != 3, update the ArguementOutOfRanceExceptionMessage below
private const int _minimumKeepAlivesPerDisconnectTimeout = 3;
// if _minimumDisconnectTimeout != 6 seconds, update the ArguementOutOfRanceExceptionMessage below
private static readonly TimeSpan _minimumDisconnectTimeout = TimeSpan.FromTicks(_minimumKeepAlive.Ticks * _minimumKeepAlivesPerDisconnectTimeout);
private bool _keepAliveConfigured;
private TimeSpan? _keepAlive;
private TimeSpan _disconnectTimeout;
public DefaultConfigurationManager()
{
ConnectionTimeout = TimeSpan.FromSeconds(110);
DisconnectTimeout = TimeSpan.FromSeconds(30);
DefaultMessageBufferSize = 1000;
}
// TODO: Should we guard against negative TimeSpans here like everywhere else?
public TimeSpan ConnectionTimeout
{
get;
set;
}
public TimeSpan DisconnectTimeout
{
get
{
return _disconnectTimeout;
}
set
{
if (value < _minimumDisconnectTimeout)
{
throw new ArgumentOutOfRangeException("value", Resources.Error_DisconnectTimeoutMustBeAtLeastSixSeconds);
}
if (_keepAliveConfigured)
{
throw new InvalidOperationException(Resources.Error_DisconnectTimeoutCannotBeConfiguredAfterKeepAlive);
}
_disconnectTimeout = value;
_keepAlive = TimeSpan.FromTicks(_disconnectTimeout.Ticks / _minimumKeepAlivesPerDisconnectTimeout);
}
}
public TimeSpan? KeepAlive
{
get
{
return _keepAlive;
}
set
{
if (value < _minimumKeepAlive)
{
throw new ArgumentOutOfRangeException("value", Resources.Error_KeepAliveMustBeGreaterThanTwoSeconds);
}
if (value > TimeSpan.FromTicks(_disconnectTimeout.Ticks / _minimumKeepAlivesPerDisconnectTimeout))
{
throw new ArgumentOutOfRangeException("value", Resources.Error_KeepAliveMustBeNoMoreThanAThirdOfTheDisconnectTimeout);
}
_keepAlive = value;
_keepAliveConfigured = true;
}
}
public int DefaultMessageBufferSize
{
get;
set;
}
}
}

@ -1,33 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Configuration
{
/// <summary>
/// Provides access to server configuration.
/// </summary>
public interface IConfigurationManager
{
/// <summary>
/// Gets or sets a <see cref="TimeSpan"/> representing the amount of time to leave a connection open before timing out.
/// </summary>
TimeSpan ConnectionTimeout { get; set; }
/// <summary>
/// Gets or sets a <see cref="TimeSpan"/> representing the amount of time to wait after a connection goes away before raising the disconnect event.
/// </summary>
TimeSpan DisconnectTimeout { get; set; }
/// <summary>
/// Gets or sets a <see cref="TimeSpan"/> representing the amount of time between send keep alive messages.
/// If enabled, this value must be at least two seconds. Set to null to disable.
/// </summary>
TimeSpan? KeepAlive { get; set; }
/// <summary>
/// Gets of sets the number of messages to buffer for a specific signal.
/// </summary>
int DefaultMessageBufferSize { get; set; }
}
}

@ -1,24 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR
{
public class ConnectionConfiguration
{
// Resolver isn't set to GlobalHost.DependencyResolver in the ctor because it is lazily created.
private IDependencyResolver _resolver;
/// <summary>
/// The dependency resolver to use for the hub connection.
/// </summary>
public IDependencyResolver Resolver
{
get { return _resolver ?? GlobalHost.DependencyResolver; }
set { _resolver = value; }
}
/// <summary>
/// Determines if browsers can make cross domain requests to SignalR endpoints.
/// </summary>
public bool EnableCrossDomain { get; set; }
}
}

@ -1,60 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR
{
public static class ConnectionExtensions
{
/// <summary>
/// Sends a message to all connections subscribed to the specified signal. An example of signal may be a
/// specific connection id.
/// </summary>
/// <param name="connection">The connection</param>
/// <param name="connectionId">The connectionId to send to.</param>
/// <param name="value">The value to publish.</param>
/// <param name="excludeConnectionIds">The list of connection ids to exclude</param>
/// <returns>A task that represents when the broadcast is complete.</returns>
public static Task Send(this IConnection connection, string connectionId, object value, params string[] excludeConnectionIds)
{
if (connection == null)
{
throw new ArgumentNullException("connection");
}
if (string.IsNullOrEmpty(connectionId))
{
throw new ArgumentException(Resources.Error_ArgumentNullOrEmpty, "connectionId");
}
var message = new ConnectionMessage(PrefixHelper.GetConnectionId(connectionId),
value,
PrefixHelper.GetPrefixedConnectionIds(excludeConnectionIds));
return connection.Send(message);
}
/// <summary>
/// Broadcasts a value to all connections, excluding the connection ids specified.
/// </summary>
/// <param name="connection">The connection</param>
/// <param name="value">The value to broadcast.</param>
/// <param name="excludeConnectionIds">The list of connection ids to exclude</param>
/// <returns>A task that represents when the broadcast is complete.</returns>
public static Task Broadcast(this IConnection connection, object value, params string[] excludeConnectionIds)
{
if (connection == null)
{
throw new ArgumentNullException("connection");
}
var message = new ConnectionMessage(connection.DefaultSignal,
value,
PrefixHelper.GetPrefixedConnectionIds(excludeConnectionIds));
return connection.Send(message);
}
}
}

@ -1,51 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR
{
/// <summary>
/// A message sent to one more connections.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "Messags are never compared, just used as data.")]
public struct ConnectionMessage
{
/// <summary>
/// The signal to this message should be sent to. Connections subscribed to this signal
/// will receive the message payload.
/// </summary>
public string Signal { get; private set; }
/// <summary>
/// The payload of the message.
/// </summary>
public object Value { get; private set; }
/// <summary>
/// Represents a list of signals that should be used to filter what connections
/// receive this message.
/// </summary>
public IList<string> ExcludedSignals { get; private set; }
public ConnectionMessage(string signal, object value)
: this(signal, value, ListHelper<string>.Empty)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ConnectionMessage"/> class.
/// </summary>
/// <param name="signal">The signal</param>
/// <param name="value">The payload of the message</param>
/// <param name="excludedSignals">The signals to exclude.</param>
public ConnectionMessage(string signal, object value, IList<string> excludedSignals)
: this()
{
Signal = signal;
Value = value;
ExcludedSignals = excludedSignals;
}
}
}

@ -1,28 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR
{
public class Cookie
{
public Cookie(string name, string value)
: this(name, value, String.Empty, String.Empty)
{
}
public Cookie(string name, string value, string domain, string path)
{
Name = name;
Value = value;
Domain = domain;
Path = path;
}
public string Name { get; private set; }
public string Domain { get; private set; }
public string Path { get; private set; }
public string Value { get; private set; }
}
}

@ -1,231 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Threading;
using Microsoft.AspNet.SignalR.Configuration;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.AspNet.SignalR.Infrastructure;
using Microsoft.AspNet.SignalR.Json;
using Microsoft.AspNet.SignalR.Messaging;
using Microsoft.AspNet.SignalR.Tracing;
using Microsoft.AspNet.SignalR.Transports;
namespace Microsoft.AspNet.SignalR
{
public class DefaultDependencyResolver : IDependencyResolver
{
private readonly Dictionary<Type, IList<Func<object>>> _resolvers = new Dictionary<Type, IList<Func<object>>>();
private readonly HashSet<IDisposable> _trackedDisposables = new HashSet<IDisposable>();
private int _disposed;
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "It's easiest")]
public DefaultDependencyResolver()
{
RegisterDefaultServices();
// Hubs
RegisterHubExtensions();
}
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "The resolver is the class that does the most coupling by design.")]
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The resolver disposes dependencies on Dispose.")]
private void RegisterDefaultServices()
{
var traceManager = new Lazy<TraceManager>(() => new TraceManager());
Register(typeof(ITraceManager), () => traceManager.Value);
var serverIdManager = new ServerIdManager();
Register(typeof(IServerIdManager), () => serverIdManager);
var serverMessageHandler = new Lazy<IServerCommandHandler>(() => new ServerCommandHandler(this));
Register(typeof(IServerCommandHandler), () => serverMessageHandler.Value);
var newMessageBus = new Lazy<IMessageBus>(() => new MessageBus(this));
Register(typeof(IMessageBus), () => newMessageBus.Value);
var stringMinifier = new Lazy<IStringMinifier>(() => new StringMinifier());
Register(typeof(IStringMinifier), () => stringMinifier.Value);
var serializer = new Lazy<JsonNetSerializer>();
Register(typeof(IJsonSerializer), () => serializer.Value);
var transportManager = new Lazy<TransportManager>(() => new TransportManager(this));
Register(typeof(ITransportManager), () => transportManager.Value);
var configurationManager = new DefaultConfigurationManager();
Register(typeof(IConfigurationManager), () => configurationManager);
var transportHeartbeat = new Lazy<TransportHeartbeat>(() => new TransportHeartbeat(this));
Register(typeof(ITransportHeartbeat), () => transportHeartbeat.Value);
var connectionManager = new Lazy<ConnectionManager>(() => new ConnectionManager(this));
Register(typeof(IConnectionManager), () => connectionManager.Value);
var ackHandler = new Lazy<AckHandler>();
Register(typeof(IAckHandler), () => ackHandler.Value);
var perfCounterWriter = new Lazy<PerformanceCounterManager>(() => new PerformanceCounterManager(this));
Register(typeof(IPerformanceCounterManager), () => perfCounterWriter.Value);
var protectedData = new DefaultProtectedData();
Register(typeof(IProtectedData), () => protectedData);
}
private void RegisterHubExtensions()
{
var methodDescriptorProvider = new Lazy<ReflectedMethodDescriptorProvider>();
Register(typeof(IMethodDescriptorProvider), () => methodDescriptorProvider.Value);
var hubDescriptorProvider = new Lazy<ReflectedHubDescriptorProvider>(() => new ReflectedHubDescriptorProvider(this));
Register(typeof(IHubDescriptorProvider), () => hubDescriptorProvider.Value);
var parameterBinder = new Lazy<DefaultParameterResolver>();
Register(typeof(IParameterResolver), () => parameterBinder.Value);
var activator = new Lazy<DefaultHubActivator>(() => new DefaultHubActivator(this));
Register(typeof(IHubActivator), () => activator.Value);
var hubManager = new Lazy<DefaultHubManager>(() => new DefaultHubManager(this));
Register(typeof(IHubManager), () => hubManager.Value);
var proxyGenerator = new Lazy<DefaultJavaScriptProxyGenerator>(() => new DefaultJavaScriptProxyGenerator(this));
Register(typeof(IJavaScriptProxyGenerator), () => proxyGenerator.Value);
var requestParser = new Lazy<HubRequestParser>();
Register(typeof(IHubRequestParser), () => requestParser.Value);
var assemblyLocator = new Lazy<DefaultAssemblyLocator>(() => new DefaultAssemblyLocator());
Register(typeof(IAssemblyLocator), () => assemblyLocator.Value);
// Setup the default hub pipeline
var dispatcher = new Lazy<IHubPipeline>(() => new HubPipeline().AddModule(new AuthorizeModule()));
Register(typeof(IHubPipeline), () => dispatcher.Value);
Register(typeof(IHubPipelineInvoker), () => dispatcher.Value);
}
public virtual object GetService(Type serviceType)
{
if (serviceType == null)
{
throw new ArgumentNullException("serviceType");
}
IList<Func<object>> activators;
if (_resolvers.TryGetValue(serviceType, out activators))
{
if (activators.Count == 0)
{
return null;
}
if (activators.Count > 1)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_MultipleActivatorsAreaRegisteredCallGetServices, serviceType.FullName));
}
return Track(activators[0]);
}
return null;
}
public virtual IEnumerable<object> GetServices(Type serviceType)
{
IList<Func<object>> activators;
if (_resolvers.TryGetValue(serviceType, out activators))
{
if (activators.Count == 0)
{
return null;
}
return activators.Select(Track).ToList();
}
return null;
}
public virtual void Register(Type serviceType, Func<object> activator)
{
IList<Func<object>> activators;
if (!_resolvers.TryGetValue(serviceType, out activators))
{
activators = new List<Func<object>>();
_resolvers.Add(serviceType, activators);
}
else
{
activators.Clear();
}
activators.Add(activator);
}
public virtual void Register(Type serviceType, IEnumerable<Func<object>> activators)
{
if (activators == null)
{
throw new ArgumentNullException("activators");
}
IList<Func<object>> list;
if (!_resolvers.TryGetValue(serviceType, out list))
{
list = new List<Func<object>>();
_resolvers.Add(serviceType, list);
}
else
{
list.Clear();
}
foreach (var a in activators)
{
list.Add(a);
}
}
private object Track(Func<object> creator)
{
object obj = creator();
if (_disposed == 0)
{
var disposable = obj as IDisposable;
if (disposable != null)
{
lock (_trackedDisposables)
{
if (_disposed == 0)
{
_trackedDisposables.Add(disposable);
}
}
}
}
return obj;
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (Interlocked.Exchange(ref _disposed, 1) == 0)
{
lock (_trackedDisposables)
{
foreach (var d in _trackedDisposables)
{
d.Dispose();
}
_trackedDisposables.Clear();
}
}
}
}
public void Dispose()
{
Dispose(true);
}
}
}

@ -1,61 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNet.SignalR
{
public static class DependencyResolverExtensions
{
public static T Resolve<T>(this IDependencyResolver resolver)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}
return (T)resolver.GetService(typeof(T));
}
public static object Resolve(this IDependencyResolver resolver, Type type)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}
if (type == null)
{
throw new ArgumentNullException("type");
}
return resolver.GetService(type);
}
public static IEnumerable<T> ResolveAll<T>(this IDependencyResolver resolver)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}
return resolver.GetServices(typeof(T)).Cast<T>();
}
public static IEnumerable<object> ResolveAll(this IDependencyResolver resolver, Type type)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}
if (type == null)
{
throw new ArgumentNullException("type");
}
return resolver.GetServices(type);
}
}
}

@ -1,66 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using Microsoft.AspNet.SignalR.Configuration;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR
{
/// <summary>
/// Provides access to default host information.
/// </summary>
public static class GlobalHost
{
private static readonly Lazy<IDependencyResolver> _defaultResolver = new Lazy<IDependencyResolver>(() => new DefaultDependencyResolver());
private static IDependencyResolver _resolver;
/// <summary>
/// Gets or sets the the default <see cref="IDependencyResolver"/>
/// </summary>
public static IDependencyResolver DependencyResolver
{
get
{
return _resolver ?? _defaultResolver.Value;
}
set
{
_resolver = value;
}
}
/// <summary>
/// Gets the default <see cref="IConfigurationManager"/>
/// </summary>
public static IConfigurationManager Configuration
{
get
{
return DependencyResolver.Resolve<IConfigurationManager>();
}
}
/// <summary>
/// Gets the default <see cref="IConnectionManager"/>
/// </summary>
public static IConnectionManager ConnectionManager
{
get
{
return DependencyResolver.Resolve<IConnectionManager>();
}
}
/// <summary>
///
/// </summary>
public static IHubPipeline HubPipeline
{
get
{
return DependencyResolver.Resolve<IHubPipeline>();
}
}
}
}

@ -1,117 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Infrastructure;
using Microsoft.AspNet.SignalR.Messaging;
namespace Microsoft.AspNet.SignalR
{
/// <summary>
/// The default <see cref="IGroupManager"/> implementation.
/// </summary>
public class GroupManager : IConnectionGroupManager
{
private readonly IConnection _connection;
private readonly string _groupPrefix;
/// <summary>
/// Initializes a new instance of the <see cref="GroupManager"/> class.
/// </summary>
/// <param name="connection">The <see cref="IConnection"/> this group resides on.</param>
/// <param name="groupPrefix">The prefix for this group. Either a <see cref="Microsoft.AspNet.SignalR.Hubs.IHub"/> name or <see cref="PersistentConnection"/> type name.</param>
public GroupManager(IConnection connection, string groupPrefix)
{
if (connection == null)
{
throw new ArgumentNullException("connection");
}
_connection = connection;
_groupPrefix = groupPrefix;
}
/// <summary>
/// Sends a value to the specified group.
/// </summary>
/// <param name="groupName">The name of the group.</param>
/// <param name="value">The value to send.</param>
/// <param name="excludeConnectionIds">The list of connection ids to exclude</param>
/// <returns>A task that represents when send is complete.</returns>
public Task Send(string groupName, object value, params string[] excludeConnectionIds)
{
if (string.IsNullOrEmpty(groupName))
{
throw new ArgumentException((Resources.Error_ArgumentNullOrEmpty), "groupName");
}
var qualifiedName = CreateQualifiedName(groupName);
var message = new ConnectionMessage(qualifiedName,
value,
PrefixHelper.GetPrefixedConnectionIds(excludeConnectionIds));
return _connection.Send(message);
}
/// <summary>
/// Adds a connection to the specified group.
/// </summary>
/// <param name="connectionId">The connection id to add to the group.</param>
/// <param name="groupName">The name of the group</param>
/// <returns>A task that represents the connection id being added to the group.</returns>
public Task Add(string connectionId, string groupName)
{
if (connectionId == null)
{
throw new ArgumentNullException("connectionId");
}
if (groupName == null)
{
throw new ArgumentNullException("groupName");
}
var command = new Command
{
CommandType = CommandType.AddToGroup,
Value = CreateQualifiedName(groupName),
WaitForAck = true
};
return _connection.Send(connectionId, command);
}
/// <summary>
/// Removes a connection from the specified group.
/// </summary>
/// <param name="connectionId">The connection id to remove from the group.</param>
/// <param name="groupName">The name of the group</param>
/// <returns>A task that represents the connection id being removed from the group.</returns>
public Task Remove(string connectionId, string groupName)
{
if (connectionId == null)
{
throw new ArgumentNullException("connectionId");
}
if (groupName == null)
{
throw new ArgumentNullException("groupName");
}
var command = new Command
{
CommandType = CommandType.RemoveFromGroup,
Value = CreateQualifiedName(groupName),
WaitForAck = true
};
return _connection.Send(connectionId, command);
}
private string CreateQualifiedName(string groupName)
{
return _groupPrefix + "." + groupName;
}
}
}

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hosting
{
public static class HostConstants
{
/// <summary>
/// The host should set this if they need to enable debug mode
/// </summary>
public static readonly string DebugMode = "debugMode";
/// <summary>
/// The host should set this is web sockets can be supported
/// </summary>
public static readonly string SupportsWebSockets = "supportsWebSockets";
/// <summary>
/// The host should set this if the web socket url is different
/// </summary>
public static readonly string WebSocketServerUrl = "webSocketServerUrl";
public static readonly string ShutdownToken = "shutdownToken";
public static readonly string InstanceName = "instanceName";
}
}

@ -1,21 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.SignalR.Hosting
{
public class HostContext
{
public IRequest Request { get; private set; }
public IResponse Response { get; private set; }
public IDictionary<string, object> Items { get; private set; }
public HostContext(IRequest request, IResponse response)
{
Request = request;
Response = response;
Items = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}
}
}

@ -1,53 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Threading;
namespace Microsoft.AspNet.SignalR.Hosting
{
public static class HostContextExtensions
{
public static T GetValue<T>(this HostContext context, string key)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
object value;
if (context.Items.TryGetValue(key, out value))
{
return (T)value;
}
return default(T);
}
public static bool IsDebuggingEnabled(this HostContext context)
{
return context.GetValue<bool>(HostConstants.DebugMode);
}
public static bool SupportsWebSockets(this HostContext context)
{
// The server needs to implement IWebSocketRequest for websockets to be supported.
// It also needs to set the flag in the items collection.
return context.GetValue<bool>(HostConstants.SupportsWebSockets) &&
context.Request is IWebSocketRequest;
}
public static string WebSocketServerUrl(this HostContext context)
{
return context.GetValue<string>(HostConstants.WebSocketServerUrl);
}
public static CancellationToken HostShutdownToken(this HostContext context)
{
return context.GetValue<CancellationToken>(HostConstants.ShutdownToken);
}
public static string InstanceName(this HostContext context)
{
return context.GetValue<string>(HostConstants.InstanceName);
}
}
}

@ -1,55 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Threading;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR.Hosting
{
public static class HostDependencyResolverExtensions
{
public static void InitializeHost(this IDependencyResolver resolver, string instanceName, CancellationToken hostShutdownToken)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}
if (String.IsNullOrEmpty(instanceName))
{
throw new ArgumentNullException("instanceName");
}
// Performance counters are broken on mono so just skip this step
if (!MonoUtility.IsRunningMono)
{
// Initialize the performance counters
resolver.InitializePerformanceCounters(instanceName, hostShutdownToken);
}
// Dispose the dependency resolver on host shut down (cleanly)
resolver.InitializeResolverDispose(hostShutdownToken);
}
private static void InitializePerformanceCounters(this IDependencyResolver resolver, string instanceName, CancellationToken hostShutdownToken)
{
var counters = resolver.Resolve<IPerformanceCounterManager>();
if (counters != null)
{
counters.Initialize(instanceName, hostShutdownToken);
}
}
private static void InitializeResolverDispose(this IDependencyResolver resolver, CancellationToken hostShutdownToken)
{
// TODO: Guard against multiple calls to this
// When the host triggers the shutdown token, dispose the resolver
hostShutdownToken.SafeRegister(state =>
{
((IDependencyResolver)state).Dispose();
},
resolver);
}
}
}

@ -1,49 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNet.SignalR.Hosting
{
/// <summary>
/// Represents a connection to the client.
/// </summary>
public interface IResponse
{
/// <summary>
/// Gets a cancellation token that represents the client's lifetime.
/// </summary>
CancellationToken CancellationToken { get; }
/// <summary>
/// Gets or sets the status code of the response.
/// </summary>
int StatusCode { get; set; }
/// <summary>
/// Gets or sets the content type of the response.
/// </summary>
string ContentType { get; set; }
/// <summary>
/// Writes buffered data.
/// </summary>
/// <param name="data">The data to write to the buffer.</param>
void Write(ArraySegment<byte> data);
/// <summary>
/// Flushes the buffered response to the client.
/// </summary>
/// <returns>A task that represents when the data has been flushed.</returns>
Task Flush();
/// <summary>
/// Closes the connection to the client.
/// </summary>
/// <returns>A task that represents when the connection is closed.</returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "End", Justification = "Ends the response thus the name is appropriate.")]
Task End();
}
}

@ -1,48 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNet.SignalR.Hosting
{
/// <summary>
/// Represents a web socket.
/// </summary>
public interface IWebSocket
{
/// <summary>
/// Invoked when data is sent over the websocket
/// </summary>
Action<string> OnMessage { get; set; }
/// <summary>
/// Invoked when the websocket closes
/// </summary>
Action OnClose { get; set; }
/// <summary>
/// Invoked when there is an error
/// </summary>
Action<Exception> OnError { get; set; }
/// <summary>
/// Sends data over the websocket.
/// </summary>
/// <param name="value">The value to send.</param>
/// <returns>A <see cref="Task"/> that represents the send is complete.</returns>
Task Send(string value);
/// <summary>
/// Sends a chunk of data over the websocket ("endOfMessage" flag set to false.)
/// </summary>
/// <param name="message"></param>
/// <returns>A <see cref="Task"/> that represents the send is complete.</returns>
Task SendChunk(ArraySegment<byte> message);
/// <summary>
/// Sends a zero byte data chunk with the "endOfMessage" flag set to true.
/// </summary>
/// <returns>A <see cref="Task"/> that represents the flush is complete.</returns>
Task Flush();
}
}

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNet.SignalR.Hosting
{
public interface IWebSocketRequest : IRequest
{
/// <summary>
/// Accepts an websocket request using the specified user function.
/// </summary>
/// <param name="callback">The callback that fires when the websocket is ready.</param>
/// <param name="initTask">The task that completes when the websocket transport is ready.</param>
Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback, Task initTask);
}
}

@ -1,52 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Globalization;
namespace Microsoft.AspNet.SignalR.Hosting
{
/// <summary>
/// Responsible for creating <see cref="PersistentConnection"/> instances.
/// </summary>
public class PersistentConnectionFactory
{
private readonly IDependencyResolver _resolver;
/// <summary>
/// Creates a new instance of the <see cref="PersistentConnectionFactory"/> class.
/// </summary>
/// <param name="resolver">The dependency resolver to use for when creating the <see cref="PersistentConnection"/>.</param>
public PersistentConnectionFactory(IDependencyResolver resolver)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}
_resolver = resolver;
}
/// <summary>
/// Creates an instance of the specified type using the dependency resolver or the type's default constructor.
/// </summary>
/// <param name="connectionType">The type of <see cref="PersistentConnection"/> to create.</param>
/// <returns>An instance of a <see cref="PersistentConnection"/>. </returns>
public PersistentConnection CreateInstance(Type connectionType)
{
if (connectionType == null)
{
throw new ArgumentNullException("connectionType");
}
var connection = (_resolver.Resolve(connectionType) ??
Activator.CreateInstance(connectionType)) as PersistentConnection;
if (connection == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_IsNotA, connectionType.FullName, typeof(PersistentConnection).FullName));
}
return connection;
}
}
}

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Hosting
{
internal static class RequestExtensions
{
/// <summary>
/// Gets a value from the QueryString, and if it's null or empty, gets it from the Form instead.
/// </summary>
public static string QueryStringOrForm(this IRequest request, string key)
{
var value = request.QueryString[key];
if (String.IsNullOrEmpty(value))
{
value = request.Form[key];
}
return value;
}
}
}

@ -1,32 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.AspNet.SignalR.Hosting
{
/// <summary>
/// Extension methods for <see cref="IResponse"/>.
/// </summary>
public static class ResponseExtensions
{
/// <summary>
/// Closes the connection to a client with optional data.
/// </summary>
/// <param name="response">The <see cref="IResponse"/>.</param>
/// <param name="data">The data to write to the connection.</param>
/// <returns>A task that represents when the connection is closed.</returns>
public static Task End(this IResponse response, string data)
{
if (response == null)
{
throw new ArgumentNullException("response");
}
var bytes = Encoding.UTF8.GetBytes(data);
response.Write(new ArraySegment<byte>(bytes, 0, bytes.Length));
return response.End();
}
}
}

@ -1,72 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Hubs;
namespace Microsoft.AspNet.SignalR
{
/// <summary>
/// Provides methods that communicate with SignalR connections that connected to a <see cref="Hub"/>.
/// </summary>
public abstract class Hub : IHub
{
protected Hub()
{
Clients = new HubConnectionContext();
Clients.All = new NullClientProxy();
Clients.Others = new NullClientProxy();
Clients.Caller = new NullClientProxy();
}
/// <summary>
///
/// </summary>
public HubConnectionContext Clients { get; set; }
/// <summary>
/// Provides information about the calling client.
/// </summary>
public HubCallerContext Context { get; set; }
/// <summary>
/// The group manager for this hub instance.
/// </summary>
public IGroupManager Groups { get; set; }
/// <summary>
/// Called when a connection disconnects from this hub instance.
/// </summary>
/// <returns>A <see cref="Task"/></returns>
public virtual Task OnDisconnected()
{
return TaskAsyncHelper.Empty;
}
/// <summary>
/// Called when the connection connects to this hub instance.
/// </summary>
/// <returns>A <see cref="Task"/></returns>
public virtual Task OnConnected()
{
return TaskAsyncHelper.Empty;
}
/// <summary>
/// Called when the connection reconnects to this hub instance.
/// </summary>
/// <returns>A <see cref="Task"/></returns>
public virtual Task OnReconnected()
{
return TaskAsyncHelper.Empty;
}
protected virtual void Dispose(bool disposing)
{
}
public void Dispose()
{
Dispose(true);
}
}
}

@ -1,24 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR
{
public class HubConfiguration : ConnectionConfiguration
{
/// <summary>
/// Determines whether JavaScript proxies for the server-side hubs should be auto generated at {Path}/hubs.
/// Defaults to true.
/// </summary>
public bool EnableJavaScriptProxies { get; set; }
/// <summary>
/// Determines whether detailed exceptions thrown in Hub methods get reported back the invoking client.
/// Defaults to false.
/// </summary>
public bool EnableDetailedErrors { get; set; }
public HubConfiguration()
{
EnableJavaScriptProxies = true;
}
}
}

@ -1,47 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// A description of a client-side hub method invocation.
/// </summary>
public class ClientHubInvocation
{
/// <summary>
/// The signal that clients receiving this invocation are subscribed to.
/// </summary>
[JsonIgnore]
public string Target { get; set; }
/// <summary>
/// The name of the hub that the method being invoked belongs to.
/// </summary>
[JsonProperty("H")]
public string Hub { get; set; }
/// <summary>
/// The name of the client-side hub method be invoked.
/// </summary>
[JsonProperty("M")]
public string Method { get; set; }
/// <summary>
/// The argument list the client-side hub method will be called with.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Type is used for serialization.")]
[JsonProperty("A")]
public object[] Args { get; set; }
/// <summary>
/// A key-value store representing the hub state on the server that has changed since the last time the hub
/// state was sent to the client.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Type is used for serialization.")]
[JsonProperty("S", NullValueHandling = NullValueHandling.Ignore)]
public IDictionary<string, object> State { get; set; }
}
}

@ -1,44 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class ClientProxy : DynamicObject, IClientProxy
{
private readonly Func<string, ClientHubInvocation, IList<string>, Task> _send;
private readonly string _hubName;
private readonly IList<string> _exclude;
public ClientProxy(Func<string, ClientHubInvocation, IList<string>, Task> send, string hubName, IList<string> exclude)
{
_send = send;
_hubName = hubName;
_exclude = exclude;
}
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Binder is passed in by the DLR")]
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = Invoke(binder.Name, args);
return true;
}
public Task Invoke(string method, params object[] args)
{
var invocation = new ClientHubInvocation
{
Hub = _hubName,
Method = method,
Args = args
};
return _send(PrefixHelper.GetHubName(_hubName), invocation, _exclude);
}
}
}

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class ConnectionIdProxy : SignalProxy
{
public ConnectionIdProxy(Func<string, ClientHubInvocation, IList<string>, Task> send, string signal, string hubName, params string[] exclude) :
base(send, signal, hubName, PrefixHelper.HubConnectionIdPrefix, exclude)
{
}
}
}

@ -1,16 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class DefaultAssemblyLocator : IAssemblyLocator
{
public virtual IList<Assembly> GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
}
}

@ -1,32 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class DefaultHubActivator : IHubActivator
{
private readonly IDependencyResolver _resolver;
public DefaultHubActivator(IDependencyResolver resolver)
{
_resolver = resolver;
}
public IHub Create(HubDescriptor descriptor)
{
if (descriptor == null)
{
throw new ArgumentNullException("descriptor");
}
if(descriptor.HubType == null)
{
return null;
}
object hub = _resolver.Resolve(descriptor.HubType) ?? Activator.CreateInstance(descriptor.HubType);
return hub as IHub;
}
}
}

@ -1,211 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.AspNet.SignalR.Json;
using Newtonsoft.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class DefaultJavaScriptProxyGenerator : IJavaScriptProxyGenerator
{
private static readonly Lazy<string> _templateFromResource = new Lazy<string>(GetTemplateFromResource);
private static readonly Type[] _numberTypes = new[] { typeof(byte), typeof(short), typeof(int), typeof(long), typeof(float), typeof(decimal), typeof(double) };
private static readonly Type[] _dateTypes = new[] { typeof(DateTime), typeof(DateTimeOffset) };
private const string ScriptResource = "Microsoft.AspNet.SignalR.Scripts.hubs.js";
private readonly IHubManager _manager;
private readonly IJavaScriptMinifier _javaScriptMinifier;
private readonly Lazy<string> _generatedTemplate;
public DefaultJavaScriptProxyGenerator(IDependencyResolver resolver) :
this(resolver.Resolve<IHubManager>(),
resolver.Resolve<IJavaScriptMinifier>())
{
}
public DefaultJavaScriptProxyGenerator(IHubManager manager, IJavaScriptMinifier javaScriptMinifier)
{
_manager = manager;
_javaScriptMinifier = javaScriptMinifier ?? NullJavaScriptMinifier.Instance;
_generatedTemplate = new Lazy<string>(() => GenerateProxy(_manager, _javaScriptMinifier, includeDocComments: false));
}
public string GenerateProxy(string serviceUrl)
{
serviceUrl = JavaScriptEncode(serviceUrl);
var generateProxy = _generatedTemplate.Value;
return generateProxy.Replace("{serviceUrl}", serviceUrl);
}
public string GenerateProxy(string serviceUrl, bool includeDocComments)
{
serviceUrl = JavaScriptEncode(serviceUrl);
string generateProxy = GenerateProxy(_manager, _javaScriptMinifier, includeDocComments);
return generateProxy.Replace("{serviceUrl}", serviceUrl);
}
private static string GenerateProxy(IHubManager hubManager, IJavaScriptMinifier javaScriptMinifier, bool includeDocComments)
{
string script = _templateFromResource.Value;
var hubs = new StringBuilder();
var first = true;
foreach (var descriptor in hubManager.GetHubs().OrderBy(h => h.Name))
{
if (!first)
{
hubs.AppendLine(";");
hubs.AppendLine();
hubs.Append(" ");
}
GenerateType(hubManager, hubs, descriptor, includeDocComments);
first = false;
}
if (hubs.Length > 0)
{
hubs.Append(";");
}
script = script.Replace("/*hubs*/", hubs.ToString());
return javaScriptMinifier.Minify(script);
}
private static void GenerateType(IHubManager hubManager, StringBuilder sb, HubDescriptor descriptor, bool includeDocComments)
{
// Get only actions with minimum number of parameters.
var methods = GetMethods(hubManager, descriptor);
var hubName = GetDescriptorName(descriptor);
sb.AppendFormat(" proxies.{0} = this.createHubProxy('{1}'); ", hubName, hubName).AppendLine();
sb.AppendFormat(" proxies.{0}.client = {{ }};", hubName).AppendLine();
sb.AppendFormat(" proxies.{0}.server = {{", hubName);
bool first = true;
foreach (var method in methods)
{
if (!first)
{
sb.Append(",").AppendLine();
}
GenerateMethod(sb, method, includeDocComments, hubName);
first = false;
}
sb.AppendLine();
sb.Append(" }");
}
private static string GetDescriptorName(Descriptor descriptor)
{
if (descriptor == null)
{
throw new ArgumentNullException("descriptor");
}
string name = descriptor.Name;
// If the name was not specified then do not camel case
if (!descriptor.NameSpecified)
{
name = JsonUtility.CamelCase(name);
}
return name;
}
private static IEnumerable<MethodDescriptor> GetMethods(IHubManager manager, HubDescriptor descriptor)
{
return from method in manager.GetHubMethods(descriptor.Name)
group method by method.Name into overloads
let oload = (from overload in overloads
orderby overload.Parameters.Count
select overload).FirstOrDefault()
orderby oload.Name
select oload;
}
private static void GenerateMethod(StringBuilder sb, MethodDescriptor method, bool includeDocComments, string hubName)
{
var parameterNames = method.Parameters.Select(p => p.Name).ToList();
sb.AppendLine();
sb.AppendFormat(" {0}: function ({1}) {{", GetDescriptorName(method), Commas(parameterNames)).AppendLine();
if (includeDocComments)
{
sb.AppendFormat(Resources.DynamicComment_CallsMethodOnServerSideDeferredPromise, method.Name, method.Hub.Name).AppendLine();
var parameterDoc = method.Parameters.Select(p => String.Format(CultureInfo.CurrentCulture, Resources.DynamicComment_ServerSideTypeIs, p.Name, MapToJavaScriptType(p.ParameterType), p.ParameterType)).ToList();
if (parameterDoc.Any())
{
sb.AppendLine(String.Join(Environment.NewLine, parameterDoc));
}
}
sb.AppendFormat(" return proxies.{0}.invoke.apply(proxies.{0}, $.merge([\"{1}\"], $.makeArray(arguments)));", hubName, method.Name).AppendLine();
sb.Append(" }");
}
private static string MapToJavaScriptType(Type type)
{
if (!type.IsPrimitive && !(type == typeof(string)))
{
return "Object";
}
if (type == typeof(string))
{
return "String";
}
if (_numberTypes.Contains(type))
{
return "Number";
}
if (typeof(IEnumerable).IsAssignableFrom(type))
{
return "Array";
}
if (_dateTypes.Contains(type))
{
return "Date";
}
return String.Empty;
}
private static string Commas(IEnumerable<string> values)
{
return Commas(values, v => v);
}
private static string Commas<T>(IEnumerable<T> values, Func<T, string> selector)
{
return String.Join(", ", values.Select(selector));
}
private static string GetTemplateFromResource()
{
using (Stream resourceStream = typeof(DefaultJavaScriptProxyGenerator).Assembly.GetManifestResourceStream(ScriptResource))
{
var reader = new StreamReader(resourceStream);
return reader.ReadToEnd();
}
}
private static string JavaScriptEncode(string value)
{
value = JsonConvert.SerializeObject(value);
// Remove the quotes
return value.Substring(1, value.Length - 2);
}
}
}

@ -1,144 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class DynamicDictionary : DynamicObject, IDictionary<string, object>
{
private readonly IDictionary<string, object> _obj;
public DynamicDictionary(IDictionary<string, object> obj)
{
_obj = obj;
}
public object this[string key]
{
get
{
object result;
_obj.TryGetValue(key, out result);
return Wrap(result);
}
set
{
_obj[key] = Unwrap(value);
}
}
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "The compiler generates calls to invoke this")]
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this[binder.Name];
return true;
}
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "The compiler generates calls to invoke this")]
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this[binder.Name] = value;
return true;
}
public static object Wrap(object value)
{
var obj = value as IDictionary<string, object>;
if (obj != null)
{
return new DynamicDictionary(obj);
}
return value;
}
public static object Unwrap(object value)
{
var dictWrapper = value as DynamicDictionary;
if (dictWrapper != null)
{
return dictWrapper._obj;
}
return value;
}
public void Add(string key, object value)
{
_obj.Add(key, value);
}
public bool ContainsKey(string key)
{
return _obj.ContainsKey(key);
}
public ICollection<string> Keys
{
get { return _obj.Keys; }
}
public bool Remove(string key)
{
return _obj.Remove(key);
}
public bool TryGetValue(string key, out object value)
{
return _obj.TryGetValue(key, out value);
}
public ICollection<object> Values
{
get { return _obj.Values; }
}
public void Add(KeyValuePair<string, object> item)
{
_obj.Add(item);
}
public void Clear()
{
_obj.Clear();
}
public bool Contains(KeyValuePair<string, object> item)
{
return _obj.Contains(item);
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
_obj.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _obj.Count; }
}
public bool IsReadOnly
{
get { return _obj.IsReadOnly; }
}
public bool Remove(KeyValuePair<string, object> item)
{
return _obj.Remove(item);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return _obj.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

@ -1,15 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Globalization;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class EmptyJavaScriptProxyGenerator : IJavaScriptProxyGenerator
{
public string GenerateProxy(string serviceUrl)
{
return String.Format(CultureInfo.InvariantCulture, "throw new Error('{0}');", Resources.Error_JavaScriptProxyDisabled);
}
}
}

@ -1,63 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR.Hubs
{
public static class HubManagerExtensions
{
public static HubDescriptor EnsureHub(this IHubManager hubManager, string hubName, params IPerformanceCounter[] counters)
{
if (hubManager == null)
{
throw new ArgumentNullException("hubManager");
}
if (String.IsNullOrEmpty(hubName))
{
throw new ArgumentNullException("hubName");
}
if (counters == null)
{
throw new ArgumentNullException("counters");
}
var descriptor = hubManager.GetHub(hubName);
if (descriptor == null)
{
for (var i = 0; i < counters.Length; i++)
{
counters[i].Increment();
}
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_HubCouldNotBeResolved, hubName));
}
return descriptor;
}
public static IEnumerable<HubDescriptor> GetHubs(this IHubManager hubManager)
{
if (hubManager == null)
{
throw new ArgumentNullException("hubManager");
}
return hubManager.GetHubs(d => true);
}
public static IEnumerable<MethodDescriptor> GetHubMethods(this IHubManager hubManager, string hubName)
{
if (hubManager == null)
{
throw new ArgumentNullException("hubManager");
}
return hubManager.GetHubMethods(hubName, m => true);
}
}
}

@ -1,30 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Hubs
{
internal static class HubTypeExtensions
{
internal static string GetHubName(this Type type)
{
if (!typeof(IHub).IsAssignableFrom(type))
{
return null;
}
return GetHubAttributeName(type) ?? type.Name;
}
internal static string GetHubAttributeName(this Type type)
{
if (!typeof(IHub).IsAssignableFrom(type))
{
return null;
}
// We can still return null if there is no attribute name
return ReflectionHelper.GetAttributeValue<HubNameAttribute, string>(type, attr => attr.HubName);
}
}
}

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
public static class MethodExtensions
{
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "The condition checks for null parameters")]
public static bool Matches(this MethodDescriptor methodDescriptor, IList<IJsonValue> parameters)
{
if (methodDescriptor == null)
{
throw new ArgumentNullException("methodDescriptor");
}
if ((methodDescriptor.Parameters.Count > 0 && parameters == null)
|| methodDescriptor.Parameters.Count != parameters.Count)
{
return false;
}
return true;
}
}
}

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class GroupProxy : SignalProxy
{
public GroupProxy(Func<string, ClientHubInvocation, IList<string>, Task> send, string signal, string hubName, IList<string> exclude) :
base(send, signal, hubName, PrefixHelper.HubGroupPrefix, exclude)
{
}
}
}

@ -1,71 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Security.Principal;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class HubCallerContext
{
/// <summary>
/// Gets the connection id of the calling client.
/// </summary>
public string ConnectionId { get; private set; }
/// <summary>
/// Gets the cookies for the request.
/// </summary>
public IDictionary<string, Cookie> RequestCookies
{
get
{
return Request.Cookies;
}
}
/// <summary>
/// Gets the headers for the request.
/// </summary>
public NameValueCollection Headers
{
get
{
return Request.Headers;
}
}
/// <summary>
/// Gets the querystring for the request.
/// </summary>
public NameValueCollection QueryString
{
get
{
return Request.QueryString;
}
}
/// <summary>
/// Gets the <see cref="IPrincipal"/> for the request.
/// </summary>
public IPrincipal User
{
get
{
return Request.User;
}
}
/// <summary>
/// Gets the <see cref="IRequest"/> for the current HTTP request.
/// </summary>
public IRequest Request { get; private set; }
public HubCallerContext(IRequest request, string connectionId)
{
ConnectionId = connectionId;
Request = request;
}
}
}

@ -1,111 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Encapsulates all information about an individual SignalR connection for an <see cref="IHub"/>.
/// </summary>
public class HubConnectionContext : IHubConnectionContext
{
private readonly string _hubName;
private readonly string _connectionId;
private readonly Func<string, ClientHubInvocation, IList<string>, Task> _send;
/// <summary>
/// Initializes a new instance of the <see cref="HubConnectionContext"/>.
/// </summary>
public HubConnectionContext()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HubConnectionContext"/>.
/// </summary>
/// <param name="pipelineInvoker">The pipeline invoker.</param>
/// <param name="connection">The connection.</param>
/// <param name="hubName">The hub name.</param>
/// <param name="connectionId">The connection id.</param>
/// <param name="tracker">The connection hub state.</param>
public HubConnectionContext(IHubPipelineInvoker pipelineInvoker, IConnection connection, string hubName, string connectionId, StateChangeTracker tracker)
{
_send = (signal, invocation, exclude) => pipelineInvoker.Send(new HubOutgoingInvokerContext(connection, signal, invocation, exclude));
_connectionId = connectionId;
_hubName = hubName;
Caller = new StatefulSignalProxy(_send, connectionId, PrefixHelper.HubConnectionIdPrefix, hubName, tracker);
All = AllExcept();
Others = AllExcept(connectionId);
}
/// <summary>
/// All connected clients.
/// </summary>
public dynamic All { get; set; }
/// <summary>
/// All connected clients except the calling client.
/// </summary>
public dynamic Others { get; set; }
/// <summary>
/// Represents the calling client.
/// </summary>
public dynamic Caller { get; set; }
/// <summary>
/// Returns a dynamic representation of all clients except the calling client ones specified.
/// </summary>
/// <param name="excludeConnectionIds">The list of connection ids to exclude</param>
/// <returns>A dynamic representation of all clients except the calling client ones specified.</returns>
public dynamic AllExcept(params string[] excludeConnectionIds)
{
return new ClientProxy(_send, _hubName, PrefixHelper.GetPrefixedConnectionIds(excludeConnectionIds));
}
/// <summary>
/// Returns a dynamic representation of all clients in a group except the calling client.
/// </summary>
/// <param name="groupName">The name of the group</param>
/// <returns>A dynamic representation of all clients in a group except the calling client.</returns>
public dynamic OthersInGroup(string groupName)
{
return Group(groupName, _connectionId);
}
/// <summary>
/// Returns a dynamic representation of the specified group.
/// </summary>
/// <param name="groupName">The name of the group</param>
/// <param name="excludeConnectionIds">The list of connection ids to exclude</param>
/// <returns>A dynamic representation of the specified group.</returns>
public dynamic Group(string groupName, params string[] excludeConnectionIds)
{
if (string.IsNullOrEmpty(groupName))
{
throw new ArgumentException(Resources.Error_ArgumentNullOrEmpty, "groupName");
}
return new GroupProxy(_send, groupName, _hubName, PrefixHelper.GetPrefixedConnectionIds(excludeConnectionIds));
}
/// <summary>
/// Returns a dynamic representation of the connection with the specified connectionid.
/// </summary>
/// <param name="connectionId">The connection id</param>
/// <returns>A dynamic representation of the specified client.</returns>
public dynamic Client(string connectionId)
{
if (string.IsNullOrEmpty(connectionId))
{
throw new ArgumentException(Resources.Error_ArgumentNullOrEmpty, "connectionId");
}
return new ConnectionIdProxy(_send, connectionId, _hubName);
}
}
}

@ -1,66 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Microsoft.AspNet.SignalR.Hubs
{
internal class HubContext : IHubContext
{
public HubContext(Func<string, ClientHubInvocation, IList<string>, Task> send, string hubName, IConnection connection)
{
Clients = new ExternalHubConnectionContext(send, hubName);
Groups = new GroupManager(connection, PrefixHelper.GetHubGroupName(hubName));
}
public IHubConnectionContext Clients { get; private set; }
public IGroupManager Groups { get; private set; }
private class ExternalHubConnectionContext : IHubConnectionContext
{
private readonly Func<string, ClientHubInvocation, IList<string>, Task> _send;
private readonly string _hubName;
public ExternalHubConnectionContext(Func<string, ClientHubInvocation, IList<string>, Task> send, string hubName)
{
_send = send;
_hubName = hubName;
All = AllExcept();
}
public dynamic All
{
get;
private set;
}
public dynamic AllExcept(params string[] exclude)
{
return new ClientProxy(_send, _hubName, exclude);
}
public dynamic Group(string groupName, params string[] exclude)
{
if (string.IsNullOrEmpty(groupName))
{
throw new ArgumentException(Resources.Error_ArgumentNullOrEmpty, "groupName");
}
return new GroupProxy(_send, groupName, _hubName, exclude);
}
public dynamic Client(string connectionId)
{
if (string.IsNullOrEmpty(connectionId))
{
throw new ArgumentException(Resources.Error_ArgumentNullOrEmpty, "connectionId");
}
return new ConnectionIdProxy(_send, connectionId, _hubName);
}
}
}
}

@ -1,522 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Hosting;
using Microsoft.AspNet.SignalR.Infrastructure;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Handles all communication over the hubs persistent connection.
/// </summary>
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This dispatcher makes use of many interfaces.")]
public class HubDispatcher : PersistentConnection
{
private const string HubsSuffix = "/hubs";
private readonly List<HubDescriptor> _hubs = new List<HubDescriptor>();
private readonly bool _enableJavaScriptProxies;
private readonly bool _enableDetailedErrors;
private IJavaScriptProxyGenerator _proxyGenerator;
private IHubManager _manager;
private IHubRequestParser _requestParser;
private IParameterResolver _binder;
private IHubPipelineInvoker _pipelineInvoker;
private IPerformanceCounterManager _counters;
private bool _isDebuggingEnabled;
private static readonly MethodInfo _continueWithMethod = typeof(HubDispatcher).GetMethod("ContinueWith", BindingFlags.NonPublic | BindingFlags.Static);
/// <summary>
/// Initializes an instance of the <see cref="HubDispatcher"/> class.
/// </summary>
/// <param name="configuration">Configuration settings determining whether to enable JS proxies and provide clients with detailed hub errors.</param>
public HubDispatcher(HubConfiguration configuration)
{
if (configuration == null)
{
throw new ArgumentNullException("configuration");
}
_enableJavaScriptProxies = configuration.EnableJavaScriptProxies;
_enableDetailedErrors = configuration.EnableDetailedErrors;
}
protected override TraceSource Trace
{
get
{
return TraceManager["SignalR.HubDispatcher"];
}
}
internal override string GroupPrefix
{
get
{
return PrefixHelper.HubGroupPrefix;
}
}
public override void Initialize(IDependencyResolver resolver, HostContext context)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}
if (context == null)
{
throw new ArgumentNullException("context");
}
_proxyGenerator = _enableJavaScriptProxies ? resolver.Resolve<IJavaScriptProxyGenerator>()
: new EmptyJavaScriptProxyGenerator();
_manager = resolver.Resolve<IHubManager>();
_binder = resolver.Resolve<IParameterResolver>();
_requestParser = resolver.Resolve<IHubRequestParser>();
_pipelineInvoker = resolver.Resolve<IHubPipelineInvoker>();
_counters = resolver.Resolve<IPerformanceCounterManager>();
base.Initialize(resolver, context);
}
protected override bool AuthorizeRequest(IRequest request)
{
// Populate _hubs
string data = request.QueryStringOrForm("connectionData");
if (!String.IsNullOrEmpty(data))
{
var clientHubInfo = JsonSerializer.Parse<IEnumerable<ClientHubInfo>>(data);
// If there's any hubs then perform the auth check
if (clientHubInfo != null && clientHubInfo.Any())
{
var hubCache = new Dictionary<string, HubDescriptor>(StringComparer.OrdinalIgnoreCase);
foreach (var hubInfo in clientHubInfo)
{
if (hubCache.ContainsKey(hubInfo.Name))
{
throw new InvalidOperationException(Resources.Error_DuplicateHubs);
}
// Try to find the associated hub type
HubDescriptor hubDescriptor = _manager.EnsureHub(hubInfo.Name,
_counters.ErrorsHubResolutionTotal,
_counters.ErrorsHubResolutionPerSec,
_counters.ErrorsAllTotal,
_counters.ErrorsAllPerSec);
if (_pipelineInvoker.AuthorizeConnect(hubDescriptor, request))
{
// Add this to the list of hub descriptors this connection is interested in
hubCache.Add(hubDescriptor.Name, hubDescriptor);
}
}
_hubs.AddRange(hubCache.Values);
// If we have any hubs in the list then we're authorized
return _hubs.Count > 0;
}
}
return base.AuthorizeRequest(request);
}
/// <summary>
/// Processes the hub's incoming method calls.
/// </summary>
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
HubRequest hubRequest = _requestParser.Parse(data);
// Create the hub
HubDescriptor descriptor = _manager.EnsureHub(hubRequest.Hub,
_counters.ErrorsHubInvocationTotal,
_counters.ErrorsHubInvocationPerSec,
_counters.ErrorsAllTotal,
_counters.ErrorsAllPerSec);
IJsonValue[] parameterValues = hubRequest.ParameterValues;
// Resolve the method
MethodDescriptor methodDescriptor = _manager.GetHubMethod(descriptor.Name, hubRequest.Method, parameterValues);
if (methodDescriptor == null)
{
_counters.ErrorsHubInvocationTotal.Increment();
_counters.ErrorsHubInvocationPerSec.Increment();
// Empty (noop) method descriptor
// Use: Forces the hub pipeline module to throw an error. This error is encapsulated in the HubDispatcher.
// Encapsulating it in the HubDispatcher prevents the error from bubbling up to the transport level.
// Specifically this allows us to return a faulted task (call .fail on client) and to not cause the
// transport to unintentionally fail.
methodDescriptor = new NullMethodDescriptor(hubRequest.Method);
}
// Resolving the actual state object
var tracker = new StateChangeTracker(hubRequest.State);
var hub = CreateHub(request, descriptor, connectionId, tracker, throwIfFailedToCreate: true);
return InvokeHubPipeline(hub, parameterValues, methodDescriptor, hubRequest, tracker)
.ContinueWith(task => hub.Dispose(), TaskContinuationOptions.ExecuteSynchronously);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions are flown to the caller.")]
private Task InvokeHubPipeline(IHub hub,
IJsonValue[] parameterValues,
MethodDescriptor methodDescriptor,
HubRequest hubRequest,
StateChangeTracker tracker)
{
Task<object> piplineInvocation;
try
{
var args = _binder.ResolveMethodParameters(methodDescriptor, parameterValues);
var context = new HubInvokerContext(hub, tracker, methodDescriptor, args);
// Invoke the pipeline and save the task
piplineInvocation = _pipelineInvoker.Invoke(context);
}
catch (Exception ex)
{
piplineInvocation = TaskAsyncHelper.FromError<object>(ex);
}
// Determine if we have a faulted task or not and handle it appropriately.
return piplineInvocation.ContinueWith(task =>
{
if (task.IsFaulted)
{
return ProcessResponse(tracker, result: null, request: hubRequest, error: task.Exception);
}
else if (task.IsCanceled)
{
return ProcessResponse(tracker, result: null, request: hubRequest, error: new OperationCanceledException());
}
else
{
return ProcessResponse(tracker, task.Result, hubRequest, error: null);
}
})
.FastUnwrap();
}
public override Task ProcessRequest(HostContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
// Trim any trailing slashes
string normalized = context.Request.Url.LocalPath.TrimEnd('/');
if (normalized.EndsWith(HubsSuffix, StringComparison.OrdinalIgnoreCase))
{
// Generate the proper hub url
string hubUrl = normalized.Substring(0, normalized.Length - HubsSuffix.Length);
// Generate the proxy
context.Response.ContentType = JsonUtility.JavaScriptMimeType;
return context.Response.End(_proxyGenerator.GenerateProxy(hubUrl));
}
_isDebuggingEnabled = context.IsDebuggingEnabled();
return base.ProcessRequest(context);
}
internal static Task Connect(IHub hub)
{
return hub.OnConnected();
}
internal static Task Reconnect(IHub hub)
{
return hub.OnReconnected();
}
internal static Task Disconnect(IHub hub)
{
return hub.OnDisconnected();
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "A faulted task is returned.")]
internal static Task<object> Incoming(IHubIncomingInvokerContext context)
{
var tcs = new TaskCompletionSource<object>();
try
{
object result = context.MethodDescriptor.Invoker(context.Hub, context.Args.ToArray());
Type returnType = context.MethodDescriptor.ReturnType;
if (typeof(Task).IsAssignableFrom(returnType))
{
var task = (Task)result;
if (!returnType.IsGenericType)
{
task.ContinueWith(tcs);
}
else
{
// Get the <T> in Task<T>
Type resultType = returnType.GetGenericArguments().Single();
Type genericTaskType = typeof(Task<>).MakeGenericType(resultType);
// Get the correct ContinueWith overload
var parameter = Expression.Parameter(typeof(object));
// TODO: Cache this whole thing
// Action<object> callback = result => ContinueWith((Task<T>)result, tcs);
MethodInfo continueWithMethod = _continueWithMethod.MakeGenericMethod(resultType);
Expression body = Expression.Call(continueWithMethod,
Expression.Convert(parameter, genericTaskType),
Expression.Constant(tcs));
var continueWithInvoker = Expression.Lambda<Action<object>>(body, parameter).Compile();
continueWithInvoker.Invoke(result);
}
}
else
{
tcs.TrySetResult(result);
}
}
catch (Exception ex)
{
tcs.TrySetUnwrappedException(ex);
}
return tcs.Task;
}
internal static Task Outgoing(IHubOutgoingInvokerContext context)
{
var message = new ConnectionMessage(context.Signal, context.Invocation, context.ExcludedSignals);
return context.Connection.Send(message);
}
protected override Task OnConnected(IRequest request, string connectionId)
{
return ExecuteHubEvent(request, connectionId, hub => _pipelineInvoker.Connect(hub));
}
protected override Task OnReconnected(IRequest request, string connectionId)
{
return ExecuteHubEvent(request, connectionId, hub => _pipelineInvoker.Reconnect(hub));
}
protected override IList<string> OnRejoiningGroups(IRequest request, IList<string> groups, string connectionId)
{
return _hubs.Select(hubDescriptor =>
{
string groupPrefix = hubDescriptor.Name + ".";
var hubGroups = groups.Where(g => g.StartsWith(groupPrefix, StringComparison.OrdinalIgnoreCase))
.Select(g => g.Substring(groupPrefix.Length))
.ToList();
return _pipelineInvoker.RejoiningGroups(hubDescriptor, request, hubGroups)
.Select(g => groupPrefix + g);
}).SelectMany(groupsToRejoin => groupsToRejoin).ToList();
}
protected override Task OnDisconnected(IRequest request, string connectionId)
{
return ExecuteHubEvent(request, connectionId, hub => _pipelineInvoker.Disconnect(hub));
}
protected override IList<string> GetSignals(string connectionId)
{
return _hubs.SelectMany(info => new[] { PrefixHelper.GetHubName(info.Name), PrefixHelper.GetHubConnectionId(info.CreateQualifiedName(connectionId)) })
.Concat(new[] { PrefixHelper.GetConnectionId(connectionId), PrefixHelper.GetAck(connectionId) })
.ToList();
}
private Task ExecuteHubEvent(IRequest request, string connectionId, Func<IHub, Task> action)
{
var hubs = GetHubs(request, connectionId).ToList();
var operations = hubs.Select(instance => action(instance).OrEmpty().Catch()).ToArray();
if (operations.Length == 0)
{
DisposeHubs(hubs);
return TaskAsyncHelper.Empty;
}
var tcs = new TaskCompletionSource<object>();
Task.Factory.ContinueWhenAll(operations, tasks =>
{
DisposeHubs(hubs);
var faulted = tasks.FirstOrDefault(t => t.IsFaulted);
if (faulted != null)
{
tcs.SetUnwrappedException(faulted.Exception);
}
else if (tasks.Any(t => t.IsCanceled))
{
tcs.SetCanceled();
}
else
{
tcs.SetResult(null);
}
});
return tcs.Task;
}
private IHub CreateHub(IRequest request, HubDescriptor descriptor, string connectionId, StateChangeTracker tracker = null, bool throwIfFailedToCreate = false)
{
try
{
var hub = _manager.ResolveHub(descriptor.Name);
if (hub != null)
{
tracker = tracker ?? new StateChangeTracker();
hub.Context = new HubCallerContext(request, connectionId);
hub.Clients = new HubConnectionContext(_pipelineInvoker, Connection, descriptor.Name, connectionId, tracker);
hub.Groups = new GroupManager(Connection, PrefixHelper.GetHubGroupName(descriptor.Name));
}
return hub;
}
catch (Exception ex)
{
Trace.TraceInformation(String.Format(CultureInfo.CurrentCulture, Resources.Error_ErrorCreatingHub + ex.Message, descriptor.Name));
if (throwIfFailedToCreate)
{
throw;
}
return null;
}
}
private IEnumerable<IHub> GetHubs(IRequest request, string connectionId)
{
return from descriptor in _hubs
select CreateHub(request, descriptor, connectionId) into hub
where hub != null
select hub;
}
private static void DisposeHubs(IEnumerable<IHub> hubs)
{
foreach (var hub in hubs)
{
hub.Dispose();
}
}
private Task ProcessResponse(StateChangeTracker tracker, object result, HubRequest request, Exception error)
{
var hubResult = new HubResponse
{
State = tracker.GetChanges(),
Result = result,
Id = request.Id,
};
if (error != null)
{
_counters.ErrorsHubInvocationTotal.Increment();
_counters.ErrorsHubInvocationPerSec.Increment();
_counters.ErrorsAllTotal.Increment();
_counters.ErrorsAllPerSec.Increment();
if (_enableDetailedErrors)
{
var exception = error.InnerException ?? error;
hubResult.StackTrace = _isDebuggingEnabled ? exception.StackTrace : null;
hubResult.Error = exception.Message;
}
else
{
hubResult.Error = String.Format(CultureInfo.CurrentCulture, Resources.Error_HubInvocationFailed, request.Hub, request.Method);
}
}
return Transport.Send(hubResult);
}
private static void ContinueWith<T>(Task<T> task, TaskCompletionSource<object> tcs)
{
if (task.IsCompleted)
{
// Fast path for tasks that completed synchronously
ContinueSync<T>(task, tcs);
}
else
{
ContinueAsync<T>(task, tcs);
}
}
private static void ContinueSync<T>(Task<T> task, TaskCompletionSource<object> tcs)
{
if (task.IsFaulted)
{
tcs.TrySetUnwrappedException(task.Exception);
}
else if (task.IsCanceled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(task.Result);
}
}
private static void ContinueAsync<T>(Task<T> task, TaskCompletionSource<object> tcs)
{
task.ContinueWith(t =>
{
if (t.IsFaulted)
{
tcs.TrySetUnwrappedException(t.Exception);
}
else if (t.IsCanceled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(t.Result);
}
});
}
[SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "It is instantiated through JSON deserialization.")]
private class ClientHubInfo
{
public string Name { get; set; }
}
}
}

@ -1,25 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Hubs
{
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class HubMethodNameAttribute : Attribute
{
public HubMethodNameAttribute(string methodName)
{
if (String.IsNullOrEmpty(methodName))
{
throw new ArgumentNullException("methodName");
}
MethodName = methodName;
}
public string MethodName
{
get;
private set;
}
}
}

@ -1,25 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Hubs
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class HubNameAttribute : Attribute
{
public HubNameAttribute(string hubName)
{
if (String.IsNullOrEmpty(hubName))
{
throw new ArgumentNullException("hubName");
}
HubName = hubName;
}
public string HubName
{
get;
private set;
}
}
}

@ -1,20 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class HubRequest
{
public string Hub { get; set; }
public string Method { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This type is used for de-serialization.")]
public IJsonValue[] ParameterValues { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This type is used for de-serialization.")]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This type is used for de-serialization.")]
public IDictionary<string, object> State { get; set; }
public string Id { get; set; }
}
}

@ -1,69 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.AspNet.SignalR.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNet.SignalR.Hubs
{
internal class HubRequestParser : IHubRequestParser
{
private static readonly IJsonValue[] _emptyArgs = new IJsonValue[0];
public HubRequest Parse(string data)
{
var serializer = new JsonNetSerializer();
var deserializedData = serializer.Parse<HubInvocation>(data);
var request = new HubRequest();
request.Hub = deserializedData.Hub;
request.Method = deserializedData.Method;
request.Id = deserializedData.Id;
request.State = GetState(deserializedData);
request.ParameterValues = (deserializedData.Args != null) ? deserializedData.Args.Select(value => new JRawValue(value)).ToArray() : _emptyArgs;
return request;
}
[SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "This type is used for deserialzation")]
private class HubInvocation
{
[JsonProperty("H")]
public string Hub { get; set; }
[JsonProperty("M")]
public string Method { get; set; }
[JsonProperty("I")]
public string Id { get; set; }
[JsonProperty("S")]
public JRaw State { get; set; }
[JsonProperty("A")]
public JRaw[] Args { get; set; }
}
private static IDictionary<string, object> GetState(HubInvocation deserializedData)
{
if (deserializedData.State == null)
{
return new Dictionary<string, object>();
}
// Get the raw JSON string and check if it's over 4K
string json = deserializedData.State.ToString();
if (json.Length > 4096)
{
throw new InvalidOperationException(Resources.Error_StateExceededMaximumLength);
}
var settings = new JsonSerializerSettings();
settings.Converters.Add(new SipHashBasedDictionaryConverter());
var serializer = new JsonNetSerializer(settings);
return serializer.Parse<IDictionary<string, object>>(json);
}
}
}

@ -1,45 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// The response returned from an incoming hub request.
/// </summary>
public class HubResponse
{
/// <summary>
/// The changes made the the round tripped state.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Type is used for serialization")]
[JsonProperty("S", NullValueHandling = NullValueHandling.Ignore)]
public IDictionary<string, object> State { get; set; }
/// <summary>
/// The result of the invocation.
/// </summary>
[JsonProperty("R", NullValueHandling = NullValueHandling.Ignore)]
public object Result { get; set; }
/// <summary>
/// The id of the operation.
/// </summary>
[JsonProperty("I")]
public string Id { get; set; }
/// <summary>
/// The exception that occurs as a result of invoking the hub method.
/// </summary>
[JsonProperty("E", NullValueHandling = NullValueHandling.Ignore)]
public string Error { get; set; }
/// <summary>
/// The stack trace of the exception that occurs as a result of invoking the hub method.
/// </summary>
[JsonProperty("T", NullValueHandling = NullValueHandling.Ignore)]
public string StackTrace { get; set; }
}
}

@ -1,14 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace Microsoft.AspNet.SignalR.Hubs
{
public interface IAssemblyLocator
{
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Might be expensive.")]
IList<Assembly> GetAssemblies();
}
}

@ -1,20 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Threading.Tasks;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// A server side proxy for the client side hub.
/// </summary>
public interface IClientProxy
{
/// <summary>
/// Invokes a method on the connection(s) represented by the <see cref="IClientProxy"/> instance.
/// </summary>
/// <param name="method">name of the method to invoke</param>
/// <param name="args">argumetns to pass to the client</param>
/// <returns>A task that represents when the data has been sent to the client.</returns>
Task Invoke(string method, params object[] args);
}
}

@ -1,41 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNet.SignalR.Hubs
{
public interface IHub : IDisposable
{
/// <summary>
/// Gets a <see cref="HubCallerContext"/>. Which contains information about the calling client.
/// </summary>
HubCallerContext Context { get; set; }
/// <summary>
/// Gets a dynamic object that represents all clients connected to this hub (not hub instance).
/// </summary>
HubConnectionContext Clients { get; set; }
/// <summary>
/// Gets the <see cref="IGroupManager"/> the hub instance.
/// </summary>
IGroupManager Groups { get; set; }
/// <summary>
/// Called when a new connection is made to the <see cref="IHub"/>.
/// </summary>
Task OnConnected();
/// <summary>
/// Called when a connection reconnects to the <see cref="IHub"/> after a timeout.
/// </summary>
Task OnReconnected();
/// <summary>
/// Called when a connection is disconnected from the <see cref="IHub"/>.
/// </summary>
Task OnDisconnected();
}
}

@ -1,9 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hubs
{
public interface IHubActivator
{
IHub Create(HubDescriptor descriptor);
}
}

@ -1,15 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Encapsulates all information about a SignalR connection for an <see cref="IHub"/>.
/// </summary>
public interface IHubConnectionContext
{
dynamic All { get; }
dynamic AllExcept(params string[] excludeConnectionIds);
dynamic Client(string connectionId);
dynamic Group(string groupName, params string[] excludeConnectionIds);
}
}

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Handles parsing incoming requests through the <see cref="HubDispatcher"/>.
/// </summary>
public interface IHubRequestParser
{
/// <summary>
/// Parses the incoming hub payload into a <see cref="HubRequest"/>.
/// </summary>
/// <param name="data">The raw hub payload.</param>
/// <returns>The resulting <see cref="HubRequest"/>.</returns>
HubRequest Parse(string data);
}
}

@ -1,9 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hubs
{
public interface IJavaScriptMinifier
{
string Minify(string source);
}
}

@ -1,9 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hubs
{
public interface IJavaScriptProxyGenerator
{
string GenerateProxy(string serviceUrl);
}
}

@ -1,95 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class DefaultHubManager : IHubManager
{
private readonly IEnumerable<IMethodDescriptorProvider> _methodProviders;
private readonly IHubActivator _activator;
private readonly IEnumerable<IHubDescriptorProvider> _hubProviders;
public DefaultHubManager(IDependencyResolver resolver)
{
_hubProviders = resolver.ResolveAll<IHubDescriptorProvider>();
_methodProviders = resolver.ResolveAll<IMethodDescriptorProvider>();
_activator = resolver.Resolve<IHubActivator>();
}
public HubDescriptor GetHub(string hubName)
{
HubDescriptor descriptor = null;
if (_hubProviders.FirstOrDefault(p => p.TryGetHub(hubName, out descriptor)) != null)
{
return descriptor;
}
return null;
}
public IEnumerable<HubDescriptor> GetHubs(Func<HubDescriptor, bool> predicate)
{
var hubs = _hubProviders.SelectMany(p => p.GetHubs());
if (predicate != null)
{
return hubs.Where(predicate);
}
return hubs;
}
public MethodDescriptor GetHubMethod(string hubName, string method, IList<IJsonValue> parameters)
{
HubDescriptor hub = GetHub(hubName);
if (hub == null)
{
return null;
}
MethodDescriptor descriptor = null;
if (_methodProviders.FirstOrDefault(p => p.TryGetMethod(hub, method, out descriptor, parameters)) != null)
{
return descriptor;
}
return null;
}
public IEnumerable<MethodDescriptor> GetHubMethods(string hubName, Func<MethodDescriptor, bool> predicate)
{
HubDescriptor hub = GetHub(hubName);
if (hub == null)
{
return null;
}
var methods = _methodProviders.SelectMany(p => p.GetMethods(hub));
if (predicate != null)
{
return methods.Where(predicate);
}
return methods;
}
public IHub ResolveHub(string hubName)
{
HubDescriptor hub = GetHub(hubName);
return hub == null ? null : _activator.Create(hub);
}
public IEnumerable<IHub> ResolveHubs()
{
return GetHubs(predicate: null).Select(hub => _activator.Create(hub));
}
}
}

@ -1,54 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class DefaultParameterResolver : IParameterResolver
{
/// <summary>
/// Resolves a parameter value based on the provided object.
/// </summary>
/// <param name="descriptor">Parameter descriptor.</param>
/// <param name="value">Value to resolve the parameter value from.</param>
/// <returns>The parameter value.</returns>
public virtual object ResolveParameter(ParameterDescriptor descriptor, IJsonValue value)
{
if (descriptor == null)
{
throw new ArgumentNullException("descriptor");
}
if (value == null)
{
throw new ArgumentNullException("value");
}
if (value.GetType() == descriptor.ParameterType)
{
return value;
}
return value.ConvertTo(descriptor.ParameterType);
}
/// <summary>
/// Resolves method parameter values based on provided objects.
/// </summary>
/// <param name="method">Method descriptor.</param>
/// <param name="values">List of values to resolve parameter values from.</param>
/// <returns>Array of parameter values.</returns>
public virtual IList<object> ResolveMethodParameters(MethodDescriptor method, IList<IJsonValue> values)
{
if (method == null)
{
throw new ArgumentNullException("method");
}
return method.Parameters.Zip(values, ResolveParameter).ToArray();
}
}
}

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hubs
{
public abstract class Descriptor
{
/// <summary>
/// Name of Descriptor.
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// Flags whether the name was specified.
/// </summary>
public virtual bool NameSpecified { get; set; }
}
}

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Holds information about a single hub.
/// </summary>
public class HubDescriptor : Descriptor
{
/// <summary>
/// Hub type.
/// </summary>
public virtual Type HubType { get; set; }
public string CreateQualifiedName(string unqualifiedName)
{
return Name + "." + unqualifiedName;
}
}
}

@ -1,42 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Holds information about a single hub method.
/// </summary>
public class MethodDescriptor : Descriptor
{
/// <summary>
/// The return type of this method.
/// </summary>
public virtual Type ReturnType { get; set; }
/// <summary>
/// Hub descriptor object, target to this method.
/// </summary>
public virtual HubDescriptor Hub { get; set; }
/// <summary>
/// Available method parameters.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is supposed to be mutable")]
public virtual IList<ParameterDescriptor> Parameters { get; set; }
/// <summary>
/// Method invocation delegate.
/// Takes a target hub and an array of invocation arguments as it's arguments.
/// </summary>
public virtual Func<IHub, object[], object> Invoker { get; set; }
/// <summary>
/// Attributes attached to this method.
/// </summary>
public virtual IEnumerable<Attribute> Attributes { get; set; }
}
}

@ -1,40 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class NullMethodDescriptor : MethodDescriptor
{
private static readonly IEnumerable<Attribute> _attributes = new List<Attribute>();
private static readonly IList<ParameterDescriptor> _parameters = new List<ParameterDescriptor>();
private string _methodName;
public NullMethodDescriptor(string methodName)
{
_methodName = methodName;
}
public override Func<IHub, object[], object> Invoker
{
get
{
return (emptyHub, emptyParameters) =>
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_MethodCouldNotBeResolved, _methodName));
};
}
}
public override IList<ParameterDescriptor> Parameters
{
get { return _parameters; }
}
public override IEnumerable<Attribute> Attributes
{
get { return _attributes; }
}
}
}

@ -1,23 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Holds information about a single hub method parameter.
/// </summary>
public class ParameterDescriptor
{
/// <summary>
/// Parameter name.
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// Parameter type.
/// </summary>
public virtual Type ParameterType { get; set; }
}
}

@ -1,79 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace Microsoft.AspNet.SignalR.Hubs
{
internal class HubMethodDispatcher
{
private HubMethodExecutor _executor;
public HubMethodDispatcher(MethodInfo methodInfo)
{
_executor = GetExecutor(methodInfo);
MethodInfo = methodInfo;
}
private delegate object HubMethodExecutor(IHub hub, object[] parameters);
private delegate void VoidHubMethodExecutor(IHub hub, object[] parameters);
public MethodInfo MethodInfo { get; private set; }
public object Execute(IHub hub, object[] parameters)
{
return _executor(hub, parameters);
}
private static HubMethodExecutor GetExecutor(MethodInfo methodInfo)
{
// Parameters to executor
ParameterExpression hubParameter = Expression.Parameter(typeof(IHub), "hub");
ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
// Build parameter list
List<Expression> parameters = new List<Expression>();
ParameterInfo[] paramInfos = methodInfo.GetParameters();
for (int i = 0; i < paramInfos.Length; i++)
{
ParameterInfo paramInfo = paramInfos[i];
BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
// valueCast is "(Ti) parameters[i]"
parameters.Add(valueCast);
}
// Call method
UnaryExpression instanceCast = (!methodInfo.IsStatic) ? Expression.Convert(hubParameter, methodInfo.ReflectedType) : null;
MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameters);
// methodCall is "((TController) hub) method((T0) parameters[0], (T1) parameters[1], ...)"
// Create function
if (methodCall.Type == typeof(void))
{
Expression<VoidHubMethodExecutor> lambda = Expression.Lambda<VoidHubMethodExecutor>(methodCall, hubParameter, parametersParameter);
VoidHubMethodExecutor voidExecutor = lambda.Compile();
return WrapVoidAction(voidExecutor);
}
else
{
// must coerce methodCall to match HubMethodExecutor signature
UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
Expression<HubMethodExecutor> lambda = Expression.Lambda<HubMethodExecutor>(castMethodCall, hubParameter, parametersParameter);
return lambda.Compile();
}
}
private static HubMethodExecutor WrapVoidAction(VoidHubMethodExecutor executor)
{
return delegate(IHub hub, object[] parameters)
{
executor(hub, parameters);
return null;
};
}
}
}

@ -1,28 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Describes hub descriptor provider, which provides information about available hubs.
/// </summary>
public interface IHubDescriptorProvider
{
/// <summary>
/// Retrieve all avaiable hubs.
/// </summary>
/// <returns>Collection of hub descriptors.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This call might be expensive")]
IList<HubDescriptor> GetHubs();
/// <summary>
/// Tries to retrieve hub with a given name.
/// </summary>
/// <param name="hubName">Name of the hub.</param>
/// <param name="descriptor">Retrieved descriptor object.</param>
/// <returns>True, if hub has been found</returns>
bool TryGetHub(string hubName, out HubDescriptor descriptor);
}
}

@ -1,57 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Describes a hub manager - main point in the whole hub and method lookup process.
/// </summary>
public interface IHubManager
{
/// <summary>
/// Retrieves a single hub descriptor.
/// </summary>
/// <param name="hubName">Name of the hub.</param>
/// <returns>Hub descriptor, if found. Null, otherwise.</returns>
HubDescriptor GetHub(string hubName);
/// <summary>
/// Retrieves all available hubs matching the given predicate.
/// </summary>
/// <returns>List of hub descriptors.</returns>
IEnumerable<HubDescriptor> GetHubs(Func<HubDescriptor, bool> predicate);
/// <summary>
/// Resolves a given hub name to a concrete object.
/// </summary>
/// <param name="hubName">Name of the hub.</param>
/// <returns>Hub implementation instance, if found. Null otherwise.</returns>
IHub ResolveHub(string hubName);
/// <summary>
/// Resolves all available hubs to their concrete objects.
/// </summary>
/// <returns>List of hub instances.</returns>
IEnumerable<IHub> ResolveHubs();
/// <summary>
/// Retrieves a method with a given name on a given hub.
/// </summary>
/// <param name="hubName">Name of the hub.</param>
/// <param name="method">Name of the method to find.</param>
/// <param name="parameters">Method parameters to match.</param>
/// <returns>Descriptor of the method, if found. Null otherwise.</returns>
MethodDescriptor GetHubMethod(string hubName, string method, IList<IJsonValue> parameters);
/// <summary>
/// Gets all methods available to call on a given hub.
/// </summary>
/// <param name="hubName">Name of the hub,</param>
/// <param name="predicate">Optional predicate for filtering results.</param>
/// <returns>List of available methods.</returns>
IEnumerable<MethodDescriptor> GetHubMethods(string hubName, Func<MethodDescriptor, bool> predicate);
}
}

@ -1,32 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Describes a hub method provider that builds a collection of available methods on a given hub.
/// </summary>
public interface IMethodDescriptorProvider
{
/// <summary>
/// Retrieve all methods on a given hub.
/// </summary>
/// <param name="hub">Hub descriptor object.</param>
/// <returns>Available methods.</returns>
IEnumerable<MethodDescriptor> GetMethods(HubDescriptor hub);
/// <summary>
/// Tries to retrieve a method.
/// </summary>
/// <param name="hub">Hub descriptor object</param>
/// <param name="method">Name of the method.</param>
/// <param name="descriptor">Descriptor of the method, if found. Null otherwise.</param>
/// <param name="parameters">Method parameters to match.</param>
/// <returns>True, if a method has been found.</returns>
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "This is a well known pattern for efficient lookup")]
bool TryGetMethod(HubDescriptor hub, string method, out MethodDescriptor descriptor, IList<IJsonValue> parameters);
}
}

@ -1,21 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Describes a parameter resolver for resolving parameter-matching values based on provided information.
/// </summary>
public interface IParameterResolver
{
/// <summary>
/// Resolves method parameter values based on provided objects.
/// </summary>
/// <param name="method">Method descriptor.</param>
/// <param name="values">List of values to resolve parameter values from.</param>
/// <returns>Array of parameter values.</returns>
IList<object> ResolveMethodParameters(MethodDescriptor method, IList<IJsonValue> values);
}
}

@ -1,87 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class ReflectedHubDescriptorProvider : IHubDescriptorProvider
{
private readonly Lazy<IDictionary<string, HubDescriptor>> _hubs;
private readonly Lazy<IAssemblyLocator> _locator;
public ReflectedHubDescriptorProvider(IDependencyResolver resolver)
{
_locator = new Lazy<IAssemblyLocator>(resolver.Resolve<IAssemblyLocator>);
_hubs = new Lazy<IDictionary<string, HubDescriptor>>(BuildHubsCache);
}
public IList<HubDescriptor> GetHubs()
{
return _hubs.Value
.Select(kv => kv.Value)
.Distinct()
.ToList();
}
public bool TryGetHub(string hubName, out HubDescriptor descriptor)
{
return _hubs.Value.TryGetValue(hubName, out descriptor);
}
protected IDictionary<string, HubDescriptor> BuildHubsCache()
{
// Getting all IHub-implementing types that apply
var types = _locator.Value.GetAssemblies()
.SelectMany(GetTypesSafe)
.Where(IsHubType);
// Building cache entries for each descriptor
// Each descriptor is stored in dictionary under a key
// that is it's name or the name provided by an attribute
var cacheEntries = types
.Select(type => new HubDescriptor
{
NameSpecified = (type.GetHubAttributeName() != null),
Name = type.GetHubName(),
HubType = type
})
.ToDictionary(hub => hub.Name,
hub => hub,
StringComparer.OrdinalIgnoreCase);
return cacheEntries;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "If we throw then it's not a hub type")]
private static bool IsHubType(Type type)
{
try
{
return typeof(IHub).IsAssignableFrom(type) &&
!type.IsAbstract &&
(type.Attributes.HasFlag(TypeAttributes.Public) ||
type.Attributes.HasFlag(TypeAttributes.NestedPublic));
}
catch
{
return false;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "If we throw then we have an empty type")]
private static IEnumerable<Type> GetTypesSafe(Assembly a)
{
try
{
return a.GetTypes();
}
catch
{
return Enumerable.Empty<Type>();
}
}
}
}

@ -1,150 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.SignalR.Json;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class ReflectedMethodDescriptorProvider : IMethodDescriptorProvider
{
private readonly ConcurrentDictionary<string, IDictionary<string, IEnumerable<MethodDescriptor>>> _methods;
private readonly ConcurrentDictionary<string, MethodDescriptor> _executableMethods;
public ReflectedMethodDescriptorProvider()
{
_methods = new ConcurrentDictionary<string, IDictionary<string, IEnumerable<MethodDescriptor>>>(StringComparer.OrdinalIgnoreCase);
_executableMethods = new ConcurrentDictionary<string, MethodDescriptor>(StringComparer.OrdinalIgnoreCase);
}
public IEnumerable<MethodDescriptor> GetMethods(HubDescriptor hub)
{
return FetchMethodsFor(hub)
.SelectMany(kv => kv.Value)
.ToList();
}
/// <summary>
/// Retrieves an existing dictionary of all available methods for a given hub from cache.
/// If cache entry does not exist - it is created automatically by BuildMethodCacheFor.
/// </summary>
/// <param name="hub"></param>
/// <returns></returns>
private IDictionary<string, IEnumerable<MethodDescriptor>> FetchMethodsFor(HubDescriptor hub)
{
return _methods.GetOrAdd(
hub.Name,
key => BuildMethodCacheFor(hub));
}
/// <summary>
/// Builds a dictionary of all possible methods on a given hub.
/// Single entry contains a collection of available overloads for a given method name (key).
/// This dictionary is being cached afterwards.
/// </summary>
/// <param name="hub">Hub to build cache for</param>
/// <returns>Dictionary of available methods</returns>
private static IDictionary<string, IEnumerable<MethodDescriptor>> BuildMethodCacheFor(HubDescriptor hub)
{
return ReflectionHelper.GetExportedHubMethods(hub.HubType)
.GroupBy(GetMethodName, StringComparer.OrdinalIgnoreCase)
.ToDictionary(group => group.Key,
group => group.Select(oload =>
new MethodDescriptor
{
ReturnType = oload.ReturnType,
Name = group.Key,
NameSpecified = (GetMethodAttributeName(oload) != null),
Invoker = new HubMethodDispatcher(oload).Execute,
Hub = hub,
Attributes = oload.GetCustomAttributes(typeof(Attribute), inherit: true).Cast<Attribute>(),
Parameters = oload.GetParameters()
.Select(p => new ParameterDescriptor
{
Name = p.Name,
ParameterType = p.ParameterType,
})
.ToList()
}),
StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// Searches the specified <paramref name="hub">Hub</paramref> for the specified <paramref name="method"/>.
/// </summary>
/// <remarks>
/// In the case that there are multiple overloads of the specified <paramref name="method"/>, the <paramref name="parameters">parameter set</paramref> helps determine exactly which instance of the overload should be resolved.
/// If there are multiple overloads found with the same number of matching parameters, none of the methods will be returned because it is not possible to determine which overload of the method was intended to be resolved.
/// </remarks>
/// <param name="hub">Hub to search for the specified <paramref name="method"/> on.</param>
/// <param name="method">The method name to search for.</param>
/// <param name="descriptor">If successful, the <see cref="MethodDescriptor"/> that was resolved.</param>
/// <param name="parameters">The set of parameters that will be used to help locate a specific overload of the specified <paramref name="method"/>.</param>
/// <returns>True if the method matching the name/parameter set is found on the hub, otherwise false.</returns>
public bool TryGetMethod(HubDescriptor hub, string method, out MethodDescriptor descriptor, IList<IJsonValue> parameters)
{
string hubMethodKey = BuildHubExecutableMethodCacheKey(hub, method, parameters);
if (!_executableMethods.TryGetValue(hubMethodKey, out descriptor))
{
IEnumerable<MethodDescriptor> overloads;
if (FetchMethodsFor(hub).TryGetValue(method, out overloads))
{
var matches = overloads.Where(o => o.Matches(parameters)).ToList();
// If only one match is found, that is the "executable" version, otherwise none of the methods can be returned because we don't know which one was actually being targeted
descriptor = matches.Count == 1 ? matches[0] : null;
}
else
{
descriptor = null;
}
// If an executable method was found, cache it for future lookups (NOTE: we don't cache null instances because it could be a surface area for DoS attack by supplying random method names to flood the cache)
if (descriptor != null)
{
_executableMethods.TryAdd(hubMethodKey, descriptor);
}
}
return descriptor != null;
}
private static string BuildHubExecutableMethodCacheKey(HubDescriptor hub, string method, IList<IJsonValue> parameters)
{
string normalizedParameterCountKeyPart;
if (parameters != null)
{
normalizedParameterCountKeyPart = parameters.Count.ToString(CultureInfo.InvariantCulture);
}
else
{
// NOTE: we normalize a null parameter array to be the same as an empty (i.e. Length == 0) parameter array
normalizedParameterCountKeyPart = "0";
}
// NOTE: we always normalize to all uppercase since method names are case insensitive and could theoretically come in diff. variations per call
string normalizedMethodName = method.ToUpperInvariant();
string methodKey = hub.Name + "::" + normalizedMethodName + "(" + normalizedParameterCountKeyPart + ")";
return methodKey;
}
private static string GetMethodName(MethodInfo method)
{
return GetMethodAttributeName(method) ?? method.Name;
}
private static string GetMethodAttributeName(MethodInfo method)
{
return ReflectionHelper.GetAttributeValue<HubMethodNameAttribute, string>(method, a => a.MethodName);
}
}
}

@ -1,21 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Dynamic;
using System.Globalization;
namespace Microsoft.AspNet.SignalR.Hubs
{
internal class NullClientProxy : DynamicObject
{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_UsingHubInstanceNotCreatedUnsupported));
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_UsingHubInstanceNotCreatedUnsupported));
}
}
}

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.AspNet.SignalR.Hubs
{
public class NullJavaScriptMinifier : IJavaScriptMinifier
{
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This is a singleton")]
public static readonly NullJavaScriptMinifier Instance = new NullJavaScriptMinifier();
public string Minify(string source)
{
return source;
}
}
}

@ -1,125 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// This module is added the the HubPipeline by default.
///
/// Hub level attributes that implement <see cref="IAuthorizeHubConnection"/> such as <see cref="AuthorizeAttribute"/> are applied to determine
/// whether to allow potential clients to receive messages sent from that hub using a <see cref="HubContext"/> or a <see cref="HubConnectionContext"/>
/// All applicable hub attributes must allow hub connection for the connection to be authorized.
///
/// Hub and method level attributes that implement <see cref="IAuthorizeHubMethodInvocation"/> such as <see cref="AuthorizeAttribute"/> are applied
/// to determine whether to allow callers to invoke hub methods.
/// All applicable hub level AND method level attributes must allow hub method invocation for the invocation to be authorized.
///
/// Optionally, this module may be instantiated with <see cref="IAuthorizeHubConnection"/> and <see cref="IAuthorizeHubMethodInvocation"/>
/// authorizers that will be applied globally to all hubs and hub methods.
/// </summary>
public class AuthorizeModule : HubPipelineModule
{
// Global authorizers
private readonly IAuthorizeHubConnection _globalConnectionAuthorizer;
private readonly IAuthorizeHubMethodInvocation _globalInvocationAuthorizer;
// Attribute authorizer caches
private readonly ConcurrentDictionary<Type, IEnumerable<IAuthorizeHubConnection>> _connectionAuthorizersCache;
private readonly ConcurrentDictionary<Type, IEnumerable<IAuthorizeHubMethodInvocation>> _classInvocationAuthorizersCache;
private readonly ConcurrentDictionary<MethodDescriptor, IEnumerable<IAuthorizeHubMethodInvocation>> _methodInvocationAuthorizersCache;
// By default, this module does not include any authorizers that are applied globally.
// This module will always apply authorizers attached to hubs or hub methods
public AuthorizeModule()
: this(globalConnectionAuthorizer: null, globalInvocationAuthorizer: null)
{
}
public AuthorizeModule(IAuthorizeHubConnection globalConnectionAuthorizer, IAuthorizeHubMethodInvocation globalInvocationAuthorizer)
{
// Set global authorizers
_globalConnectionAuthorizer = globalConnectionAuthorizer;
_globalInvocationAuthorizer = globalInvocationAuthorizer;
// Initialize attribute authorizer caches
_connectionAuthorizersCache = new ConcurrentDictionary<Type, IEnumerable<IAuthorizeHubConnection>>();
_classInvocationAuthorizersCache = new ConcurrentDictionary<Type, IEnumerable<IAuthorizeHubMethodInvocation>>();
_methodInvocationAuthorizersCache = new ConcurrentDictionary<MethodDescriptor, IEnumerable<IAuthorizeHubMethodInvocation>>();
}
public override Func<HubDescriptor, IRequest, bool> BuildAuthorizeConnect(Func<HubDescriptor, IRequest, bool> authorizeConnect)
{
return base.BuildAuthorizeConnect((hubDescriptor, request) =>
{
// Execute custom modules first and short circuit if any deny authorization.
if (!authorizeConnect(hubDescriptor, request))
{
return false;
}
// Execute the global hub connection authorizer if there is one next and short circuit if it denies authorization.
if (_globalConnectionAuthorizer != null && !_globalConnectionAuthorizer.AuthorizeHubConnection(hubDescriptor, request))
{
return false;
}
// Get hub attributes implementing IAuthorizeHubConnection from the cache
// If the attributes do not exist in the cache, retrieve them using reflection and add them to the cache
var attributeAuthorizers = _connectionAuthorizersCache.GetOrAdd(hubDescriptor.HubType,
hubType => hubType.GetCustomAttributes(typeof(IAuthorizeHubConnection), inherit: true).Cast<IAuthorizeHubConnection>());
// Every attribute (if any) implementing IAuthorizeHubConnection attached to the relevant hub MUST allow the connection
return attributeAuthorizers.All(a => a.AuthorizeHubConnection(hubDescriptor, request));
});
}
public override Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke)
{
return base.BuildIncoming(context =>
{
// Execute the global method invocation authorizer if there is one and short circuit if it denies authorization.
if (_globalInvocationAuthorizer == null || _globalInvocationAuthorizer.AuthorizeHubMethodInvocation(context, appliesToMethod: false))
{
// Get hub attributes implementing IAuthorizeHubMethodInvocation from the cache
// If the attributes do not exist in the cache, retrieve them using reflection and add them to the cache
var classLevelAuthorizers = _classInvocationAuthorizersCache.GetOrAdd(context.Hub.GetType(),
hubType => hubType.GetCustomAttributes(typeof(IAuthorizeHubMethodInvocation), inherit: true).Cast<IAuthorizeHubMethodInvocation>());
// Execute all hub level authorizers and short circuit if ANY deny authorization.
if (classLevelAuthorizers.All(a => a.AuthorizeHubMethodInvocation(context, appliesToMethod: false)))
{
// If the MethodDescriptor is a NullMethodDescriptor, we don't want to cache it since a new one is created
// for each invocation with an invalid method name. #1801
if (context.MethodDescriptor is NullMethodDescriptor)
{
return invoke(context);
}
// Get method attributes implementing IAuthorizeHubMethodInvocation from the cache
// If the attributes do not exist in the cache, retrieve them from the MethodDescriptor and add them to the cache
var methodLevelAuthorizers = _methodInvocationAuthorizersCache.GetOrAdd(context.MethodDescriptor,
methodDescriptor => methodDescriptor.Attributes.OfType<IAuthorizeHubMethodInvocation>());
// Execute all method level authorizers. If ALL provide authorization, continue executing the invocation pipeline.
if (methodLevelAuthorizers.All(a => a.AuthorizeHubMethodInvocation(context, appliesToMethod: true)))
{
return invoke(context);
}
}
}
// Send error back to the client
return TaskAsyncHelper.FromError<object>(
new NotAuthorizedException(String.Format(CultureInfo.CurrentCulture, Resources.Error_CallerNotAuthorizedToInvokeMethodOn,
context.MethodDescriptor.Name,
context.MethodDescriptor.Hub.Name)));
});
}
}
}

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Interface to be implemented by <see cref="System.Attribute"/>s that can authorize client to connect to a <see cref="IHub"/>.
/// </summary>
public interface IAuthorizeHubConnection
{
/// <summary>
/// Given a <see cref="HubCallerContext"/>, determine whether client is authorized to connect to <see cref="IHub"/>.
/// </summary>
/// <param name="hubDescriptor">Description of the hub client is attempting to connect to.</param>
/// <param name="request">The connection request from the client.</param>
/// <returns>true if the caller is authorized to connect to the hub; otherwise, false.</returns>
bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request);
}
}

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
namespace Microsoft.AspNet.SignalR.Hubs
{
/// <summary>
/// Interface to be implemented by <see cref="System.Attribute"/>s that can authorize the invocation of <see cref="IHub"/> methods.
/// </summary>
public interface IAuthorizeHubMethodInvocation
{
/// <summary>
/// Given a <see cref="IHubIncomingInvokerContext"/>, determine whether client is authorized to invoke the <see cref="IHub"/> method.
/// </summary>
/// <param name="hubIncomingInvokerContext">An <see cref="IHubIncomingInvokerContext"/> providing details regarding the <see cref="IHub"/> method invocation.</param>
/// <param name="appliesToMethod">Indicates whether the interface instance is an attribute applied directly to a method.</param>
/// <returns>true if the caller is authorized to invoke the <see cref="IHub"/> method; otherwise, false.</returns>
bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod);
}
}

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
namespace Microsoft.AspNet.SignalR.Hubs
{
[Serializable]
public class NotAuthorizedException : Exception
{
public NotAuthorizedException() { }
public NotAuthorizedException(string message) : base(message) { }
public NotAuthorizedException(string message, Exception inner) : base(message, inner) { }
protected NotAuthorizedException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
}

@ -1,42 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.SignalR.Hubs
{
internal class HubInvokerContext : IHubIncomingInvokerContext
{
public HubInvokerContext(IHub hub, StateChangeTracker tracker, MethodDescriptor methodDescriptor, IList<object> args)
{
Hub = hub;
MethodDescriptor = methodDescriptor;
Args = args;
StateTracker = tracker;
}
public IHub Hub
{
get;
private set;
}
public MethodDescriptor MethodDescriptor
{
get;
private set;
}
public IList<object> Args
{
get;
private set;
}
public StateChangeTracker StateTracker
{
get;
private set;
}
}
}

@ -1,41 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.SignalR.Hubs
{
internal class HubOutgoingInvokerContext : IHubOutgoingInvokerContext
{
public HubOutgoingInvokerContext(IConnection connection, string signal, ClientHubInvocation invocation, IList<string> excludedSignals)
{
Connection = connection;
Signal = signal;
Invocation = invocation;
ExcludedSignals = excludedSignals;
}
public IConnection Connection
{
get;
private set;
}
public ClientHubInvocation Invocation
{
get;
private set;
}
public string Signal
{
get;
private set;
}
public IList<string> ExcludedSignals
{
get;
private set;
}
}
}

@ -1,103 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.SignalR.Hubs
{
internal class HubPipeline : IHubPipeline, IHubPipelineInvoker
{
private readonly Stack<IHubPipelineModule> _modules;
private readonly Lazy<ComposedPipeline> _pipeline;
public HubPipeline()
{
_modules = new Stack<IHubPipelineModule>();
_pipeline = new Lazy<ComposedPipeline>(() => new ComposedPipeline(_modules));
}
public IHubPipeline AddModule(IHubPipelineModule pipelineModule)
{
if (_pipeline.IsValueCreated)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.Error_UnableToAddModulePiplineAlreadyInvoked));
}
_modules.Push(pipelineModule);
return this;
}
private ComposedPipeline Pipeline
{
get { return _pipeline.Value; }
}
public Task<object> Invoke(IHubIncomingInvokerContext context)
{
return Pipeline.Invoke(context);
}
public Task Connect(IHub hub)
{
return Pipeline.Connect(hub);
}
public Task Reconnect(IHub hub)
{
return Pipeline.Reconnect(hub);
}
public Task Disconnect(IHub hub)
{
return Pipeline.Disconnect(hub);
}
public bool AuthorizeConnect(HubDescriptor hubDescriptor, IRequest request)
{
return Pipeline.AuthorizeConnect(hubDescriptor, request);
}
public IList<string> RejoiningGroups(HubDescriptor hubDescriptor, IRequest request, IList<string> groups)
{
return Pipeline.RejoiningGroups(hubDescriptor, request, groups);
}
public Task Send(IHubOutgoingInvokerContext context)
{
return Pipeline.Send(context);
}
private class ComposedPipeline
{
public Func<IHubIncomingInvokerContext, Task<object>> Invoke;
public Func<IHub, Task> Connect;
public Func<IHub, Task> Reconnect;
public Func<IHub, Task> Disconnect;
public Func<HubDescriptor, IRequest, bool> AuthorizeConnect;
public Func<HubDescriptor, IRequest, IList<string>, IList<string>> RejoiningGroups;
public Func<IHubOutgoingInvokerContext, Task> Send;
public ComposedPipeline(Stack<IHubPipelineModule> modules)
{
// This wouldn't look nearly as gnarly if C# had better type inference, but now we don't need the ComposedModule or PassThroughModule.
Invoke = Compose<Func<IHubIncomingInvokerContext, Task<object>>>(modules, (m, f) => m.BuildIncoming(f))(HubDispatcher.Incoming);
Connect = Compose<Func<IHub, Task>>(modules, (m, f) => m.BuildConnect(f))(HubDispatcher.Connect);
Reconnect = Compose<Func<IHub, Task>>(modules, (m, f) => m.BuildReconnect(f))(HubDispatcher.Reconnect);
Disconnect = Compose<Func<IHub, Task>>(modules, (m, f) => m.BuildDisconnect(f))(HubDispatcher.Disconnect);
AuthorizeConnect = Compose<Func<HubDescriptor, IRequest, bool>>(modules, (m, f) => m.BuildAuthorizeConnect(f))((h, r) => true);
RejoiningGroups = Compose<Func<HubDescriptor, IRequest, IList<string>, IList<string>>>(modules, (m, f) => m.BuildRejoiningGroups(f))((h, r, g) => g);
Send = Compose<Func<IHubOutgoingInvokerContext, Task>>(modules, (m, f) => m.BuildOutgoing(f))(HubDispatcher.Outgoing);
}
// IHubPipelineModule could be turned into a second generic parameter, but it would make the above invocations even longer than they currently are.
private static Func<T, T> Compose<T>(IEnumerable<IHubPipelineModule> modules, Func<IHubPipelineModule, T, T> method)
{
// Notice we are reversing and aggregating in one step. (Function composition is associative)
return modules.Aggregate<IHubPipelineModule, Func<T, T>>(x => x, (a, b) => (x => method(b, a(x))));
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save