This repository has been archived by the owner on Oct 18, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 114
/
Copy pathWebSocketMiddleware.cs
128 lines (112 loc) · 4.84 KB
/
WebSocketMiddleware.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.WebSockets.Internal;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.WebSockets
{
public class WebSocketMiddleware
{
private readonly RequestDelegate _next;
private readonly WebSocketOptions _options;
public WebSocketMiddleware(RequestDelegate next, IOptions<WebSocketOptions> options)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
_next = next;
_options = options.Value;
// TODO: validate options.
}
public Task Invoke(HttpContext context)
{
// Detect if an opaque upgrade is available. If so, add a websocket upgrade.
var upgradeFeature = context.Features.Get<IHttpUpgradeFeature>();
if (upgradeFeature != null)
{
if (_options.ReplaceFeature || context.Features.Get<IHttpWebSocketFeature>() == null)
{
context.Features.Set<IHttpWebSocketFeature>(new UpgradeHandshake(context, upgradeFeature, _options));
}
}
return _next(context);
}
private class UpgradeHandshake : IHttpWebSocketFeature
{
private readonly HttpContext _context;
private readonly IHttpUpgradeFeature _upgradeFeature;
private readonly WebSocketOptions _options;
public UpgradeHandshake(HttpContext context, IHttpUpgradeFeature upgradeFeature, WebSocketOptions options)
{
_context = context;
_upgradeFeature = upgradeFeature;
_options = options;
}
public bool IsWebSocketRequest
{
get
{
if (!_upgradeFeature.IsUpgradableRequest)
{
return false;
}
var headers = new List<KeyValuePair<string, string>>();
foreach (string headerName in HandshakeHelpers.NeededHeaders)
{
foreach (var value in _context.Request.Headers.GetCommaSeparatedValues(headerName))
{
headers.Add(new KeyValuePair<string, string>(headerName, value));
}
}
return HandshakeHelpers.CheckSupportedWebSocketRequest(_context.Request.Method, headers);
}
}
public async Task<WebSocket> AcceptAsync(WebSocketAcceptContext acceptContext)
{
if (!IsWebSocketRequest)
{
throw new InvalidOperationException("Not a WebSocket request."); // TODO: LOC
}
string subProtocol = null;
if (acceptContext != null)
{
subProtocol = acceptContext.SubProtocol;
}
TimeSpan keepAliveInterval = _options.KeepAliveInterval;
int receiveBufferSize = _options.ReceiveBufferSize;
var advancedAcceptContext = acceptContext as ExtendedWebSocketAcceptContext;
if (advancedAcceptContext != null)
{
if (advancedAcceptContext.ReceiveBufferSize.HasValue)
{
receiveBufferSize = advancedAcceptContext.ReceiveBufferSize.Value;
}
if (advancedAcceptContext.KeepAliveInterval.HasValue)
{
keepAliveInterval = advancedAcceptContext.KeepAliveInterval.Value;
}
}
string key = string.Join(", ", _context.Request.Headers[Constants.Headers.SecWebSocketKey]);
var responseHeaders = HandshakeHelpers.GenerateResponseHeaders(key, subProtocol);
foreach (var headerPair in responseHeaders)
{
_context.Response.Headers[headerPair.Key] = headerPair.Value;
}
Stream opaqueTransport = await _upgradeFeature.UpgradeAsync(); // Sets status code to 101
return WebSocketFactory.CreateServerWebSocket(opaqueTransport, subProtocol, keepAliveInterval, receiveBufferSize);
}
}
}
}