Skip to content
This repository was archived by the owner on Feb 1, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
src/Owin.WebSocket/bin
src/Owin.WebSocket/obj
src/Owin.WebSocket/packages
src/Owin.WebSocket.Fleck/bin
src/Owin.WebSocket.Fleck/obj
src/packages
src/UnitTests/bin
src/UnitTests/obj
src/build
src/UnitTests.Fleck/bin
src/UnitTests.Fleck/obj
*.DotSettings
*.dll
*.pdb
*.suo
Expand Down
56 changes: 56 additions & 0 deletions src/Owin.WebSocket.Fleck/Extensions/AppBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Text.RegularExpressions;
using Fleck;
using Microsoft.Practices.ServiceLocation;

namespace Owin.WebSocket.Extensions
{
public static class AppBuilderExtensions
{
public static IAppBuilder MapFleckRoute(this IAppBuilder app, Action<IWebSocketConnection> config, IServiceLocator locator = null, string regexPatternMatch = null)
{
return app.MapFleckRoute(string.Empty, config, locator, regexPatternMatch);
}

public static IAppBuilder MapFleckRoute(this IAppBuilder app, string route, Action<IWebSocketConnection> config, IServiceLocator locator = null, string regexPatternMatch = null)
{
return app.MapFleckRoute<FleckWebSocketConnection>(route, config, locator, regexPatternMatch);
}

public static IAppBuilder MapFleckRoute<T>(this IAppBuilder app, Action<IWebSocketConnection> config, IServiceLocator locator = null, string regexPatternMatch = null)
where T : FleckWebSocketConnection, new()
{
return app.MapFleckRoute<T>(string.Empty, config, locator, regexPatternMatch);
}

public static IAppBuilder MapFleckRoute<T>(this IAppBuilder app, string route, Action<IWebSocketConnection> config, IServiceLocator locator = null, string regexPatternMatch = null)
where T : FleckWebSocketConnection, new()
{
var regex = string.IsNullOrWhiteSpace(regexPatternMatch) ? null : new Regex(regexPatternMatch, RegexOptions.Compiled | RegexOptions.IgnoreCase);
return app.Map(route, a => a.Use<FleckWebSocketConnectionMiddleware<T>>(config, locator, regex));
}

public static IAppBuilder MapOwinFleckRoute(this IAppBuilder app, Action<IOwinWebSocketConnection> config, IServiceLocator locator = null, string regexPatternMatch = null)
{
return app.MapOwinFleckRoute(string.Empty, config, locator, regexPatternMatch);
}

public static IAppBuilder MapOwinFleckRoute(this IAppBuilder app, string route, Action<IOwinWebSocketConnection> config, IServiceLocator locator = null, string regexPatternMatch = null)
{
return app.MapOwinFleckRoute<FleckWebSocketConnection>(route, config, locator, regexPatternMatch);
}

public static IAppBuilder MapOwinFleckRoute<T>(this IAppBuilder app, Action<IOwinWebSocketConnection> config, IServiceLocator locator = null, string regexPatternMatch = null)
where T : FleckWebSocketConnection, new()
{
return app.MapOwinFleckRoute<T>(string.Empty, config, locator, regexPatternMatch);
}

public static IAppBuilder MapOwinFleckRoute<T>(this IAppBuilder app, string route, Action<IOwinWebSocketConnection> config, IServiceLocator locator = null, string regexPatternMatch = null)
where T : FleckWebSocketConnection, new()
{
var regex = string.IsNullOrWhiteSpace(regexPatternMatch) ? null : new Regex(regexPatternMatch, RegexOptions.Compiled | RegexOptions.IgnoreCase);
return app.Map(route, a => a.Use<FleckWebSocketConnectionMiddleware<T>>(config, locator, regex));
}
}
}
160 changes: 160 additions & 0 deletions src/Owin.WebSocket.Fleck/FleckWebSocketConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using System;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using Fleck;
using Microsoft.Owin;
using Owin.WebSocket.Models;

