Permalink
Browse files

Made websocket implementation for self host on .NET 4.5.

- Made a .NET 45 solution with asp.net and self host impl for 4.5
- Shared websocket logic between asp.net host and self host.
- Made AcceptWebSocket return a Task instead of void.
- Updated websocket js to wait for reconncetDelay instead of hammering the server with reconnects.
  • Loading branch information...
1 parent 712c92b commit e94d091100a86396d6efa81f72fca6198cbb57f1 @davidfowl davidfowl committed Jul 1, 2012
Showing with 540 additions and 122 deletions.
  1. +1 −1 Build/Build.proj
  2. +4 −2 SignalR.Hosting.AspNet/AspNetRequest.cs
  3. +0 −43 SignalR.Hosting.AspNet45.sln
  4. +1 −1 SignalR.Hosting.AspNet45/Infrastructure/ByteBuffer.cs
  5. +1 −1 SignalR.Hosting.AspNet45/Infrastructure/TaskQueue.cs
  6. +1 −1 SignalR.Hosting.AspNet45/Properties/AssemblyInfo.cs
  7. +1 −1 SignalR.Hosting.AspNet45/SignalR.Hosting.AspNet45.csproj
  8. +2 −3 SignalR.Hosting.AspNet45/{AspNetWebSocketHandler.cs → WebSockets/DefaultWebSocketHandler.cs}
  9. +5 −7 SignalR.Hosting.AspNet45/WebSockets/WebSocketHandler.cs
  10. +1 −1 SignalR.Hosting.AspNet45/WebSockets/WebSocketMessage.cs
  11. +2 −2 SignalR.Hosting.AspNet45/WebSockets/WebSocketMessageReader.cs
  12. +3 −1 SignalR.Hosting.Memory/Request.cs
  13. +2 −2 SignalR.Hosting.Owin/OwinRequest.cs
  14. +23 −13 SignalR.Hosting.Self/HttpListenerRequestWrapper.cs
  15. +11 −2 SignalR.Hosting.Self/HttpListenerResponseWrapper.cs
  16. +4 −0 SignalR.Hosting.Self/Infrastructure/ResponseExtensions.cs
  17. +5 −1 SignalR.Hosting.Self/Server.cs
  18. +10 −0 SignalR.Hosting.Self45/Properties/AssemblyInfo.cs
  19. +104 −0 SignalR.Hosting.Self45/SignalR.Hosting.Self45.csproj
  20. +1 −1 SignalR/Hosting/IRequest.cs
  21. +12 −8 SignalR/Scripts/jquery.signalR.js
  22. +1 −1 SignalR/Scripts/jquery.signalR.min.js
  23. +1 −3 SignalR/Transports/WebSocketTransport.cs
  24. +96 −0 SignalR45.sln
  25. +12 −8 samples/SignalR.Hosting.AspNet.Samples/Scripts/jquery.signalR.js
  26. +1 −1 samples/SignalR.Hosting.AspNet.Samples/Scripts/jquery.signalR.min.js
  27. +12 −8 samples/SignalR.Hosting.AspNet45.Samples/Scripts/jquery.signalR.js
  28. +1 −1 samples/SignalR.Hosting.AspNet45.Samples/Scripts/jquery.signalR.min.js
  29. +12 −8 samples/SignalR.Hosting.Owin.Samples/Content/Scripts/jquery.signalR.js
  30. +1 −1 samples/SignalR.Hosting.Owin.Samples/Content/Scripts/jquery.signalR.min.js
  31. +58 −0 samples/SignalR.Hosting.Self45.Samples/Program.cs
  32. +36 −0 samples/SignalR.Hosting.Self45.Samples/Properties/AssemblyInfo.cs
  33. +89 −0 samples/SignalR.Hosting.Self45.Samples/SignalR.Hosting.Self.Samples.csproj
  34. +22 −0 samples/SignalR.Hosting.Self45.Samples/app.config
  35. +4 −0 samples/SignalR.Hosting.Self45.Samples/packages.config
