commit
c5b6be6b55
|
|
@ -269,6 +269,52 @@ describe("HttpConnection", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not select ServerSentEvents transport when not available in environment", async (done) => {
|
||||||
|
const serverSentEventsTransport = { transport: "ServerSentEvents", transferFormats: [ "Text" ] };
|
||||||
|
|
||||||
|
const options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
|
httpClient: new TestHttpClient()
|
||||||
|
.on("POST", (r) => ({ connectionId: "42", availableTransports: [serverSentEventsTransport] })),
|
||||||
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
|
const connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await connection.start(TransferFormat.Text);
|
||||||
|
fail();
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
// ServerSentEvents is only transport returned from server but is not selected
|
||||||
|
// because there is no support in the environment, leading to the following error
|
||||||
|
expect(e.message).toBe("Unable to initialize any of the available transports.");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not select WebSockets transport when not available in environment", async (done) => {
|
||||||
|
const webSocketsTransport = { transport: "WebSockets", transferFormats: [ "Text" ] };
|
||||||
|
|
||||||
|
const options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
|
httpClient: new TestHttpClient()
|
||||||
|
.on("POST", (r) => ({ connectionId: "42", availableTransports: [webSocketsTransport] })),
|
||||||
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
|
const connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await connection.start(TransferFormat.Text);
|
||||||
|
fail();
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
// WebSockets is only transport returned from server but is not selected
|
||||||
|
// because there is no support in the environment, leading to the following error
|
||||||
|
expect(e.message).toBe("Unable to initialize any of the available transports.");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
describe(".constructor", () => {
|
describe(".constructor", () => {
|
||||||
it("throws if no Url is provided", async () => {
|
it("throws if no Url is provided", async () => {
|
||||||
// Force TypeScript to let us call the constructor incorrectly :)
|
// Force TypeScript to let us call the constructor incorrectly :)
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,13 @@ export class HttpConnection implements IConnection {
|
||||||
const transferFormats = endpoint.transferFormats.map((s) => TransferFormat[s]);
|
const transferFormats = endpoint.transferFormats.map((s) => TransferFormat[s]);
|
||||||
if (!requestedTransport || transport === requestedTransport) {
|
if (!requestedTransport || transport === requestedTransport) {
|
||||||
if (transferFormats.indexOf(requestedTransferFormat) >= 0) {
|
if (transferFormats.indexOf(requestedTransferFormat) >= 0) {
|
||||||
this.logger.log(LogLevel.Trace, `Selecting transport '${TransportType[transport]}'`);
|
if ((transport === TransportType.WebSockets && typeof WebSocket === "undefined") ||
|
||||||
return transport;
|
(transport === TransportType.ServerSentEvents && typeof EventSource === "undefined")) {
|
||||||
|
this.logger.log(LogLevel.Trace, `Skipping transport '${TransportType[transport]}' because it is not supported in your environment.'`);
|
||||||
|
} else {
|
||||||
|
this.logger.log(LogLevel.Trace, `Selecting transport '${TransportType[transport]}'`);
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.logger.log(LogLevel.Trace, `Skipping transport '${TransportType[transport]}' because it does not support the requested transfer format '${TransferFormat[requestedTransferFormat]}'.`);
|
this.logger.log(LogLevel.Trace, `Skipping transport '${TransportType[transport]}' because it does not support the requested transfer format '${TransferFormat[requestedTransferFormat]}'.`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.ExceptionServices;
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using Microsoft.AspNetCore.Protocols;
|
using Microsoft.AspNetCore.Protocols;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
|
|
@ -41,15 +43,18 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
||||||
{
|
{
|
||||||
while (BinaryMessageParser.TryParseMessage(ref input, out var payload))
|
while (BinaryMessageParser.TryParseMessage(ref input, out var payload))
|
||||||
{
|
{
|
||||||
messages.Add(ParseMessage(payload.ToArray(), binder));
|
var isArray = MemoryMarshal.TryGetArray(payload, out var arraySegment);
|
||||||
|
// This will never be false unless we started using un-managed buffers
|
||||||
|
Debug.Assert(isArray);
|
||||||
|
messages.Add(ParseMessage(arraySegment.Array, arraySegment.Offset, binder));
|
||||||
}
|
}
|
||||||
|
|
||||||
return messages.Count > 0;
|
return messages.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HubMessage ParseMessage(byte[] input, IInvocationBinder binder)
|
private static HubMessage ParseMessage(byte[] input, int startOffset, IInvocationBinder binder)
|
||||||
{
|
{
|
||||||
using (var unpacker = Unpacker.Create(input))
|
using (var unpacker = Unpacker.Create(input, startOffset))
|
||||||
{
|
{
|
||||||
_ = ReadArrayLength(unpacker, "elementCount");
|
_ = ReadArrayLength(unpacker, "elementCount");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,9 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
private static readonly Action<ILogger, string, Exception> _transportFailed =
|
private static readonly Action<ILogger, string, Exception> _transportFailed =
|
||||||
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(29, "TransportFailed"), "Skipping transport {TransportName} because it failed to initialize.");
|
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(29, "TransportFailed"), "Skipping transport {TransportName} because it failed to initialize.");
|
||||||
|
|
||||||
|
private static readonly Action<ILogger, Exception> _webSocketsNotSupportedByOperatingSystem =
|
||||||
|
LoggerMessage.Define(LogLevel.Debug, new EventId(30, "WebSocketsNotSupportedByOperatingSystem"), "Skipping WebSockets because they are not supported by the operating system.");
|
||||||
|
|
||||||
public static void HttpConnectionStarting(ILogger logger)
|
public static void HttpConnectionStarting(ILogger logger)
|
||||||
{
|
{
|
||||||
_httpConnectionStarting(logger, null);
|
_httpConnectionStarting(logger, null);
|
||||||
|
|
@ -260,6 +263,11 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
_transportFailed(logger, transport.ToString(), ex);
|
_transportFailed(logger, transport.ToString(), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void WebSocketsNotSupportedByOperatingSystem(ILogger logger)
|
||||||
|
{
|
||||||
|
_webSocketsNotSupportedByOperatingSystem(logger, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.IO;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
|
@ -25,6 +26,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
public partial class HttpConnection : IConnection
|
public partial class HttpConnection : IConnection
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan HttpClientTimeout = TimeSpan.FromSeconds(120);
|
private static readonly TimeSpan HttpClientTimeout = TimeSpan.FromSeconds(120);
|
||||||
|
private static readonly Version Windows8Version = new Version(6, 2);
|
||||||
|
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
@ -203,6 +205,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
var connectUrl = Url;
|
var connectUrl = Url;
|
||||||
if (_requestedTransportType == TransportType.WebSockets)
|
if (_requestedTransportType == TransportType.WebSockets)
|
||||||
{
|
{
|
||||||
|
// if we're running on Windows 7 this could throw because the OS does not support web sockets
|
||||||
Log.StartingTransport(_logger, _requestedTransportType, connectUrl);
|
Log.StartingTransport(_logger, _requestedTransportType, connectUrl);
|
||||||
await StartTransport(connectUrl, _requestedTransportType, transferFormat);
|
await StartTransport(connectUrl, _requestedTransportType, transferFormat);
|
||||||
}
|
}
|
||||||
|
|
@ -233,6 +236,12 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transportType == TransportType.WebSockets && !IsWebSocketsSupported())
|
||||||
|
{
|
||||||
|
Log.WebSocketsNotSupportedByOperatingSystem(_logger);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ((transportType & _requestedTransportType) == 0)
|
if ((transportType & _requestedTransportType) == 0)
|
||||||
|
|
@ -711,6 +720,26 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsWebSocketsSupported()
|
||||||
|
{
|
||||||
|
#if NETCOREAPP2_1
|
||||||
|
// .NET Core 2.1 and above has a managed implementation
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
|
if (!isWindows)
|
||||||
|
{
|
||||||
|
// Assume other OSes have websockets
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Windows 8 and above has websockets
|
||||||
|
return Environment.OSVersion.Version >= Windows8Version;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Internal because it's used by logging to avoid ToStringing prematurely.
|
// Internal because it's used by logging to avoid ToStringing prematurely.
|
||||||
internal enum ConnectionState
|
internal enum ConnectionState
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,9 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
|
|
||||||
Log.StartTransport(_logger, transferFormat);
|
Log.StartTransport(_logger, transferFormat);
|
||||||
|
|
||||||
|
var startTcs = new TaskCompletionSource<object>(TaskContinuationOptions.RunContinuationsAsynchronously);
|
||||||
var sendTask = SendUtils.SendMessages(url, _application, _httpClient, _httpOptions, _transportCts, _logger);
|
var sendTask = SendUtils.SendMessages(url, _application, _httpClient, _httpOptions, _transportCts, _logger);
|
||||||
var receiveTask = OpenConnection(_application, url, _transportCts.Token);
|
var receiveTask = OpenConnection(_application, url, startTcs, _transportCts.Token);
|
||||||
|
|
||||||
Running = Task.WhenAll(sendTask, receiveTask).ContinueWith(t =>
|
Running = Task.WhenAll(sendTask, receiveTask).ContinueWith(t =>
|
||||||
{
|
{
|
||||||
|
|
@ -66,17 +67,30 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
return t;
|
return t;
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return startTcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OpenConnection(IDuplexPipe application, Uri url, CancellationToken cancellationToken)
|
private async Task OpenConnection(IDuplexPipe application, Uri url, TaskCompletionSource<object> startTcs, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Log.StartReceive(_logger);
|
Log.StartReceive(_logger);
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
SendUtils.PrepareHttpRequest(request, _httpOptions);
|
SendUtils.PrepareHttpRequest(request, _httpOptions);
|
||||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
|
||||||
var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
|
||||||
|
HttpResponseMessage response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
startTcs.TrySetResult(null);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.TransportStopping(_logger);
|
||||||
|
startTcs.TrySetException(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using (var stream = await response.Content.ReadAsStreamAsync())
|
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
logScope.ConnectionId = connection.ConnectionId;
|
logScope.ConnectionId = connection.ConnectionId;
|
||||||
|
|
||||||
// Get the bytes for the connection id
|
// Get the bytes for the connection id
|
||||||
var negotiateResponseBuffer = Encoding.UTF8.GetBytes(GetNegotiatePayload(connection.ConnectionId, options));
|
var negotiateResponseBuffer = Encoding.UTF8.GetBytes(GetNegotiatePayload(connection.ConnectionId, context, options));
|
||||||
|
|
||||||
Log.NegotiationRequest(_logger);
|
Log.NegotiationRequest(_logger);
|
||||||
|
|
||||||
|
|
@ -377,7 +377,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
return context.Response.Body.WriteAsync(negotiateResponseBuffer, 0, negotiateResponseBuffer.Length);
|
return context.Response.Body.WriteAsync(negotiateResponseBuffer, 0, negotiateResponseBuffer.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetNegotiatePayload(string connectionId, HttpSocketOptions options)
|
private static string GetNegotiatePayload(string connectionId, HttpContext context, HttpSocketOptions options)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
using (var jsonWriter = new JsonTextWriter(new StringWriter(sb)))
|
using (var jsonWriter = new JsonTextWriter(new StringWriter(sb)))
|
||||||
|
|
@ -387,18 +387,25 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
jsonWriter.WriteValue(connectionId);
|
jsonWriter.WriteValue(connectionId);
|
||||||
jsonWriter.WritePropertyName("availableTransports");
|
jsonWriter.WritePropertyName("availableTransports");
|
||||||
jsonWriter.WriteStartArray();
|
jsonWriter.WriteStartArray();
|
||||||
if ((options.Transports & TransportType.WebSockets) != 0)
|
|
||||||
|
if (ServerHasWebSockets(context.Features))
|
||||||
{
|
{
|
||||||
WriteTransport(jsonWriter, nameof(TransportType.WebSockets), TransferFormat.Text | TransferFormat.Binary);
|
if ((options.Transports & TransportType.WebSockets) != 0)
|
||||||
|
{
|
||||||
|
WriteTransport(jsonWriter, nameof(TransportType.WebSockets), TransferFormat.Text | TransferFormat.Binary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((options.Transports & TransportType.ServerSentEvents) != 0)
|
if ((options.Transports & TransportType.ServerSentEvents) != 0)
|
||||||
{
|
{
|
||||||
WriteTransport(jsonWriter, nameof(TransportType.ServerSentEvents), TransferFormat.Text);
|
WriteTransport(jsonWriter, nameof(TransportType.ServerSentEvents), TransferFormat.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((options.Transports & TransportType.LongPolling) != 0)
|
if ((options.Transports & TransportType.LongPolling) != 0)
|
||||||
{
|
{
|
||||||
WriteTransport(jsonWriter, nameof(TransportType.LongPolling), TransferFormat.Text | TransferFormat.Binary);
|
WriteTransport(jsonWriter, nameof(TransportType.LongPolling), TransferFormat.Text | TransferFormat.Binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonWriter.WriteEndArray();
|
jsonWriter.WriteEndArray();
|
||||||
jsonWriter.WriteEndObject();
|
jsonWriter.WriteEndObject();
|
||||||
}
|
}
|
||||||
|
|
@ -406,6 +413,11 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool ServerHasWebSockets(IFeatureCollection features)
|
||||||
|
{
|
||||||
|
return features.Get<IHttpWebSocketFeature>() != null;
|
||||||
|
}
|
||||||
|
|
||||||
private static void WriteTransport(JsonWriter writer, string transportName, TransferFormat supportedTransferFormats)
|
private static void WriteTransport(JsonWriter writer, string transportName, TransferFormat supportedTransferFormats)
|
||||||
{
|
{
|
||||||
writer.WriteStartObject();
|
writer.WriteStartObject();
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,59 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SSEWontStartIfSuccessfulConnectionIsNotEstablished()
|
||||||
|
{
|
||||||
|
using (StartLog(out var loggerFactory))
|
||||||
|
{
|
||||||
|
var httpHandler = new TestHttpMessageHandler();
|
||||||
|
|
||||||
|
httpHandler.OnGet("/?id=00000000-0000-0000-0000-000000000000", (_, __) =>
|
||||||
|
{
|
||||||
|
return Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.InternalServerError));
|
||||||
|
});
|
||||||
|
|
||||||
|
var sse = new ServerSentEventsTransport(new HttpClient(httpHandler));
|
||||||
|
|
||||||
|
await WithConnectionAsync(
|
||||||
|
CreateConnection(httpHandler, loggerFactory: loggerFactory, url: null, transport: sse),
|
||||||
|
async (connection, closed) =>
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => connection.StartAsync(TransferFormat.Text).OrTimeout());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SSEWaitsForResponseToStart()
|
||||||
|
{
|
||||||
|
using (StartLog(out var loggerFactory))
|
||||||
|
{
|
||||||
|
var httpHandler = new TestHttpMessageHandler();
|
||||||
|
|
||||||
|
var connectResponseTcs = new TaskCompletionSource<object>();
|
||||||
|
httpHandler.OnGet("/?id=00000000-0000-0000-0000-000000000000", async (_, __) =>
|
||||||
|
{
|
||||||
|
await connectResponseTcs.Task;
|
||||||
|
return ResponseUtils.CreateResponse(HttpStatusCode.Accepted);
|
||||||
|
});
|
||||||
|
|
||||||
|
var sse = new ServerSentEventsTransport(new HttpClient(httpHandler));
|
||||||
|
|
||||||
|
await WithConnectionAsync(
|
||||||
|
CreateConnection(httpHandler, loggerFactory: loggerFactory, url: null, transport: sse),
|
||||||
|
async (connection, closed) =>
|
||||||
|
{
|
||||||
|
var startTask = connection.StartAsync(TransferFormat.Text).OrTimeout();
|
||||||
|
Assert.False(connectResponseTcs.Task.IsCompleted);
|
||||||
|
Assert.False(startTask.IsCompleted);
|
||||||
|
connectResponseTcs.TrySetResult(null);
|
||||||
|
await startTask;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TransportIsStoppedWhenConnectionIsStopped()
|
public async Task TransportIsStoppedWhenConnectionIsStopped()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var dispatcher = new HttpConnectionDispatcher(manager, loggerFactory);
|
var dispatcher = new HttpConnectionDispatcher(manager, loggerFactory);
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
context.Features.Set<IHttpResponseFeature>(new ResponseFeature());
|
context.Features.Set<IHttpResponseFeature>(new ResponseFeature());
|
||||||
|
context.Features.Set<IHttpWebSocketFeature>(new TestWebSocketConnectionFeature());
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton<TestEndPoint>();
|
services.AddSingleton<TestEndPoint>();
|
||||||
services.AddOptions();
|
services.AddOptions();
|
||||||
|
|
@ -1225,6 +1226,31 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task NegotiateDoesNotReturnWebSocketsWhenNotAvailable()
|
||||||
|
{
|
||||||
|
using (StartLog(out var loggerFactory, LogLevel.Debug))
|
||||||
|
{
|
||||||
|
var manager = CreateConnectionManager(loggerFactory);
|
||||||
|
var dispatcher = new HttpConnectionDispatcher(manager, loggerFactory);
|
||||||
|
var context = new DefaultHttpContext();
|
||||||
|
context.Features.Set<IHttpResponseFeature>(new ResponseFeature());
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddSingleton<TestEndPoint>();
|
||||||
|
services.AddOptions();
|
||||||
|
var ms = new MemoryStream();
|
||||||
|
context.Request.Path = "/foo";
|
||||||
|
context.Request.Method = "POST";
|
||||||
|
context.Response.Body = ms;
|
||||||
|
await dispatcher.ExecuteNegotiateAsync(context, new HttpSocketOptions { Transports = TransportType.WebSockets });
|
||||||
|
|
||||||
|
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
|
var availableTransports = (JArray)negotiateResponse["availableTransports"];
|
||||||
|
|
||||||
|
Assert.Empty(availableTransports);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class RejectHandler : TestAuthenticationHandler
|
private class RejectHandler : TestAuthenticationHandler
|
||||||
{
|
{
|
||||||
protected override bool ShouldAccept => false;
|
protected override bool ShouldAccept => false;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue