diff --git a/src/Microsoft.AspNet.SignalR.Core/ConnectionConfiguration.cs b/src/Microsoft.AspNet.SignalR.Core/ConnectionConfiguration.cs
index 15ace8693a..98b63705ec 100644
--- a/src/Microsoft.AspNet.SignalR.Core/ConnectionConfiguration.cs
+++ b/src/Microsoft.AspNet.SignalR.Core/ConnectionConfiguration.cs
@@ -13,5 +13,10 @@ public IDependencyResolver Resolver
get { return _resolver ?? GlobalHost.DependencyResolver; }
set { _resolver = value; }
}
+
+ ///
+ /// Determines if browsers can make cross domain requests to SignalR endpoints.
+ ///
+ public bool EnableCrossDomain { get; set; }
}
}
diff --git a/src/Microsoft.AspNet.SignalR.Owin/Handlers/CallHandler.cs b/src/Microsoft.AspNet.SignalR.Owin/Handlers/CallHandler.cs
index 169d2c7283..42ca9635ae 100644
--- a/src/Microsoft.AspNet.SignalR.Owin/Handlers/CallHandler.cs
+++ b/src/Microsoft.AspNet.SignalR.Owin/Handlers/CallHandler.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Hosting;
@@ -12,18 +11,16 @@ namespace Microsoft.AspNet.SignalR.Owin
{
public class CallHandler
{
- private readonly IDependencyResolver _resolver;
+ private readonly ConnectionConfiguration _configuration;
private readonly PersistentConnection _connection;
- private static readonly string[] AllowCredentialsTrue = new[] { "true" };
-
private static bool _supportWebSockets;
private static bool _supportWebSocketsInitialized;
private static object _supportWebSocketsLock = new object();
- public CallHandler(IDependencyResolver resolver, PersistentConnection connection)
+ public CallHandler(ConnectionConfiguration configuration, PersistentConnection connection)
{
- _resolver = resolver;
+ _configuration = configuration;
_connection = connection;
}
@@ -33,12 +30,29 @@ public Task Invoke(IDictionary environment)
var serverResponse = new ServerResponse(environment);
var hostContext = new HostContext(serverRequest, serverResponse);
- // Add CORS support
- var origins = serverRequest.RequestHeaders.GetHeaders("Origin");
- if (origins != null && origins.Any(origin => !String.IsNullOrEmpty(origin)))
+ string origin = serverRequest.RequestHeaders.GetHeader("Origin");
+
+ if (_configuration.EnableCrossDomain)
{
- serverResponse.ResponseHeaders["Access-Control-Allow-Origin"] = origins;
- serverResponse.ResponseHeaders["Access-Control-Allow-Credentials"] = AllowCredentialsTrue;
+ // Add CORS response headers support
+ if (!String.IsNullOrEmpty(origin))
+ {
+ serverResponse.ResponseHeaders.SetHeader("Access-Control-Allow-Origin", origin);
+ serverResponse.ResponseHeaders.SetHeader("Access-Control-Allow-Credentials", "true");
+ }
+ }
+ else
+ {
+ string callback = serverRequest.QueryString["callback"];
+
+ // If it's a JSONP request and we're not allowing cross domain requests then block it
+ // If there's an origin header and it's not a same origin request then block it.
+
+ if (!String.IsNullOrEmpty(callback) ||
+ (!String.IsNullOrEmpty(origin) && !IsSameOrigin(serverRequest.Url, origin)))
+ {
+ return EndResponse(environment, 403, "Forbidden");
+ }
}
hostContext.Items[HostConstants.SupportsWebSockets] = LazyInitializer.EnsureInitialized(
@@ -53,21 +67,39 @@ public Task Invoke(IDictionary environment)
serverRequest.DisableRequestBuffering();
serverResponse.DisableResponseBuffering();
- _connection.Initialize(_resolver, hostContext);
+ _connection.Initialize(_configuration.Resolver, hostContext);
if (!_connection.Authorize(serverRequest))
{
// If we failed to authorize the request then return a 403 since the request
// can't do anything
- environment[OwinConstants.ResponseStatusCode] = 403;
- environment[OwinConstants.ResponseReasonPhrase] = "Forbidden";
-
- return TaskAsyncHelper.Empty;
+ return EndResponse(environment, 403, "Forbidden");
}
else
{
return _connection.ProcessRequest(hostContext);
}
}
+
+ private static Task EndResponse(IDictionary environment, int statusCode, string reason)
+ {
+ environment[OwinConstants.ResponseStatusCode] = statusCode;
+ environment[OwinConstants.ResponseReasonPhrase] = reason;
+
+ return TaskAsyncHelper.Empty;
+ }
+
+ private static bool IsSameOrigin(Uri requestUri, string origin)
+ {
+ Uri originUri;
+ if (!Uri.TryCreate(origin.Trim(), UriKind.Absolute, out originUri))
+ {
+ return false;
+ }
+
+ return (requestUri.Scheme == originUri.Scheme) &&
+ (requestUri.Host == originUri.Host) &&
+ (requestUri.Port == originUri.Port);
+ }
}
}
diff --git a/src/Microsoft.AspNet.SignalR.Owin/Handlers/HubDispatcherHandler.cs b/src/Microsoft.AspNet.SignalR.Owin/Handlers/HubDispatcherHandler.cs
index 6015b8e830..0868a52b3a 100644
--- a/src/Microsoft.AspNet.SignalR.Owin/Handlers/HubDispatcherHandler.cs
+++ b/src/Microsoft.AspNet.SignalR.Owin/Handlers/HubDispatcherHandler.cs
@@ -13,15 +13,13 @@ public class HubDispatcherHandler
{
private readonly AppFunc _next;
private readonly string _path;
- private readonly bool _enableJavaScriptProxies;
- private readonly IDependencyResolver _resolver;
+ private readonly HubConfiguration _configuration;
- public HubDispatcherHandler(AppFunc next, string path, bool enableJavaScriptProxies, IDependencyResolver resolver)
+ public HubDispatcherHandler(AppFunc next, string path, HubConfiguration configuration)
{
_next = next;
_path = path;
- _enableJavaScriptProxies = enableJavaScriptProxies;
- _resolver = resolver;
+ _configuration = configuration;
}
public Task Invoke(IDictionary environment)
@@ -33,9 +31,9 @@ public Task Invoke(IDictionary environment)
}
var pathBase = environment.Get(OwinConstants.RequestPathBase);
- var dispatcher = new HubDispatcher(pathBase + _path, _enableJavaScriptProxies);
+ var dispatcher = new HubDispatcher(pathBase + _path, _configuration.EnableJavaScriptProxies);
- var handler = new CallHandler(_resolver, dispatcher);
+ var handler = new CallHandler(_configuration, dispatcher);
return handler.Invoke(environment);
}
}
diff --git a/src/Microsoft.AspNet.SignalR.Owin/Handlers/PersistentConnectionHandler.cs b/src/Microsoft.AspNet.SignalR.Owin/Handlers/PersistentConnectionHandler.cs
index 2c2b85e63d..82b84a5cb5 100644
--- a/src/Microsoft.AspNet.SignalR.Owin/Handlers/PersistentConnectionHandler.cs
+++ b/src/Microsoft.AspNet.SignalR.Owin/Handlers/PersistentConnectionHandler.cs
@@ -14,14 +14,14 @@ public class PersistentConnectionHandler
private readonly AppFunc _next;
private readonly string _path;
private readonly Type _connectionType;
- private readonly IDependencyResolver _resolver;
+ private readonly ConnectionConfiguration _configuration;
- public PersistentConnectionHandler(AppFunc next, string path, Type connectionType, IDependencyResolver resolver)
+ public PersistentConnectionHandler(AppFunc next, string path, Type connectionType, ConnectionConfiguration configuration)
{
_next = next;
_path = path;
_connectionType = connectionType;
- _resolver = resolver;
+ _configuration = configuration;
}
public Task Invoke(IDictionary environment)
@@ -32,10 +32,10 @@ public Task Invoke(IDictionary environment)
return _next(environment);
}
- var connectionFactory = new PersistentConnectionFactory(_resolver);
+ var connectionFactory = new PersistentConnectionFactory(_configuration.Resolver);
var connection = connectionFactory.CreateInstance(_connectionType);
- var handler = new CallHandler(_resolver, connection);
+ var handler = new CallHandler(_configuration, connection);
return handler.Invoke(environment);
}
}
diff --git a/src/Microsoft.AspNet.SignalR.Owin/OwinExtensions.cs b/src/Microsoft.AspNet.SignalR.Owin/OwinExtensions.cs
index 7bd9d81f96..7ff9c5d8c2 100644
--- a/src/Microsoft.AspNet.SignalR.Owin/OwinExtensions.cs
+++ b/src/Microsoft.AspNet.SignalR.Owin/OwinExtensions.cs
@@ -30,7 +30,7 @@ public static IAppBuilder MapHubs(this IAppBuilder builder, string path, HubConf
throw new ArgumentNullException("configuration");
}
- return builder.UseType(path, configuration.EnableJavaScriptProxies, configuration.Resolver);
+ return builder.UseType(path, configuration);
}
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "The type parameter is syntactic sugar")]
@@ -52,14 +52,22 @@ public static IAppBuilder MapConnection(this IAppBuilder builder, string url, Ty
throw new ArgumentNullException("configuration");
}
- return builder.UseType(url, connectionType, configuration.Resolver);
+ return builder.UseType(url, connectionType, configuration);
}
private static IAppBuilder UseType(this IAppBuilder builder, params object[] args)
{
if (args.Length > 0)
{
- var resolver = args[args.Length - 1] as IDependencyResolver;
+ var configuration = args[args.Length - 1] as ConnectionConfiguration;
+
+ if (configuration == null)
+ {
+ throw new ArgumentException(Resources.Error_NoConfiguration);
+ }
+
+ var resolver = configuration.Resolver;
+
if (resolver == null)
{
throw new ArgumentException(Resources.Error_NoDepenendeyResolver);
diff --git a/src/Microsoft.AspNet.SignalR.Owin/Resources.Designer.cs b/src/Microsoft.AspNet.SignalR.Owin/Resources.Designer.cs
index c9a072c4d4..13b986cbb1 100644
--- a/src/Microsoft.AspNet.SignalR.Owin/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.SignalR.Owin/Resources.Designer.cs
@@ -60,6 +60,15 @@ internal class Resources {
}
}
+ ///
+ /// Looks up a localized string similar to A configuration object must be specified..
+ ///
+ internal static string Error_NoConfiguration {
+ get {
+ return ResourceManager.GetString("Error_NoConfiguration", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to A dependency resolver must be specified..
///
diff --git a/src/Microsoft.AspNet.SignalR.Owin/Resources.resx b/src/Microsoft.AspNet.SignalR.Owin/Resources.resx
index 0ad631261d..a43b7d6f72 100644
--- a/src/Microsoft.AspNet.SignalR.Owin/Resources.resx
+++ b/src/Microsoft.AspNet.SignalR.Owin/Resources.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ A configuration object must be specified.
+
A dependency resolver must be specified.
diff --git a/tests/Microsoft.AspNet.SignalR.Tests.Common/App_Start/RegisterHubs.cs b/tests/Microsoft.AspNet.SignalR.Tests.Common/App_Start/RegisterHubs.cs
index 5ecc646b7d..37cb5c1d38 100644
--- a/tests/Microsoft.AspNet.SignalR.Tests.Common/App_Start/RegisterHubs.cs
+++ b/tests/Microsoft.AspNet.SignalR.Tests.Common/App_Start/RegisterHubs.cs
@@ -34,7 +34,7 @@ public static void Start()
{
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(connectionTimeout);
}
-
+
int disconnectTimeout;
if (Int32.TryParse(disconnectTimeoutRaw, out disconnectTimeout))
{
@@ -54,8 +54,12 @@ public static void Start()
GlobalHost.HubPipeline.EnableAutoRejoiningGroups();
}
+ var config = new HubConfiguration
+ {
+ EnableCrossDomain = true
+ };
- RouteTable.Routes.MapHubs();
+ RouteTable.Routes.MapHubs(config);
RouteTable.Routes.MapHubs("signalr.hubs2", "/signalr2/test", new HubConfiguration());
RouteTable.Routes.MapConnection("errors-are-fun", "ErrorsAreFun");