View
@@ -115,7 +115,7 @@
Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\WindowsXaml')"
Properties="Configuration=$(Configuration)" />
- <MSBuild Projects="$(ProjectRoot)\SignalR.Hosting.AspNet45.sln"
+ <MSBuild Projects="$(ProjectRoot)\SignalR45.sln"
Targets="Build"
Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0\Web')"
Properties="Configuration=$(Configuration)" />
@@ -146,16 +146,18 @@ private static GetUnvalidatedCollections ResolveCollectionsMethod()
return (GetUnvalidatedCollections)Delegate.CreateDelegate(typeof(GetUnvalidatedCollections), firstArgument: null, method: getUnvalidatedCollectionsMethod);
}
- public void AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
+ public Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
{
#if NET45
_context.AcceptWebSocketRequest(ws =>
{
- var handler = new AspNetWebSocketHandler();
+ var handler = new SignalR.WebSockets.DefaultWebSocketHandler();
var task = handler.ProcessWebSocketRequestAsync(ws);
callback(handler).Then(h => h.CleanClose(), handler);
return task;
});
+
+ return TaskAsyncHelper.Empty;
#else
throw new NotSupportedException();
#endif
@@ -1,43 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 11
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Hosting.AspNet45", "SignalR.Hosting.AspNet45\SignalR.Hosting.AspNet45.csproj", "{6F53F576-0E35-4E89-8D4D-B6B40084A16C}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR", "SignalR\SignalR.csproj", "{1B9A82C4-BCA1-4834-A33E-226F17BE070B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Hosting.Common", "SignalR.Hosting.Common\SignalR.Hosting.Common.csproj", "{3B71F0AE-D4B6-4F47-BF62-333D45615673}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{A7D75EA8-1439-4D57-9B88-EC60842B5FB3}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Hosting.AspNet.Samples", "samples\SignalR.Hosting.AspNet45.Samples\SignalR.Hosting.AspNet.Samples.csproj", "{1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- 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}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1B9A82C4-BCA1-4834-A33E-226F17BE070B}.Release|Any CPU.Build.0 = 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}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82}.Release|Any CPU.Build.0 = Release|Any CPU
- {3B71F0AE-D4B6-4F47-BF62-333D45615673}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3B71F0AE-D4B6-4F47-BF62-333D45615673}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3B71F0AE-D4B6-4F47-BF62-333D45615673}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3B71F0AE-D4B6-4F47-BF62-333D45615673}.Release|Any CPU.Build.0 = Release|Any CPU
- {6F53F576-0E35-4E89-8D4D-B6B40084A16C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6F53F576-0E35-4E89-8D4D-B6B40084A16C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6F53F576-0E35-4E89-8D4D-B6B40084A16C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6F53F576-0E35-4E89-8D4D-B6B40084A16C}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {1EA34A62-E03E-45CF-A9C9-82D2DA0FCD82} = {A7D75EA8-1439-4D57-9B88-EC60842B5FB3}
- EndGlobalSection
-EndGlobal
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
-namespace SignalR.Hosting.AspNet.Infrastructure
+namespace SignalR.Infrastructure
{
// Similar to MemoryStream, but tries to allocate as few objects as possible on the LOH
@@ -1,7 +1,7 @@
using System;
using System.Threading.Tasks;
-namespace SignalR.Hosting.AspNet.Infrastructure
+namespace SignalR.Infrastructure
{
// Allows serial queuing of Task instances
// The tasks are not called on the current synchronization context
@@ -2,4 +2,4 @@
[assembly: AssemblyTitle("SignalR.Hosting.AspNet45")]
[assembly: AssemblyDescription("Asp.Net host for SignalR on .NET 4.5")]
-[assembly: AssemblyVersion("0.5.1.0")]
+[assembly: AssemblyVersion("0.5.2.0")]
@@ -89,7 +89,7 @@
<Compile Include="..\SignalR\TaskAsyncHelper.cs">
<Link>Infrastructure\TaskAsyncHelper.cs</Link>
</Compile>
- <Compile Include="AspNetWebSocketHandler.cs" />
+ <Compile Include="WebSockets\DefaultWebSocketHandler.cs" />
<Compile Include="Infrastructure\ByteBuffer.cs" />
<Compile Include="Infrastructure\TaskQueue.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -1,10 +1,9 @@
using System;
using System.Threading.Tasks;
-using SignalR.Hosting.AspNet.WebSockets;
-namespace SignalR.Hosting.AspNet
+namespace SignalR.WebSockets
{
- internal class AspNetWebSocketHandler : WebSocketHandler, IWebSocket
+ internal class DefaultWebSocketHandler : WebSocketHandler, IWebSocket
{
private bool _raiseEvent = true;
@@ -1,14 +1,12 @@
using System;
-using System.ComponentModel;
using System.Net.WebSockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using System.Web.WebSockets;
-using SignalR.Hosting.AspNet.Infrastructure;
+using SignalR.Infrastructure;
-namespace SignalR.Hosting.AspNet.WebSockets
+namespace SignalR.WebSockets
{
internal class WebSocketHandler
{
@@ -108,14 +106,14 @@ public int MaxIncomingMessageSize
* MISC PROPERTIES
*/
- public AspNetWebSocketContext WebSocketContext { get; set; }
+ public WebSocketContext WebSocketContext { get; set; }
public Exception Error { get; set; }
/*
* IMPLEMENTATION
*/
- public Task ProcessWebSocketRequestAsync(AspNetWebSocketContext webSocketContext)
+ public Task ProcessWebSocketRequestAsync(WebSocketContext webSocketContext)
{
if (webSocketContext == null)
{
@@ -127,7 +125,7 @@ public Task ProcessWebSocketRequestAsync(AspNetWebSocketContext webSocketContext
return ProcessWebSocketRequestAsync(webSocketContext, () => WebSocketMessageReader.ReadMessageAsync(webSocket, buffer, MaxIncomingMessageSize));
}
- internal async Task ProcessWebSocketRequestAsync(AspNetWebSocketContext webSocketContext, Func<Task<WebSocketMessage>> messageRetriever)
+ internal async Task ProcessWebSocketRequestAsync(WebSocketContext webSocketContext, Func<Task<WebSocketMessage>> messageRetriever)
{
try
{
@@ -1,7 +1,7 @@
using System;
using System.Net.WebSockets;
-namespace SignalR.Hosting.AspNet.WebSockets
+namespace SignalR.WebSockets
{
internal sealed class WebSocketMessage
{
@@ -3,9 +3,9 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using SignalR.Hosting.AspNet.Infrastructure;
+using SignalR.Infrastructure;
-namespace SignalR.Hosting.AspNet.WebSockets
+namespace SignalR.WebSockets
{
internal static class WebSocketMessageReader
{
@@ -109,8 +109,10 @@ public IPrincipal User
}
}
- public void AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
+ public Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
{
+ // TODO: Add support
+ throw new NotSupportedException();
}
}
}
@@ -97,9 +97,9 @@ private Uri BuildUrl(Gate.Environment env)
return new Uri(url);
}
- public void AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
+ public Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
{
-
+ throw new NotSupportedException();
}
}
}
@@ -10,20 +10,20 @@ namespace SignalR.Hosting.Self
{
public class HttpListenerRequestWrapper : IRequest
{
- private readonly HttpListenerRequest _httpListenerRequest;
+ private readonly HttpListenerContext _httpListenerContext;
private NameValueCollection _form;
- public HttpListenerRequestWrapper(HttpListenerRequest httpListenerRequest, IPrincipal user)
+ public HttpListenerRequestWrapper(HttpListenerContext httpListenerContext)
{
- _httpListenerRequest = httpListenerRequest;
- QueryString = new NameValueCollection(httpListenerRequest.QueryString);
- Headers = new NameValueCollection(httpListenerRequest.Headers);
- Cookies = new CookieCollectionWrapper(_httpListenerRequest.Cookies);
+ _httpListenerContext = httpListenerContext;
+ QueryString = new NameValueCollection(httpListenerContext.Request.QueryString);
+ Headers = new NameValueCollection(httpListenerContext.Request.Headers);
+ Cookies = new CookieCollectionWrapper(httpListenerContext.Request.Cookies);
ServerVariables = new NameValueCollection();
- User = user;
+ User = httpListenerContext.User;
// Set the client IP
- ServerVariables["REMOTE_ADDR"] = _httpListenerRequest.RemoteEndPoint.Address.ToString();
+ ServerVariables["REMOTE_ADDR"] = httpListenerContext.Request.RemoteEndPoint.Address.ToString();
}
public IRequestCookieCollection Cookies
@@ -57,7 +57,7 @@ public Uri Url
{
get
{
- return _httpListenerRequest.Url;
+ return _httpListenerContext.Request.Url;
}
}
@@ -78,23 +78,33 @@ private void EnsureForm()
if (_form == null)
{
// Do nothing if there's no body
- if (!_httpListenerRequest.HasEntityBody)
+ if (!_httpListenerContext.Request.HasEntityBody)
{
_form = new NameValueCollection();
return;
}
- using (var sw = new StreamReader(_httpListenerRequest.InputStream))
+ using (var sw = new StreamReader(_httpListenerContext.Request.InputStream))
{
var body = sw.ReadToEnd();
_form = HttpUtility.ParseDelimited(body);
}
}
}
- public void AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
+ public Task AcceptWebSocketRequest(Func<IWebSocket, Task> callback)
{
-
+#if NET45
+ return _httpListenerContext.AcceptWebSocketAsync(subProtocol: null).Then(ws =>
+ {
+ var handler = new SignalR.WebSockets.DefaultWebSocketHandler();
+ var task = handler.ProcessWebSocketRequestAsync(ws);
+ callback(handler).Then(h => h.CleanClose(), handler);
+ return task;
+ });
+#else
+ throw new NotSupportedException();
+#endif
}
}
}
@@ -45,8 +45,17 @@ public Task WriteAsync(ArraySegment<byte> data)
{
Interlocked.Exchange(ref _onInitialWrite, () => { }).Invoke();
- return DoWrite(data).Then(response => response.OutputStream.Flush(), _httpListenerResponse)
- .Catch(ex => _ended = true);
+ return DoWrite(data).Then(response =>
+ {
+#if NET45
+ return response.OutputStream.FlushAsync();
+#else
+ response.OutputStream.Flush();
+ return TaskAsyncHelper.Empty;
+#endif
+
+ }, _httpListenerResponse)
+ .Catch(ex => _ended = true);
}
public Task EndAsync(ArraySegment<byte> data)
@@ -27,6 +27,9 @@ public static Task WriteAsync(this HttpListenerResponse response, string value)
public static Task WriteAsync(this HttpListenerResponse response, ArraySegment<byte> buffer)
{
+#if NET45
+ return response.OutputStream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count);
+#else
try
{
return Task.Factory.FromAsync((cb, state) => response.OutputStream.BeginWrite(buffer.Array, buffer.Offset, buffer.Count, cb, state),
@@ -37,6 +40,7 @@ public static Task WriteAsync(this HttpListenerResponse response, ArraySegment<b
{
return TaskAsyncHelper.FromError(ex);
}
+#endif
}
public static void CloseSafe(this HttpListenerResponse response)
@@ -160,10 +160,14 @@ private Task ProcessRequestAsync(HttpListenerContext context)
context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
}
- var request = new HttpListenerRequestWrapper(context.Request, context.User);
+ var request = new HttpListenerRequestWrapper(context);
var response = new HttpListenerResponseWrapper(context.Response, () => RegisterForDisconnect(context, cts.Cancel), cts.Token);
var hostContext = new HostContext(request, response);
+#if NET45
+ hostContext.Items[HostConstants.SupportsWebSockets] = true;
+#endif
+
if (OnProcessRequest != null)
{
OnProcessRequest(hostContext);
@@ -0,0 +1,10 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SignalR.Hosting.Self45")]
+[assembly: AssemblyDescription("HttpListener host for SignalR on .NET 4.5")]
+[assembly: AssemblyVersion("0.5.2.0")]
Oops, something went wrong.

0 comments on commit e94d091

Please sign in to comment.