Skip to content

Commit 22667db

Browse files
authored
Merge pull request #164 from Code-Sharp/develop
Owin transport
2 parents d055543 + 14450ec commit 22667db

30 files changed

+854
-19
lines changed

NuGet/WampSharp.Owin.nuspec

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
3+
<metadata>
4+
<version>$version$</version>
5+
<authors>CodeSharp</authors>
6+
<id>WampSharp.Owin</id>
7+
<title>WampSharp Owin support</title>
8+
<projectUrl>https://github.com/Code-Sharp/WampSharp/</projectUrl>
9+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
10+
<description>WampSharp Owin support</description>
11+
<tags>websockets,owin,wampws,rpc,pubsub,wampv1,wampv2</tags>
12+
<dependencies>
13+
<group targetFramework="net45">
14+
<dependency id="Microsoft.Owin" version="[3.0.1, )" />
15+
<dependency id="WampSharp.WebSockets" version="[$version$]" />
16+
</group>
17+
</dependencies>
18+
</metadata>
19+
<files>
20+
<file src="bin\net45\WampSharp.Owin.dll" target="lib\net45\WampSharp.Owin.dll" />
21+
<file src="bin\net45\WampSharp.Owin.xml" target="lib\net45\WampSharp.Owin.xml" />
22+
</files>
23+
</package>

NuGet/WampSharp.WebSockets.nuspec

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@
1414
<dependency id="System.Net.WebSockets.Client" version="[4.0.0, )" />
1515
<dependency id="WampSharp" version="[$version$]" />
1616
</group>
17+
<group targetFramework="net45">
18+
<dependency id="WampSharp" version="[$version$]" />
19+
</group>
1720
</dependencies>
1821
</metadata>
1922
<files>
23+
<file src="bin\net45\WampSharp.WebSockets.dll" target="lib\net45\WampSharp.WebSockets.dll" />
24+
<file src="bin\net45\WampSharp.WebSockets.xml" target="lib\net45\WampSharp.WebSockets.xml" />
2025
<file src="bin\netstandard1.3\WampSharp.WebSockets.dll" target="lib\netstandard1.3\WampSharp.WebSockets.dll" />
2126
<file src="bin\netstandard1.3\WampSharp.WebSockets.xml" target="lib\netstandard1.3\WampSharp.WebSockets.xml" />
2227
</files>

src/net45/Extensions/WampSharp.AspNetCore.WebSockets.Server/AspNetCoreWebSocketTransport.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public override void Dispose()
3333