namespace Owin.WebSocket
{
public class FleckWebSocketConnection : WebSocketConnection, IOwinWebSocketConnection
{
private const string PingPongError = "Owin handles ping pong messages internally";

private readonly OwinWebSocketContext _context;
private readonly IOwinWebSocketConnection _connection;
private readonly IWebSocketConnectionInfo _connectionInfo;

private bool _isAvailable;

public FleckWebSocketConnection()
: this(1024*64)
{
}

public FleckWebSocketConnection(int maxMessageSize)
: base(maxMessageSize)
{
_connection = this;
_context = new OwinWebSocketContext(_connection.Context, MaxMessageSize);
_connectionInfo = new FleckWebSocketConnectionInfo(_connection.Context);
}

#region WebSocketConnection

public override bool AuthenticateRequest(IOwinRequest request)
{
return _connection.OnAuthenticateRequest?.Invoke() ?? true;
}

public override Task<bool> AuthenticateRequestAsync(IOwinRequest request)
{
return _connection.OnAuthenticateRequestAsync?.Invoke() ?? Task.FromResult(true);
}

public override void OnOpen()
{
_isAvailable = true;
_connection.OnOpen?.Invoke();
}

public override Task OnOpenAsync()
{
return _connection.OnOpenAsync?.Invoke() ?? Task.FromResult(true);
}

public override void OnClose(WebSocketCloseStatus? closeStatus, string closeStatusDescription)
{
_isAvailable = false;
_context.CloseStatus = closeStatus;
_context.CloseStatusDescription = closeStatusDescription;
_connection.OnClose?.Invoke();
}

public override Task OnCloseAsync(WebSocketCloseStatus? closeStatus, string closeStatusDescription)
{
return _connection.OnCloseAsync?.Invoke() ?? Task.FromResult(true);
}

public override Task OnMessageReceived(ArraySegment<byte> message, WebSocketMessageType type)
{
if (type == WebSocketMessageType.Binary && (_connection.OnBinary != null || _connection.OnBinaryAsync != null))
{
var array = message.ToArray();
_connection.OnBinary?.Invoke(array);
return _connection.OnBinaryAsync?.Invoke(array) ?? Task.FromResult(true);
}

if (type == WebSocketMessageType.Text && (_connection.OnMessage != null || _connection.OnMessageAsync!= null))
{
var @string = Encoding.UTF8.GetString(message.Array, 0, message.Count);
_connection.OnMessage?.Invoke(@string);
return _connection.OnMessageAsync?.Invoke(@string) ?? Task.FromResult(true);
}

return Task.FromResult(true);
}

public override void OnReceiveError(Exception error)
{
_connection.OnError?.Invoke(error);
}

#endregion

#region IOwinWebSocketConnection

IOwinWebSocketContext IOwinWebSocketConnection.Context => _context;
Func<Task> IOwinWebSocketConnection.OnOpenAsync { get; set; }
Func<Task> IOwinWebSocketConnection.OnCloseAsync { get; set; }
Func<string, Task> IOwinWebSocketConnection.OnMessageAsync { get; set; }
Func<byte[], Task> IOwinWebSocketConnection.OnBinaryAsync { get; set; }
Func<bool> IOwinWebSocketConnection.OnAuthenticateRequest { get; set; }
Func<Task<bool>> IOwinWebSocketConnection.OnAuthenticateRequestAsync { get; set; }

#endregion

#region IWebSocketConnection

Action IWebSocketConnection.OnOpen { get; set; }
Action IWebSocketConnection.OnClose { get; set; }
Action<string> IWebSocketConnection.OnMessage { get; set; }
Action<byte[]> IWebSocketConnection.OnBinary { get; set; }
Action<Exception> IWebSocketConnection.OnError { get; set; }

Action<byte[]> IWebSocketConnection.OnPing
{
get { return null; }
set { throw new NotSupportedException(PingPongError); }
}

Action<byte[]> IWebSocketConnection.OnPong
{
get { return null; }
set { throw new NotSupportedException(PingPongError); }
}

IWebSocketConnectionInfo IWebSocketConnection.ConnectionInfo => _connectionInfo;
bool IWebSocketConnection.IsAvailable => _isAvailable;

Task IWebSocketConnection.Send(string message)
{
var bytes = Encoding.UTF8.GetBytes(message);
return SendText(bytes, true);
}

Task IWebSocketConnection.Send(byte[] message)
{
return SendBinary(message, true);
}

Task IWebSocketConnection.SendPing(byte[] message)
{
throw new NotSupportedException(PingPongError);
}

Task IWebSocketConnection.SendPong(byte[] message)
{
throw new NotSupportedException(PingPongError);
}

void IWebSocketConnection.Close()
{
Close(WebSocketCloseStatus.NormalClosure, string.Empty).Wait();
}

#endregion
}
}
64 changes: 64 additions & 0 deletions src/Owin.WebSocket.Fleck/FleckWebSocketConnectionMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Fleck;
using Microsoft.Owin;
using Microsoft.Practices.ServiceLocation;

