Permalink
Browse files

Added AutoTransport which is will pick the best transport supported b…

…y the client and server.

- Added ConnectTimeout to ServerSentEventsTransport.
- Handle stopping the SSE transport better.
- Added Wp7 sample.
- Added Delay event based on threading timers to the TaskAsyncHelper.
  • Loading branch information...
1 parent a4eb02d commit 4b4db04e92b39f799c38cf7600f8decd73512086 @davidfowl davidfowl committed Jan 15, 2012
@@ -28,7 +28,7 @@ public MainPage()
});
};
- connection.Start(Transport.LongPolling).ContinueWith(task =>
+ connection.Start().ContinueWith(task =>
{
Debug.WriteLine("ERROR: {0}", task.Exception.GetBaseException().Message);
},
@@ -103,6 +103,9 @@
<Compile Include="..\SignalR.Client\NegotiationResponse.cs">
<Link>NegotiationResponse.cs</Link>
</Compile>
+ <Compile Include="..\SignalR.Client\Transports\AutoTransport.cs">
+ <Link>Transports\AutoTransport.cs</Link>
+ </Compile>
<Compile Include="..\SignalR.Client\Transports\HttpBasedTransport.cs">
<Link>Transports\HttpBasedTransport.cs</Link>
</Compile>
@@ -105,6 +105,9 @@
<Compile Include="..\SignalR.Client\NegotiationResponse.cs">
<Link>NegotiationResponse.cs</Link>
</Compile>
+ <Compile Include="..\SignalR.Client\Transports\AutoTransport.cs">
+ <Link>Transports\AutoTransport.cs</Link>
+ </Compile>
<Compile Include="..\SignalR.Client\Transports\HttpBasedTransport.cs">
<Link>Transports\HttpBasedTransport.cs</Link>
</Compile>
@@ -18,6 +18,9 @@ public class Connection : IConnection
private IClientTransport _transport;
private bool _initialized;
+ // Used by transports to sync
+ internal int _initializedCalled;
+
public event Action<string> Received;
public event Action<Exception> Error;
public event Action Closed;
@@ -52,7 +55,8 @@ public Connection(string url)
public Task Start()
{
- return Start(Transport.ServerSentEvents);
+ // Pick the best transport supported by the client
+ return Start(new AutoTransport());
}
public virtual Task Start(IClientTransport transport)
@@ -73,6 +73,7 @@
<Compile Include="ObservableConnection.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Hubs\HubProxyExtensions.cs" />
+ <Compile Include="Transports\AutoTransport.cs" />
<Compile Include="Transports\HttpBasedTransport.cs" />
<Compile Include="Transports\IClientTransport.cs" />
<Compile Include="Transports\LongPollingTransport.cs" />
@@ -0,0 +1,66 @@
+using System.Threading.Tasks;
+
+namespace SignalR.Client.Transports
+{
+ public class AutoTransport : IClientTransport
+ {
+ // Transport that's in use
+ private IClientTransport _transport;
+
+ // List of transports in fallback order
+ private static readonly IClientTransport[] _transports = new[] { Transport.ServerSentEvents, Transport.LongPolling };
+
+ public Task Start(Connection connection, string data)
+ {
+ var tcs = new TaskCompletionSource<object>();
+
+ // Resolve the transport
+ ResolveTransport(connection, data, tcs, 0);
+
+ return tcs.Task;
+ }
+
+ private void ResolveTransport(Connection connection, string data, TaskCompletionSource<object> tcs, int index)
+ {
+ // Pick the current transport
+ IClientTransport transport = _transports[index];
+
+ transport.Start(connection, data).ContinueWith(task =>
+ {
+ if (task.IsFaulted)
+ {
+ // If that transport fails to initialize then fallback
+ var next = index + 1;
+ if (next < _transports.Length)
+ {
+ // Try the next transport
+ ResolveTransport(connection, data, tcs, next);
+ }
+ else
+ {
+ // If there's nothing else to try then just fail
+ tcs.SetException(task.Exception);
+ }
+ }
+ else
+ {
+ // Set the active transport
+ _transport = transport;
+
+ // Complete the process
+ tcs.SetResult(null);
+ }
+ });
+ }
+
+ public Task<T> Send<T>(Connection connection, string data)
+ {
+ return _transport.Send<T>(connection, data);
+ }
+
+ public void Stop(Connection connection)
+ {
+ _transport.Stop(connection);
+ }
+ }
+}
@@ -10,13 +10,20 @@ namespace SignalR.Client.Transports
public class ServerSentEventsTransport : HttpBasedTransport
{
private const string ReaderKey = "sse.reader";
+
private static readonly TimeSpan ReconnectDelay = TimeSpan.FromSeconds(2);
public ServerSentEventsTransport()
: base("serverSentEvents")
{
+ ConnectionTimeout = TimeSpan.FromSeconds(2);
}
+ /// <summary>
+ /// Time allowed before failing the connect request
+ /// </summary>
+ public TimeSpan ConnectionTimeout { get; set; }
+
protected override void OnStart(Connection connection, string data, Action initializeCallback, Action<Exception> errorCallback)
{
OpenConnection(connection, data, initializeCallback, errorCallback);
@@ -41,14 +48,19 @@ private void OpenConnection(Connection connection, string data, Action initializ
{
if (task.IsFaulted)
{
- if (errorCallback != null)
- {
- errorCallback(task.Exception);
- }
- else
+ var exception = task.Exception.GetBaseException();
+ if (!IsRequestAborted(exception) &&
+ Interlocked.CompareExchange(ref connection._initializedCalled, 0, 0) == 0)
{
- // Raise the error event if we failed to reconnect
- connection.OnError(task.Exception.GetBaseException());
+ if (errorCallback != null)
+ {
+ errorCallback(exception);
+ }
+ else
+ {
+ // Raise the error event if we failed to reconnect
+ connection.OnError(exception);
+ }
}
}
else
@@ -57,7 +69,13 @@ private void OpenConnection(Connection connection, string data, Action initializ
var stream = task.Result.GetResponseStream();
var reader = new AsyncStreamReader(stream,
connection,
- initializeCallback,
+ () =>
+ {
+ if (Interlocked.CompareExchange(ref connection._initializedCalled, 1, 0) == 0)
+ {
+ initializeCallback();
+ }
+ },
() =>
{
// Wait for a bit before reconnecting
@@ -72,6 +90,21 @@ private void OpenConnection(Connection connection, string data, Action initializ
connection.Items[ReaderKey] = reader;
}
});
+
+ if (initializeCallback != null)
+ {
+ TaskAsyncHelper.Delay(ConnectionTimeout).Then(() =>
+ {
+ if (Interlocked.CompareExchange(ref connection._initializedCalled, 1, 0) == 0)
+ {
+ // Stop the connection
+ Stop(connection);
+
+ // Connection timeout occured
+ errorCallback(new TimeoutException());
+ }
+ });
+ }
}
protected override void OnBeforeAbort(Connection connection)
@@ -12,7 +12,7 @@
<RootNamespace>SignalR.Samples</RootNamespace>
<AssemblyName>SignalR.Samples</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
- <UseIISExpress>false</UseIISExpress>
+ <UseIISExpress>true</UseIISExpress>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
@@ -244,7 +244,7 @@
<AutoAssignPort>False</AutoAssignPort>
<DevelopmentServerPort>40476</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
- <IISUrl>http://localhost/SignalR.Samples</IISUrl>
+ <IISUrl>http://localhost:40476/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
View
@@ -26,46 +26,112 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.ScaleOut", "SignalR
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.AspNet", "SignalR.AspNet\SignalR.AspNet.csproj", "{0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Client.Samples", "SignalR.Client.Samples\SignalR.Client.Samples.csproj", "{E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|Mixed Platforms = Debug|Mixed Platforms
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|Mixed Platforms = Release|Mixed Platforms
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Debug|x86.ActiveCfg = Debug|Any CPU
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|x86.ActiveCfg = Release|Any CPU
{EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Debug|x86.ActiveCfg = Debug|Any CPU
{EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {EB46B9C6-EE37-48F9-835E-E49580E40E0A}.Release|x86.ActiveCfg = Release|Any CPU
{2D23C742-9886-4079-A70F-05C7E4401969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D23C742-9886-4079-A70F-05C7E4401969}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D23C742-9886-4079-A70F-05C7E4401969}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {2D23C742-9886-4079-A70F-05C7E4401969}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {2D23C742-9886-4079-A70F-05C7E4401969}.Debug|x86.ActiveCfg = Debug|Any CPU
{2D23C742-9886-4079-A70F-05C7E4401969}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D23C742-9886-4079-A70F-05C7E4401969}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2D23C742-9886-4079-A70F-05C7E4401969}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2D23C742-9886-4079-A70F-05C7E4401969}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {2D23C742-9886-4079-A70F-05C7E4401969}.Release|x86.ActiveCfg = Release|Any CPU
{1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Debug|x86.ActiveCfg = Debug|Any CPU
{1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {1B2BD09D-ECFF-427F-BA58-C59A01EE6A2C}.Release|x86.ActiveCfg = Release|Any CPU
{E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
+ {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Debug|x86.ActiveCfg = Debug|Any CPU
{E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Any CPU.Build.0 = Release|Any CPU
{E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
+ {E059F6F3-E9D5-4113-AF2B-C2D19CE7FAFF}.Release|x86.ActiveCfg = Release|Any CPU
{1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Debug|x86.ActiveCfg = Debug|Any CPU
{1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|x86.ActiveCfg = Release|Any CPU
{32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Debug|x86.ActiveCfg = Debug|Any CPU
{32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {32D16B36-970E-4CF2-B954-78CB1833CBC1}.Release|x86.ActiveCfg = Release|Any CPU
{0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Debug|x86.ActiveCfg = Debug|Any CPU
{0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {0E513AE2-BEA8-40CF-B9F2-102B351F2FB2}.Release|x86.ActiveCfg = Release|Any CPU
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Debug|x86.ActiveCfg = Debug|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Debug|x86.Build.0 = Debug|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Release|Any CPU.ActiveCfg = Release|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Release|Mixed Platforms.Build.0 = Release|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Release|x86.ActiveCfg = Release|x86
+ {E0223FDC-0982-4D80-B6C2-BFAA6C6748C5}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
View
@@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
+using System.Threading;
namespace SignalR
{
@@ -385,6 +386,22 @@ public static Task<T> FastUnwrap<T>(this Task<Task<T>> task)
return innerTask ?? task.Unwrap();
}
+ public static Task Delay(TimeSpan timeOut)
+ {
+ var tcs = new TaskCompletionSource<object>();
+
+ var timer = new Timer(tcs.SetResult,
+ null,
+ timeOut,
+ TimeSpan.FromMilliseconds(-1));
+
+ return tcs.Task.ContinueWith(_ =>
+ {
+ timer.Dispose();
+ },
+ TaskContinuationOptions.ExecuteSynchronously);
+ }
+
public static Task AllSucceeded(this Task[] tasks, Action continuation)
{
return AllSucceeded(tasks, _ => continuation());

0 comments on commit 4b4db04

Please sign in to comment.