3434
protected override void OpenConnection<TMessage>(WebSocketData original, IWampConnection<TMessage> connection)
3535
{
36-
WebSocketConnection<TMessage> casted = connection as WebSocketConnection<TMessage>;
36+
WebSocketWrapperConnection<TMessage> casted = connection as WebSocketWrapperConnection<TMessage>;
3737

3838
Task task = Task.Run(casted.RunAsync);
3939

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Net;
4+
using Microsoft.Owin;
5+
using WampSharp.V2.Authentication;
6+
7+
namespace WampSharp.Owin
8+
{
9+
internal class OwinCookieProvider : ICookieProvider
10+
{
11+
private readonly IOwinContext mOwinContext;
12+
13+
public OwinCookieProvider(IOwinContext owinContext)
14+
{
15+
mOwinContext = owinContext;
16+
}
17+
18+
public IEnumerable<Cookie> Cookies
19+
{
20+
get
21+
{
22+
return mOwinContext.Request.Cookies.Select(x => new Cookie(x.Key, x.Value));
23+
}
24+
}
25+
26+
public Cookie GetCookieByName(string cookieName)
27+
{
28+
string cookieValue = mOwinContext.Request.Cookies[cookieName];
29+
30+
if (cookieValue != null)
31+
{
32+
return new Cookie(cookieName, cookieValue);
33+
}
34+
35+
return null;
36+
}
37+
}
38+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Microsoft.Owin;
6+
using Owin;
7+
using WampSharp.Core.Listener;
8+
using WampSharp.V2.Authentication;
9+
using WampSharp.V2.Binding;
10+
using WampSharp.V2.Transports;
11+
using WampSharp.WebSockets;
12+
13+
namespace WampSharp.Owin
14+
{
15+
public class OwinWebSocketTransport : WebSocketTransport<WebSocketData>
16+
{
17+
private const string WebSocketSubProtocol = "websocket.SubProtocol";
18+
private const string SecWebSocketProtocolHeader = "Sec-WebSocket-Protocol";
19+
20+
private Func<IOwinContext, Func<Task>, Task> mHandler;
21+
22+
public OwinWebSocketTransport
23+
(IAppBuilder app,
24+
ICookieAuthenticatorFactory authenticatorFactory = null) :
25+
base(authenticatorFactory)
26+
{
27+
mHandler = this.EmptyHandler;
28+
app.Use(HttpHandler);
29+
}
30+
31+
public override void Dispose()
32+
{
33+
mHandler = this.EmptyHandler;
34+
}
35+
36+
protected override void OpenConnection<TMessage>(WebSocketData original, IWampConnection<TMessage> connection)
37+
{
38+
WebSocketWrapperConnection<TMessage> casted = connection as WebSocketWrapperConnection<TMessage>;
39+
40+
Task task = Task.Run((Func<Task>) casted.RunAsync);
41+
42+
original.ReadTask = task;
43+
}
44+
45+
protected override string GetSubProtocol(WebSocketData connection)
46+
{
47+
return connection.SubProtocol;
48+
}
49+
50+
protected override IWampConnection<TMessage> CreateBinaryConnection<TMessage>
51+
(WebSocketData connection,
52+
IWampBinaryBinding<TMessage> binding)
53+
{
54+
return new BinaryWebSocketWrapperConnection<TMessage>
55+
(new OwinWebSocketWrapper(connection.WebSocketContext),
56+
binding,
57+
new OwinCookieProvider(connection.OwinContext),
58+
AuthenticatorFactory);
59+
}
60+
61+
protected override IWampConnection<TMessage> CreateTextConnection<TMessage>
62+
(WebSocketData connection,
63+
IWampTextBinding<TMessage> binding)
64+
{
65+
return new TextWebSocketWrapperConnection<TMessage>
66+
(new OwinWebSocketWrapper(connection.WebSocketContext),
67+
binding,
68+
new OwinCookieProvider(connection.OwinContext),
69+
AuthenticatorFactory);
70+
}
71+
72+
public override void Open()
73+
{
74+
mHandler = this.WebSocketHandler;
75+
}
76+
77+
private Task HttpHandler(IOwinContext context, Func<Task> next)
78+
{
79+
return mHandler(context, next);
80+
}
81+
82+
private async Task EmptyHandler(IOwinContext owinContext, Func<Task> next)
83+
{
84+
await next();
85+
}
86+
87+
private async Task WebSocketHandler(IOwinContext context, Func<Task> next)
88+
{
89+
Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>> accept =
90+
context.Get<Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>>("websocket.Accept");
91+
92+
if (accept != null)
93+
{
94+
IEnumerable<string> possibleSubProtocols =
95+
context.Request.Headers.GetCommaSeparatedValues(SecWebSocketProtocolHeader)
96+
.Intersect(this.SubProtocols);
97+
98+
string subprotocol =
99+
possibleSubProtocols.FirstOrDefault();
100+
101+
if (subprotocol != null)
102+
{
103+
accept(new Dictionary<string, object>()
104+
{
105+
{WebSocketSubProtocol, subprotocol}
106+
},
107+
websocketContext => OnAccept(websocketContext, context, subprotocol));
108+
109+
return;
110+
}
111+
}
112+
113+
await next();
114+
}
115+
116+
private async Task OnAccept(IDictionary<string, object> webSocketContext, IOwinContext context, string subprotocol)
117+
{
118+
// In an ideal world, OnNewConnection would return the
119+
// connection itself and then we could somehow access its
120+
// task, but for now we wrap the WebSocket with a WebSocketData
121+
// struct, and let OnNewConnection to fill us magically
122+
// the ReadTask
123+
WebSocketData webSocketData = new WebSocketData(webSocketContext, context, subprotocol);
124+
125+
OnNewConnection(webSocketData);
126+
127+
await webSocketData.ReadTask.ConfigureAwait(false);
128+
}
129+
}
130+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net.WebSockets;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using WampSharp.WebSockets;
7+
8+
namespace WampSharp.Owin
9+
{
10+
internal class OwinWebSocketWrapper : IWebSocketWrapper
11+
{
12+
private readonly IDictionary<string, object> mWebsocketContext;
13+
private readonly Func<ArraySegment<byte>, int, bool, CancellationToken, Task> mSendAsync;
14+
private readonly Func<ArraySegment<byte>, CancellationToken, Task<Tuple<int, bool, int>>> mReceiveAsync;
15+
private readonly Func<int, string, CancellationToken, Task> mCloseAsync;
16+
17+
private const string WebSocketSendAsync = "websocket.SendAsync";
18+
private const string WebSocketReceiveAsync = "websocket.ReceiveAsync";
19+
private const string WebSocketCloseAsync = "websocket.CloseAsync";
20+
private const string WebSocketCallCancelled = "websocket.CallCancelled";
21+
private const string WebSocketClientCloseDescription = "websocket.ClientCloseDescription";
22+
private const string WebSocketClientCloseStatus = "websocket.ClientCloseStatus";
23+
private const string WebSocketSubProtocol = "websocket.SubProtocol";
24+
25+
internal OwinWebSocketWrapper(IDictionary<string, object> websocketContext)
26+
{
27+
mWebsocketContext = websocketContext;
28+
29+
mSendAsync =
30+
(Func<ArraySegment<byte>, int, bool, CancellationToken, Task>) mWebsocketContext[WebSocketSendAsync];
31+
32+
mReceiveAsync =
33+
(Func<ArraySegment<byte>, CancellationToken, Task<Tuple<int, bool, int>>>)
34+
mWebsocketContext[WebSocketReceiveAsync];
35+
36+
mCloseAsync = (Func<int, string, CancellationToken, Task>) mWebsocketContext[WebSocketCloseAsync];
37+
}
38+
39+
public string ClientCloseDescription
40+
{
41+
get
42+
{
43+
object description;
44+
45+
if (mWebsocketContext.TryGetValue(WebSocketClientCloseDescription, out description))
46+
{
47+
return (string) description;
48+
}
49+
50+
return null;
51+
}
52+
}
53+
54+
public WebSocketCloseStatus? ClientCloseStatus
55+
{
56+
get
57+
{
58+
object status;
59+
60+
if (mWebsocketContext.TryGetValue(WebSocketClientCloseStatus, out status))
61+
{
62+
return (WebSocketCloseStatus) (int)status;
63+
}
64+
65+
return null;
66+
}
67+
}
68+
69+
public CancellationToken CancellationToken
70+
{
71+
get { return (CancellationToken) mWebsocketContext[WebSocketCallCancelled]; }
72+
}
73+
74+
public string SubProtocol
75+
{
76+
get
77+
{
78+
object subprotocol;
79+
80+
if (mWebsocketContext.TryGetValue(WebSocketSubProtocol, out subprotocol))
81+
{
82+
return (string) subprotocol;
83+
}
84+
85+
return null;
86+
}
87+
}
88+
89+
90+
public async Task<WebSocketReceiveResult> ReceiveAsync
91+
(ArraySegment<byte> arraySegment,
92+
CancellationToken callCancelled)
93+
{
94+
Tuple<int, bool, int> result =
95+
await mReceiveAsync(arraySegment, callCancelled)
96+
.ConfigureAwait(false);
97+
98+
return new WebSocketReceiveResult(count: result.Item3, messageType: GetMessageType(result.Item1), endOfMessage: result.Item2);
99+
}
100+
101+
public Task SendAsync(ArraySegment<byte> data, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancel)
102+
{
103+
return mSendAsync(data, ConvertMessageTypeToInt(messageType), endOfMessage, cancel);
104+
}
105+
106+
public Task CloseAsync(WebSocketCloseStatus closeStatus, string closeDescription, CancellationToken cancel)
107+
{
108+
return mCloseAsync((int) closeStatus, closeDescription, cancel);
109+
}
110+
111+
public bool IsConnected
112+
{
113+
get
114+
{
115+
WebSocketCloseStatus? closeStatus = ClientCloseStatus;
116+
117+
return ((closeStatus == null) || (closeStatus == 0));
118+
}
119+
}
120+
121+
private WebSocketMessageType GetMessageType(int messageType)
122+
{
123+
switch (messageType)
124+
{
125+
case WebSocketMessageTypes.Binary:
126+
return WebSocketMessageType.Binary;
127+
case WebSocketMessageTypes.Text:
128+
return WebSocketMessageType.Text;
129+
case WebSocketMessageTypes.Close:
130+
return WebSocketMessageType.Close;
131+
}
132+
133+
return default(WebSocketMessageType);
134+
}
135+
136+
private int ConvertMessageTypeToInt(WebSocketMessageType messageType)
137+
{
138+
switch (messageType)
139+
{
140+
case WebSocketMessageType.Binary:
141+
return WebSocketMessageTypes.Binary;
142+
case WebSocketMessageType.Text:
143+
return WebSocketMessageTypes.Text;
144+
case WebSocketMessageType.Close:
145+
return WebSocketMessageTypes.Close;
146+
}
147+
148+
return 0;
149+
}
150+
}
151+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using Microsoft.Owin;
4+
5+
namespace WampSharp.Owin
6+
{
7+
public class WebSocketData
8+
{
9+
internal WebSocketData(IDictionary<string, object> webSocketContext, IOwinContext owinContext, string subProtocol)
10+
{
11+
WebSocketContext = webSocketContext;
12+
OwinContext = owinContext;
13+
SubProtocol = subProtocol;
14+
}
15+
16+
public IDictionary<string, object> WebSocketContext { get; private set; }
17+
18+
public IOwinContext OwinContext { get; private set; }
19+
20+
public Task ReadTask { get; internal set; }
21+
22+
public string SubProtocol { get; private set; }
23+
}
24+
}

0 commit comments

Comments
 (0)