namespace Owin.WebSocket
{
public class FleckWebSocketConnectionMiddleware<T> : OwinMiddleware
where T : FleckWebSocketConnection, new()
{
private readonly Action<IWebSocketConnection> _config;
private readonly Action<IOwinWebSocketConnection> _owinConfig;
private readonly IServiceLocator _locator;
private readonly Regex _matchPattern;

public FleckWebSocketConnectionMiddleware(OwinMiddleware next, Action<IWebSocketConnection> config, IServiceLocator locator, Regex matchPattern)
: this(next, locator, matchPattern)
{
_config = config;
}

public FleckWebSocketConnectionMiddleware(OwinMiddleware next, Action<IOwinWebSocketConnection> config, IServiceLocator locator, Regex matchPattern)
: this(next, locator, matchPattern)
{
_owinConfig = config;
}

private FleckWebSocketConnectionMiddleware(OwinMiddleware next, IServiceLocator locator, Regex matchPattern)
: base(next)
{
_locator = locator;
_matchPattern = matchPattern;
}

public override Task Invoke(IOwinContext context)
{
var matches = new Dictionary<string, string>();

if (_matchPattern != null)
{
var match = _matchPattern.Match(context.Request.Path.Value);
if (!match.Success)
return Next.Invoke(context);

for (var i = 1; i <= match.Groups.Count; i++)
{
var name = _matchPattern.GroupNameFromNumber(i);
var value = match.Groups[i];
matches.Add(name, value.Value);
}
}

var connection = _locator?.GetInstance<T>() ?? new T();

_config?.Invoke(connection);
_owinConfig?.Invoke(connection);

return connection.AcceptSocketAsync(context, matches);
}
}
}
18 changes: 18 additions & 0 deletions src/Owin.WebSocket.Fleck/IOwinWebSocketConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Threading.Tasks;
using Fleck;
using Owin.WebSocket.Models;

namespace Owin.WebSocket
{
public interface IOwinWebSocketConnection : IWebSocketConnection
{
IOwinWebSocketContext Context { get; }
Func<Task> OnOpenAsync { get; set; }
Func<Task> OnCloseAsync { get; set; }
Func<string, Task> OnMessageAsync { get; set; }
Func<byte[], Task> OnBinaryAsync { get; set; }
Func<bool> OnAuthenticateRequest { get; set; }
Func<Task<bool>> OnAuthenticateRequestAsync { get; set; }
}
}
54 changes: 54 additions & 0 deletions src/Owin.WebSocket.Fleck/Models/FleckWebSocketConnectionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Fleck;
using Microsoft.Owin;

namespace Owin.WebSocket.Models
{
public class FleckWebSocketConnectionInfo : IWebSocketConnectionInfo
{
private readonly IOwinContext _context;

public FleckWebSocketConnectionInfo(IOwinContext context)
{
_context = context;
}

string IWebSocketConnectionInfo.SubProtocol
{
get
{
const string protocol = "Sec-WebSocket-Protocol";
return _context.Request.Headers.ContainsKey(protocol)
? _context.Request.Headers[protocol]
: string.Empty;
}
}

string IWebSocketConnectionInfo.Origin
{
get
{
const string origin1 = "Origin";
if (_context.Request.Headers.ContainsKey(origin1))
return _context.Request.Headers[origin1];

const string origin2 = "Sec-WebSocket-Origin";
return _context.Request.Headers.ContainsKey(origin2)
? _context.Request.Headers[origin2]
: string.Empty;
}
}

string IWebSocketConnectionInfo.Host => _context.Request.Host.Value;
string IWebSocketConnectionInfo.Path => _context.Request.Path.Value;
string IWebSocketConnectionInfo.ClientIpAddress => _context.Request.RemoteIpAddress;
int IWebSocketConnectionInfo.ClientPort => _context.Request.RemotePort ?? 0;
IDictionary<string, string> IWebSocketConnectionInfo.Cookies => _context.Request.Cookies.ToDictionary(k => k.Key, v => v.Value);
IDictionary<string, string> IWebSocketConnectionInfo.Headers => _context.Request.Headers.SelectMany(p => p.Value.Select(v => new { p.Key, Value = v })).ToDictionary(k => k.Key, v => v.Value);

Guid IWebSocketConnectionInfo.Id { get; } = Guid.NewGuid();
string IWebSocketConnectionInfo.NegotiatedSubProtocol { get; } = string.Empty;
}
}
12 changes: 12 additions & 0 deletions src/Owin.WebSocket.Fleck/Models/IOwinWebSocketContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Net.WebSockets;
using Microsoft.Owin;

namespace Owin.WebSocket.Models
{
public interface IOwinWebSocketContext : IOwinContext
{
int MaxMessageSize { get; }
WebSocketCloseStatus? CloseStatus { get; }
string CloseStatusDescription { get; }
}
}
Loading