SignalR ConnectionToken/ConnectionAddress feature (#13773)
This commit is contained in:
parent
ea8c2b92f4
commit
7d59ae1720
|
|
@ -36,6 +36,12 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
return RunInvalidNegotiateResponseTest<FormatException>(ResponseUtils.CreateNegotiationContent(connectionId: string.Empty), "Invalid connection id.");
|
return RunInvalidNegotiateResponseTest<FormatException>(ResponseUtils.CreateNegotiationContent(connectionId: string.Empty), "Invalid connection id.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public Task NegotiateResponseWithNegotiateVersionRequiresConnectionToken()
|
||||||
|
{
|
||||||
|
return RunInvalidNegotiateResponseTest<InvalidDataException>(ResponseUtils.CreateNegotiationContent(negotiateVersion: 1, connectionToken: null), "Invalid negotiation response received.");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public Task ConnectionCannotBeStartedIfNoCommonTransportsBetweenClientAndServer()
|
public Task ConnectionCannotBeStartedIfNoCommonTransportsBetweenClientAndServer()
|
||||||
{
|
{
|
||||||
|
|
@ -156,6 +162,87 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
Assert.Equal("0rge0d00-0040-0030-0r00-000q00r00e00", connectionId);
|
Assert.Equal("0rge0d00-0040-0030-0r00-000q00r00e00", connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConnectionIdGetsSetWithNegotiateProtocolGreaterThanZero()
|
||||||
|
{
|
||||||
|
string connectionId = null;
|
||||||
|
|
||||||
|
var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false);
|
||||||
|
testHttpHandler.OnNegotiate((request, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK,
|
||||||
|
JsonConvert.SerializeObject(new
|
||||||
|
{
|
||||||
|
connectionId = "0rge0d00-0040-0030-0r00-000q00r00e00",
|
||||||
|
negotiateVersion = 1,
|
||||||
|
connectionToken = "different-id",
|
||||||
|
availableTransports = new object[]
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
transport = "LongPolling",
|
||||||
|
transferFormats = new[] { "Text" }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newField = "ignore this",
|
||||||
|
})));
|
||||||
|
testHttpHandler.OnLongPoll(cancellationToken => ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
|
||||||
|
testHttpHandler.OnLongPollDelete((token) => ResponseUtils.CreateResponse(HttpStatusCode.Accepted));
|
||||||
|
|
||||||
|
using (var noErrorScope = new VerifyNoErrorsScope())
|
||||||
|
{
|
||||||
|
await WithConnectionAsync(
|
||||||
|
CreateConnection(testHttpHandler, loggerFactory: noErrorScope.LoggerFactory),
|
||||||
|
async (connection) =>
|
||||||
|
{
|
||||||
|
await connection.StartAsync().OrTimeout();
|
||||||
|
connectionId = connection.ConnectionId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal("0rge0d00-0040-0030-0r00-000q00r00e00", connectionId);
|
||||||
|
Assert.Equal("http://fakeuri.org/negotiate?negotiateVersion=1", testHttpHandler.ReceivedRequests[0].RequestUri.ToString());
|
||||||
|
Assert.Equal("http://fakeuri.org/?negotiateVersion=1&id=different-id", testHttpHandler.ReceivedRequests[1].RequestUri.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConnectionTokenFieldIsIgnoredForNegotiateIdLessThanOne()
|
||||||
|
{
|
||||||
|
string connectionId = null;
|
||||||
|
|
||||||
|
var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false);
|
||||||
|
testHttpHandler.OnNegotiate((request, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK,
|
||||||
|
JsonConvert.SerializeObject(new
|
||||||
|
{
|
||||||
|
connectionId = "0rge0d00-0040-0030-0r00-000q00r00e00",
|
||||||
|
connectionToken = "different-id",
|
||||||
|
availableTransports = new object[]
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
transport = "LongPolling",
|
||||||
|
transferFormats = new[] { "Text" }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newField = "ignore this",
|
||||||
|
})));
|
||||||
|
testHttpHandler.OnLongPoll(cancellationToken => ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
|
||||||
|
testHttpHandler.OnLongPollDelete((token) => ResponseUtils.CreateResponse(HttpStatusCode.Accepted));
|
||||||
|
|
||||||
|
using (var noErrorScope = new VerifyNoErrorsScope())
|
||||||
|
{
|
||||||
|
await WithConnectionAsync(
|
||||||
|
CreateConnection(testHttpHandler, loggerFactory: noErrorScope.LoggerFactory),
|
||||||
|
async (connection) =>
|
||||||
|
{
|
||||||
|
await connection.StartAsync().OrTimeout();
|
||||||
|
connectionId = connection.ConnectionId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal("0rge0d00-0040-0030-0r00-000q00r00e00", connectionId);
|
||||||
|
Assert.Equal("http://fakeuri.org/negotiate?negotiateVersion=1", testHttpHandler.ReceivedRequests[0].RequestUri.ToString());
|
||||||
|
Assert.Equal("http://fakeuri.org/?negotiateVersion=1&id=0rge0d00-0040-0030-0r00-000q00r00e00", testHttpHandler.ReceivedRequests[1].RequestUri.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task NegotiateThatReturnsUrlGetFollowed()
|
public async Task NegotiateThatReturnsUrlGetFollowed()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string CreateNegotiationContent(string connectionId = "00000000-0000-0000-0000-000000000000",
|
public static string CreateNegotiationContent(string connectionId = "00000000-0000-0000-0000-000000000000",
|
||||||
HttpTransportType? transportTypes = null)
|
HttpTransportType? transportTypes = null, string connectionToken = "connection-token", int negotiateVersion = 0)
|
||||||
{
|
{
|
||||||
var availableTransports = new List<object>();
|
var availableTransports = new List<object>();
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return JsonConvert.SerializeObject(new { connectionId, availableTransports });
|
return JsonConvert.SerializeObject(new { connectionId, availableTransports, connectionToken, negotiateVersion });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
|
||||||
private readonly HttpConnectionOptions _httpConnectionOptions;
|
private readonly HttpConnectionOptions _httpConnectionOptions;
|
||||||
private ITransport _transport;
|
private ITransport _transport;
|
||||||
private readonly ITransportFactory _transportFactory;
|
private readonly ITransportFactory _transportFactory;
|
||||||
|
private string _connectionToken;
|
||||||
private string _connectionId;
|
private string _connectionId;
|
||||||
private readonly ConnectionLogScope _logScope;
|
private readonly ConnectionLogScope _logScope;
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
@ -342,7 +343,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should only need to happen once
|
// This should only need to happen once
|
||||||
var connectUrl = CreateConnectUrl(uri, negotiationResponse.ConnectionId);
|
var connectUrl = CreateConnectUrl(uri, _connectionToken);
|
||||||
|
|
||||||
// We're going to search for the transfer format as a string because we don't want to parse
|
// We're going to search for the transfer format as a string because we don't want to parse
|
||||||
// all the transfer formats in the negotiation response, and we want to allow transfer formats
|
// all the transfer formats in the negotiation response, and we want to allow transfer formats
|
||||||
|
|
@ -383,7 +384,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
|
||||||
if (negotiationResponse == null)
|
if (negotiationResponse == null)
|
||||||
{
|
{
|
||||||
negotiationResponse = await GetNegotiationResponseAsync(uri, cancellationToken);
|
negotiationResponse = await GetNegotiationResponseAsync(uri, cancellationToken);
|
||||||
connectUrl = CreateConnectUrl(uri, negotiationResponse.ConnectionId);
|
connectUrl = CreateConnectUrl(uri, _connectionToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.StartingTransport(_logger, transportType, connectUrl);
|
Log.StartingTransport(_logger, transportType, connectUrl);
|
||||||
|
|
@ -609,7 +610,19 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
|
||||||
private async Task<NegotiationResponse> GetNegotiationResponseAsync(Uri uri, CancellationToken cancellationToken)
|
private async Task<NegotiationResponse> GetNegotiationResponseAsync(Uri uri, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var negotiationResponse = await NegotiateAsync(uri, _httpClient, _logger, cancellationToken);
|
var negotiationResponse = await NegotiateAsync(uri, _httpClient, _logger, cancellationToken);
|
||||||
_connectionId = negotiationResponse.ConnectionId;
|
// If the negotiationVersion is greater than zero then we know that the negotiation response contains a
|
||||||
|
// connectionToken that will be required to conenct. Otherwise we just set the connectionId and the
|
||||||
|
// connectionToken on the client to the same value.
|
||||||
|
if (negotiationResponse.Version > 0)
|
||||||
|
{
|
||||||
|
_connectionId = negotiationResponse.ConnectionId;
|
||||||
|
_connectionToken = negotiationResponse.ConnectionToken;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_connectionToken = _connectionId = negotiationResponse.ConnectionId;
|
||||||
|
}
|
||||||
|
|
||||||
_logScope.ConnectionId = _connectionId;
|
_logScope.ConnectionId = _connectionId;
|
||||||
return negotiationResponse;
|
return negotiationResponse;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
public string AccessToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string AccessToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public System.Collections.Generic.IList<Microsoft.AspNetCore.Http.Connections.AvailableTransport> AvailableTransports { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public System.Collections.Generic.IList<Microsoft.AspNetCore.Http.Connections.AvailableTransport> AvailableTransports { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
|
public string ConnectionToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string Error { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string Error { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string Url { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string Url { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public int Version { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public int Version { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
public string AccessToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string AccessToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public System.Collections.Generic.IList<Microsoft.AspNetCore.Http.Connections.AvailableTransport> AvailableTransports { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public System.Collections.Generic.IList<Microsoft.AspNetCore.Http.Connections.AvailableTransport> AvailableTransports { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
|
public string ConnectionToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string Error { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string Error { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string Url { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string Url { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public int Version { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public int Version { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
{
|
{
|
||||||
private const string ConnectionIdPropertyName = "connectionId";
|
private const string ConnectionIdPropertyName = "connectionId";
|
||||||
private static JsonEncodedText ConnectionIdPropertyNameBytes = JsonEncodedText.Encode(ConnectionIdPropertyName);
|
private static JsonEncodedText ConnectionIdPropertyNameBytes = JsonEncodedText.Encode(ConnectionIdPropertyName);
|
||||||
|
private const string ConnectionTokenPropertyName = "connectionToken";
|
||||||
|
private static JsonEncodedText ConnectionTokenPropertyNameBytes = JsonEncodedText.Encode(ConnectionTokenPropertyName);
|
||||||
private const string UrlPropertyName = "url";
|
private const string UrlPropertyName = "url";
|
||||||
private static JsonEncodedText UrlPropertyNameBytes = JsonEncodedText.Encode(UrlPropertyName);
|
private static JsonEncodedText UrlPropertyNameBytes = JsonEncodedText.Encode(UrlPropertyName);
|
||||||
private const string AccessTokenPropertyName = "accessToken";
|
private const string AccessTokenPropertyName = "accessToken";
|
||||||
|
|
@ -71,6 +73,11 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
writer.WriteString(ConnectionIdPropertyNameBytes, response.ConnectionId);
|
writer.WriteString(ConnectionIdPropertyNameBytes, response.ConnectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.Version > 0 && !string.IsNullOrEmpty(response.ConnectionToken))
|
||||||
|
{
|
||||||
|
writer.WriteString(ConnectionTokenPropertyNameBytes, response.ConnectionToken);
|
||||||
|
}
|
||||||
|
|
||||||
writer.WriteStartArray(AvailableTransportsPropertyNameBytes);
|
writer.WriteStartArray(AvailableTransportsPropertyNameBytes);
|
||||||
|
|
||||||
if (response.AvailableTransports != null)
|
if (response.AvailableTransports != null)
|
||||||
|
|
@ -127,6 +134,7 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
reader.EnsureObjectStart();
|
reader.EnsureObjectStart();
|
||||||
|
|
||||||
string connectionId = null;
|
string connectionId = null;
|
||||||
|
string connectionToken = null;
|
||||||
string url = null;
|
string url = null;
|
||||||
string accessToken = null;
|
string accessToken = null;
|
||||||
List<AvailableTransport> availableTransports = null;
|
List<AvailableTransport> availableTransports = null;
|
||||||
|
|
@ -151,6 +159,10 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
{
|
{
|
||||||
connectionId = reader.ReadAsString(ConnectionIdPropertyName);
|
connectionId = reader.ReadAsString(ConnectionIdPropertyName);
|
||||||
}
|
}
|
||||||
|
else if (reader.ValueTextEquals(ConnectionTokenPropertyNameBytes.EncodedUtf8Bytes))
|
||||||
|
{
|
||||||
|
connectionToken = reader.ReadAsString(ConnectionTokenPropertyName);
|
||||||
|
}
|
||||||
else if (reader.ValueTextEquals(NegotiateVersionPropertyNameBytes.EncodedUtf8Bytes))
|
else if (reader.ValueTextEquals(NegotiateVersionPropertyNameBytes.EncodedUtf8Bytes))
|
||||||
{
|
{
|
||||||
version = reader.ReadAsInt32(NegotiateVersionPropertyName).GetValueOrDefault();
|
version = reader.ReadAsInt32(NegotiateVersionPropertyName).GetValueOrDefault();
|
||||||
|
|
@ -202,6 +214,14 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
throw new InvalidDataException($"Missing required property '{ConnectionIdPropertyName}'.");
|
throw new InvalidDataException($"Missing required property '{ConnectionIdPropertyName}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version > 0)
|
||||||
|
{
|
||||||
|
if (connectionToken == null)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException($"Missing required property '{ConnectionTokenPropertyName}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (availableTransports == null)
|
if (availableTransports == null)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException($"Missing required property '{AvailableTransportsPropertyName}'.");
|
throw new InvalidDataException($"Missing required property '{AvailableTransportsPropertyName}'.");
|
||||||
|
|
@ -211,6 +231,7 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
return new NegotiationResponse
|
return new NegotiationResponse
|
||||||
{
|
{
|
||||||
ConnectionId = connectionId,
|
ConnectionId = connectionId,
|
||||||
|
ConnectionToken = connectionToken,
|
||||||
Url = url,
|
Url = url,
|
||||||
AccessToken = accessToken,
|
AccessToken = accessToken,
|
||||||
AvailableTransports = availableTransports,
|
AvailableTransports = availableTransports,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.Http.Connections
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
public string AccessToken { get; set; }
|
public string AccessToken { get; set; }
|
||||||
public string ConnectionId { get; set; }
|
public string ConnectionId { get; set; }
|
||||||
|
public string ConnectionToken { get; set; }
|
||||||
public int Version { get; set; }
|
public int Version { get; set; }
|
||||||
public IList<AvailableTransport> AvailableTransports { get; set; }
|
public IList<AvailableTransport> AvailableTransports { get; set; }
|
||||||
public string Error { get; set; }
|
public string Error { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,13 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
/// Creates the DefaultConnectionContext without Pipes to avoid upfront allocations.
|
/// Creates the DefaultConnectionContext without Pipes to avoid upfront allocations.
|
||||||
/// The caller is expected to set the <see cref="Transport"/> and <see cref="Application"/> pipes manually.
|
/// The caller is expected to set the <see cref="Transport"/> and <see cref="Application"/> pipes manually.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id"></param>
|
/// <param name="connectionId"></param>
|
||||||
|
/// <param name="connectionToken"></param>
|
||||||
/// <param name="logger"></param>
|
/// <param name="logger"></param>
|
||||||
public HttpConnectionContext(string id, ILogger logger)
|
public HttpConnectionContext(string connectionId, string connectionToken, ILogger logger)
|
||||||
{
|
{
|
||||||
ConnectionId = id;
|
ConnectionId = connectionId;
|
||||||
|
ConnectionToken = connectionToken;
|
||||||
LastSeenUtc = DateTime.UtcNow;
|
LastSeenUtc = DateTime.UtcNow;
|
||||||
|
|
||||||
// The default behavior is that both formats are supported.
|
// The default behavior is that both formats are supported.
|
||||||
|
|
@ -74,8 +76,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
Features.Set<IConnectionInherentKeepAliveFeature>(this);
|
Features.Set<IConnectionInherentKeepAliveFeature>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application, ILogger logger = null)
|
internal HttpConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application, ILogger logger = null)
|
||||||
: this(id, logger)
|
: this(id, null, logger)
|
||||||
{
|
{
|
||||||
Transport = transport;
|
Transport = transport;
|
||||||
Application = application;
|
Application = application;
|
||||||
|
|
@ -113,6 +115,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
|
|
||||||
public override string ConnectionId { get; set; }
|
public override string ConnectionId { get; set; }
|
||||||
|
|
||||||
|
internal string ConnectionToken { get; set; }
|
||||||
|
|
||||||
public override IFeatureCollection Features { get; }
|
public override IFeatureCollection Features { get; }
|
||||||
|
|
||||||
public ClaimsPrincipal User { get; set; }
|
public ClaimsPrincipal User { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,15 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
// Create the log scope and attempt to pass the Connection ID to it so as many logs as possible contain
|
// Create the log scope and attempt to pass the Connection ID to it so as many logs as possible contain
|
||||||
// the Connection ID metadata. If this is the negotiate request then the Connection ID for the scope will
|
// the Connection ID metadata. If this is the negotiate request then the Connection ID for the scope will
|
||||||
// be set a little later.
|
// be set a little later.
|
||||||
var logScope = new ConnectionLogScope(GetConnectionId(context));
|
|
||||||
|
HttpConnectionContext connectionContext = null;
|
||||||
|
var connectionToken = GetConnectionToken(context);
|
||||||
|
if (connectionToken != null)
|
||||||
|
{
|
||||||
|
_manager.TryGetConnection(GetConnectionToken(context), out connectionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
var logScope = new ConnectionLogScope(connectionContext?.ConnectionId);
|
||||||
using (_logger.BeginScope(logScope))
|
using (_logger.BeginScope(logScope))
|
||||||
{
|
{
|
||||||
if (HttpMethods.IsPost(context.Request.Method))
|
if (HttpMethods.IsPost(context.Request.Method))
|
||||||
|
|
@ -279,13 +287,29 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
private async Task ProcessNegotiate(HttpContext context, HttpConnectionDispatcherOptions options, ConnectionLogScope logScope)
|
private async Task ProcessNegotiate(HttpContext context, HttpConnectionDispatcherOptions options, ConnectionLogScope logScope)
|
||||||
{
|
{
|
||||||
context.Response.ContentType = "application/json";
|
context.Response.ContentType = "application/json";
|
||||||
|
string error = null;
|
||||||
|
int clientProtocolVersion = 0;
|
||||||
|
if (context.Request.Query.TryGetValue("NegotiateVersion", out var queryStringVersion))
|
||||||
|
{
|
||||||
|
// Set the negotiate response to the protocol we use.
|
||||||
|
var queryStringVersionValue = queryStringVersion.ToString();
|
||||||
|
if (!int.TryParse(queryStringVersionValue, out clientProtocolVersion))
|
||||||
|
{
|
||||||
|
error = $"The client requested an invalid protocol version '{queryStringVersionValue}'";
|
||||||
|
Log.InvalidNegotiateProtocolVersion(_logger, queryStringVersionValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Establish the connection
|
// Establish the connection
|
||||||
var connection = CreateConnection(options);
|
HttpConnectionContext connection = null;
|
||||||
|
if (error == null)
|
||||||
|
{
|
||||||
|
connection = CreateConnection(options, clientProtocolVersion);
|
||||||
|
}
|
||||||
|
|
||||||
// Set the Connection ID on the logging scope so that logs from now on will have the
|
// Set the Connection ID on the logging scope so that logs from now on will have the
|
||||||
// Connection ID metadata set.
|
// Connection ID metadata set.
|
||||||
logScope.ConnectionId = connection.ConnectionId;
|
logScope.ConnectionId = connection?.ConnectionId;
|
||||||
|
|
||||||
// Don't use thread static instance here because writer is used with async
|
// Don't use thread static instance here because writer is used with async
|
||||||
var writer = new MemoryBufferWriter();
|
var writer = new MemoryBufferWriter();
|
||||||
|
|
@ -293,7 +317,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get the bytes for the connection id
|
// Get the bytes for the connection id
|
||||||
WriteNegotiatePayload(writer, connection.ConnectionId, context, options);
|
WriteNegotiatePayload(writer, connection?.ConnectionId, connection?.ConnectionToken, context, options, clientProtocolVersion, error);
|
||||||
|
|
||||||
Log.NegotiationRequest(_logger);
|
Log.NegotiationRequest(_logger);
|
||||||
|
|
||||||
|
|
@ -307,38 +331,34 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteNegotiatePayload(IBufferWriter<byte> writer, string connectionId, HttpContext context, HttpConnectionDispatcherOptions options)
|
private void WriteNegotiatePayload(IBufferWriter<byte> writer, string connectionId, string connectionToken, HttpContext context, HttpConnectionDispatcherOptions options,
|
||||||
|
int clientProtocolVersion, string error)
|
||||||
{
|
{
|
||||||
var response = new NegotiationResponse();
|
var response = new NegotiationResponse();
|
||||||
|
|
||||||
if (context.Request.Query.TryGetValue("NegotiateVersion", out var queryStringVersion))
|
if (!string.IsNullOrEmpty(error))
|
||||||
{
|
{
|
||||||
// Set the negotiate response to the protocol we use.
|
response.Error = error;
|
||||||
var queryStringVersionValue = queryStringVersion.ToString();
|
NegotiateProtocol.WriteResponse(response, writer);
|
||||||
if (int.TryParse(queryStringVersionValue, out var clientProtocolVersion))
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientProtocolVersion > 0)
|
||||||
|
{
|
||||||
|
if (clientProtocolVersion < options.MinimumProtocolVersion)
|
||||||
{
|
{
|
||||||
if (clientProtocolVersion < options.MinimumProtocolVersion)
|
response.Error = $"The client requested version '{clientProtocolVersion}', but the server does not support this version.";
|
||||||
{
|
Log.NegotiateProtocolVersionMismatch(_logger, clientProtocolVersion);
|
||||||
response.Error = $"The client requested version '{clientProtocolVersion}', but the server does not support this version.";
|
NegotiateProtocol.WriteResponse(response, writer);
|
||||||
Log.NegotiateProtocolVersionMismatch(_logger, clientProtocolVersion);
|
return;
|
||||||
NegotiateProtocol.WriteResponse(response, writer);
|
}
|
||||||
return;
|
else if (clientProtocolVersion > _protocolVersion)
|
||||||
}
|
{
|
||||||
else if (clientProtocolVersion > _protocolVersion)
|
response.Version = _protocolVersion;
|
||||||
{
|
|
||||||
response.Version = _protocolVersion;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response.Version = clientProtocolVersion;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
response.Error = $"The client requested an invalid protocol version '{queryStringVersionValue}'";
|
response.Version = clientProtocolVersion;
|
||||||
Log.InvalidNegotiateProtocolVersion(_logger, queryStringVersionValue);
|
|
||||||
NegotiateProtocol.WriteResponse(response, writer);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (options.MinimumProtocolVersion > 0)
|
else if (options.MinimumProtocolVersion > 0)
|
||||||
|
|
@ -350,6 +370,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
response.ConnectionId = connectionId;
|
response.ConnectionId = connectionId;
|
||||||
|
response.ConnectionToken = connectionToken;
|
||||||
response.AvailableTransports = new List<AvailableTransport>();
|
response.AvailableTransports = new List<AvailableTransport>();
|
||||||
|
|
||||||
if ((options.Transports & HttpTransportType.WebSockets) != 0 && ServerHasWebSockets(context.Features))
|
if ((options.Transports & HttpTransportType.WebSockets) != 0 && ServerHasWebSockets(context.Features))
|
||||||
|
|
@ -375,7 +396,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
return features.Get<IHttpWebSocketFeature>() != null;
|
return features.Get<IHttpWebSocketFeature>() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetConnectionId(HttpContext context) => context.Request.Query["id"];
|
private static string GetConnectionToken(HttpContext context) => context.Request.Query["id"];
|
||||||
|
|
||||||
private async Task ProcessSend(HttpContext context, HttpConnectionDispatcherOptions options)
|
private async Task ProcessSend(HttpContext context, HttpConnectionDispatcherOptions options)
|
||||||
{
|
{
|
||||||
|
|
@ -648,9 +669,9 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
|
|
||||||
private async Task<HttpConnectionContext> GetConnectionAsync(HttpContext context)
|
private async Task<HttpConnectionContext> GetConnectionAsync(HttpContext context)
|
||||||
{
|
{
|
||||||
var connectionId = GetConnectionId(context);
|
var connectionToken = GetConnectionToken(context);
|
||||||
|
|
||||||
if (StringValues.IsNullOrEmpty(connectionId))
|
if (StringValues.IsNullOrEmpty(connectionToken))
|
||||||
{
|
{
|
||||||
// There's no connection ID: bad request
|
// There's no connection ID: bad request
|
||||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||||
|
|
@ -659,7 +680,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_manager.TryGetConnection(connectionId, out var connection))
|
if (!_manager.TryGetConnection(connectionToken, out var connection))
|
||||||
{
|
{
|
||||||
// No connection with that ID: Not Found
|
// No connection with that ID: Not Found
|
||||||
context.Response.StatusCode = StatusCodes.Status404NotFound;
|
context.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||||
|
|
@ -674,15 +695,15 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
// This is only used for WebSockets connections, which can connect directly without negotiating
|
// This is only used for WebSockets connections, which can connect directly without negotiating
|
||||||
private async Task<HttpConnectionContext> GetOrCreateConnectionAsync(HttpContext context, HttpConnectionDispatcherOptions options)
|
private async Task<HttpConnectionContext> GetOrCreateConnectionAsync(HttpContext context, HttpConnectionDispatcherOptions options)
|
||||||
{
|
{
|
||||||
var connectionId = GetConnectionId(context);
|
var connectionToken = GetConnectionToken(context);
|
||||||
HttpConnectionContext connection;
|
HttpConnectionContext connection;
|
||||||
|
|
||||||
// There's no connection id so this is a brand new connection
|
// There's no connection id so this is a brand new connection
|
||||||
if (StringValues.IsNullOrEmpty(connectionId))
|
if (StringValues.IsNullOrEmpty(connectionToken))
|
||||||
{
|
{
|
||||||
connection = CreateConnection(options);
|
connection = CreateConnection(options);
|
||||||
}
|
}
|
||||||
else if (!_manager.TryGetConnection(connectionId, out connection))
|
else if (!_manager.TryGetConnection(connectionToken, out connection))
|
||||||
{
|
{
|
||||||
// No connection with that ID: Not Found
|
// No connection with that ID: Not Found
|
||||||
context.Response.StatusCode = StatusCodes.Status404NotFound;
|
context.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||||
|
|
@ -693,12 +714,11 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpConnectionContext CreateConnection(HttpConnectionDispatcherOptions options)
|
private HttpConnectionContext CreateConnection(HttpConnectionDispatcherOptions options, int clientProtocolVersion = 0)
|
||||||
{
|
{
|
||||||
var transportPipeOptions = new PipeOptions(pauseWriterThreshold: options.TransportMaxBufferSize, resumeWriterThreshold: options.TransportMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false);
|
var transportPipeOptions = new PipeOptions(pauseWriterThreshold: options.TransportMaxBufferSize, resumeWriterThreshold: options.TransportMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false);
|
||||||
var appPipeOptions = new PipeOptions(pauseWriterThreshold: options.ApplicationMaxBufferSize, resumeWriterThreshold: options.ApplicationMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false);
|
var appPipeOptions = new PipeOptions(pauseWriterThreshold: options.ApplicationMaxBufferSize, resumeWriterThreshold: options.ApplicationMaxBufferSize / 2, readerScheduler: PipeScheduler.ThreadPool, useSynchronizationContext: false);
|
||||||
|
return _manager.CreateConnection(transportPipeOptions, appPipeOptions, clientProtocolVersion);
|
||||||
return _manager.CreateConnection(transportPipeOptions, appPipeOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EmptyServiceProvider : IServiceProvider
|
private class EmptyServiceProvider : IServiceProvider
|
||||||
|
|
|
||||||
|
|
@ -78,18 +78,28 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
/// Creates a connection without Pipes setup to allow saving allocations until Pipes are needed.
|
/// Creates a connection without Pipes setup to allow saving allocations until Pipes are needed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
internal HttpConnectionContext CreateConnection(PipeOptions transportPipeOptions, PipeOptions appPipeOptions)
|
internal HttpConnectionContext CreateConnection(PipeOptions transportPipeOptions, PipeOptions appPipeOptions, int negotiateVersion = 0)
|
||||||
{
|
{
|
||||||
|
string connectionToken;
|
||||||
var id = MakeNewConnectionId();
|
var id = MakeNewConnectionId();
|
||||||
|
if (negotiateVersion > 0)
|
||||||
|
{
|
||||||
|
connectionToken = MakeNewConnectionId();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connectionToken = id;
|
||||||
|
}
|
||||||
|
|
||||||
Log.CreatedNewConnection(_logger, id);
|
Log.CreatedNewConnection(_logger, id);
|
||||||
var connectionTimer = HttpConnectionsEventSource.Log.ConnectionStart(id);
|
var connectionTimer = HttpConnectionsEventSource.Log.ConnectionStart(id);
|
||||||
var connection = new HttpConnectionContext(id, _connectionLogger);
|
var connection = new HttpConnectionContext(id, connectionToken, _connectionLogger);
|
||||||
var pair = DuplexPipe.CreateConnectionPair(transportPipeOptions, appPipeOptions);
|
var pair = DuplexPipe.CreateConnectionPair(transportPipeOptions, appPipeOptions);
|
||||||
connection.Transport = pair.Application;
|
connection.Transport = pair.Application;
|
||||||
connection.Application = pair.Transport;
|
connection.Application = pair.Transport;
|
||||||
|
|
||||||
_connections.TryAdd(id, (connection, connectionTimer));
|
_connections.TryAdd(connectionToken, (connection, connectionTimer));
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,7 +215,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
|
||||||
{
|
{
|
||||||
// Remove it from the list after disposal so that's it's easy to see
|
// Remove it from the list after disposal so that's it's easy to see
|
||||||
// connections that might be in a hung state via the connections list
|
// connections that might be in a hung state via the connections list
|
||||||
RemoveConnection(connection.ConnectionId);
|
RemoveConnection(connection.ConnectionToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
public class HttpConnectionDispatcherTests : VerifiableLoggedTest
|
public class HttpConnectionDispatcherTests : VerifiableLoggedTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task NegotiateReservesConnectionIdAndReturnsIt()
|
public async Task NegotiateVersionZeroReservesConnectionIdAndReturnsIt()
|
||||||
{
|
{
|
||||||
using (StartVerifiableLog())
|
using (StartVerifiableLog())
|
||||||
{
|
{
|
||||||
|
|
@ -54,8 +54,35 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
await dispatcher.ExecuteNegotiateAsync(context, new HttpConnectionDispatcherOptions());
|
await dispatcher.ExecuteNegotiateAsync(context, new HttpConnectionDispatcherOptions());
|
||||||
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
var connectionId = negotiateResponse.Value<string>("connectionId");
|
var connectionId = negotiateResponse.Value<string>("connectionId");
|
||||||
Assert.True(manager.TryGetConnection(connectionId, out var connectionContext));
|
var connectionToken = negotiateResponse.Value<string>("connectionToken");
|
||||||
|
Assert.Null(connectionToken);
|
||||||
|
Assert.NotNull(connectionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task NegotiateReservesConnectionTokenAndConnectionIdAndReturnsIt()
|
||||||
|
{
|
||||||
|
using (StartVerifiableLog())
|
||||||
|
{
|
||||||
|
var manager = CreateConnectionManager(LoggerFactory);
|
||||||
|
var dispatcher = new HttpConnectionDispatcher(manager, LoggerFactory);
|
||||||
|
var context = new DefaultHttpContext();
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddSingleton<TestConnectionHandler>();
|
||||||
|
services.AddOptions();
|
||||||
|
var ms = new MemoryStream();
|
||||||
|
context.Request.Path = "/foo";
|
||||||
|
context.Request.Method = "POST";
|
||||||
|
context.Response.Body = ms;
|
||||||
|
context.Request.QueryString = new QueryString("?negotiateVersion=1");
|
||||||
|
await dispatcher.ExecuteNegotiateAsync(context, new HttpConnectionDispatcherOptions());
|
||||||
|
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
|
var connectionId = negotiateResponse.Value<string>("connectionId");
|
||||||
|
var connectionToken = negotiateResponse.Value<string>("connectionToken");
|
||||||
|
Assert.True(manager.TryGetConnection(connectionToken, out var connectionContext));
|
||||||
Assert.Equal(connectionId, connectionContext.ConnectionId);
|
Assert.Equal(connectionId, connectionContext.ConnectionId);
|
||||||
|
Assert.NotEqual(connectionId, connectionToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,12 +101,13 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
context.Response.Body = ms;
|
context.Response.Body = ms;
|
||||||
|
context.Request.QueryString = new QueryString("?negotiateVersion=1");
|
||||||
var options = new HttpConnectionDispatcherOptions { TransportMaxBufferSize = 4, ApplicationMaxBufferSize = 4 };
|
var options = new HttpConnectionDispatcherOptions { TransportMaxBufferSize = 4, ApplicationMaxBufferSize = 4 };
|
||||||
await dispatcher.ExecuteNegotiateAsync(context, options);
|
await dispatcher.ExecuteNegotiateAsync(context, options);
|
||||||
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
var connectionId = negotiateResponse.Value<string>("connectionId");
|
var connectionToken = negotiateResponse.Value<string>("connectionToken");
|
||||||
context.Request.QueryString = context.Request.QueryString.Add("id", connectionId);
|
context.Request.QueryString = context.Request.QueryString.Add("id", connectionToken);
|
||||||
Assert.True(manager.TryGetConnection(connectionId, out var connection));
|
Assert.True(manager.TryGetConnection(connectionToken, out var connection));
|
||||||
// Fake actual connection after negotiate to populate the pipes on the connection
|
// Fake actual connection after negotiate to populate the pipes on the connection
|
||||||
await dispatcher.ExecuteAsync(context, options, c => Task.CompletedTask);
|
await dispatcher.ExecuteAsync(context, options, c => Task.CompletedTask);
|
||||||
|
|
||||||
|
|
@ -111,7 +139,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
context.Response.Body = ms;
|
context.Response.Body = ms;
|
||||||
context.Request.QueryString = new QueryString("?negotiateVersion=Invalid");
|
context.Request.QueryString = new QueryString("?negotiateVersion=Invalid");
|
||||||
|
|
||||||
var options = new HttpConnectionDispatcherOptions { TransportMaxBufferSize = 4, ApplicationMaxBufferSize = 4 };
|
var options = new HttpConnectionDispatcherOptions { TransportMaxBufferSize = 4, ApplicationMaxBufferSize = 4 };
|
||||||
await dispatcher.ExecuteNegotiateAsync(context, options);
|
await dispatcher.ExecuteNegotiateAsync(context, options);
|
||||||
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
|
|
@ -140,7 +167,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
context.Response.Body = ms;
|
context.Response.Body = ms;
|
||||||
context.Request.QueryString = new QueryString("");
|
context.Request.QueryString = new QueryString("");
|
||||||
|
|
||||||
var options = new HttpConnectionDispatcherOptions { TransportMaxBufferSize = 4, ApplicationMaxBufferSize = 4, MinimumProtocolVersion = 1 };
|
var options = new HttpConnectionDispatcherOptions { TransportMaxBufferSize = 4, ApplicationMaxBufferSize = 4, MinimumProtocolVersion = 1 };
|
||||||
await dispatcher.ExecuteNegotiateAsync(context, options);
|
await dispatcher.ExecuteNegotiateAsync(context, options);
|
||||||
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
|
|
@ -183,7 +209,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -224,6 +251,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
context.Response.Body = ms;
|
context.Response.Body = ms;
|
||||||
|
context.Request.QueryString = new QueryString("?negotiateVersion=1");
|
||||||
await dispatcher.ExecuteNegotiateAsync(context, new HttpConnectionDispatcherOptions { Transports = transports });
|
await dispatcher.ExecuteNegotiateAsync(context, new HttpConnectionDispatcherOptions { Transports = transports });
|
||||||
|
|
||||||
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
|
|
@ -262,6 +290,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Method = "GET";
|
context.Request.Method = "GET";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = "unknown";
|
values["id"] = "unknown";
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
SetTransport(context, transportType);
|
SetTransport(context, transportType);
|
||||||
|
|
@ -298,6 +327,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = "unknown";
|
values["id"] = "unknown";
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -334,7 +364,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -373,6 +404,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionId;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -412,7 +444,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "GET";
|
context.Request.Method = "GET";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -473,7 +506,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "GET";
|
context.Request.Method = "GET";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -539,7 +573,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -602,6 +637,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionId;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -671,7 +707,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "GET";
|
context.Request.Method = "GET";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
values["another"] = "value";
|
values["another"] = "value";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
@ -719,8 +756,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
var connectionHttpContext = connection.GetHttpContext();
|
var connectionHttpContext = connection.GetHttpContext();
|
||||||
Assert.NotNull(connectionHttpContext);
|
Assert.NotNull(connectionHttpContext);
|
||||||
|
|
||||||
Assert.Equal(2, connectionHttpContext.Request.Query.Count);
|
Assert.Equal(3, connectionHttpContext.Request.Query.Count);
|
||||||
Assert.Equal(connection.ConnectionId, connectionHttpContext.Request.Query["id"]);
|
|
||||||
Assert.Equal("value", connectionHttpContext.Request.Query["another"]);
|
Assert.Equal("value", connectionHttpContext.Request.Query["another"]);
|
||||||
|
|
||||||
Assert.Equal(3, connectionHttpContext.Request.Headers.Count);
|
Assert.Equal(3, connectionHttpContext.Request.Headers.Count);
|
||||||
|
|
@ -764,6 +800,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
services.AddSingleton<TestConnectionHandler>();
|
services.AddSingleton<TestConnectionHandler>();
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "GET";
|
context.Request.Method = "GET";
|
||||||
|
context.Request.QueryString = new QueryString("?negotiateVersion=1");
|
||||||
|
|
||||||
SetTransport(context, transportType);
|
SetTransport(context, transportType);
|
||||||
|
|
||||||
|
|
@ -806,7 +843,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -833,6 +871,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
services.AddSingleton<TestConnectionHandler>();
|
services.AddSingleton<TestConnectionHandler>();
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
|
context.Request.QueryString = new QueryString("?negotiateVersion=1");
|
||||||
|
|
||||||
var builder = new ConnectionBuilder(services.BuildServiceProvider());
|
var builder = new ConnectionBuilder(services.BuildServiceProvider());
|
||||||
builder.UseConnectionHandler<TestConnectionHandler>();
|
builder.UseConnectionHandler<TestConnectionHandler>();
|
||||||
|
|
@ -904,6 +943,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
var dispatcher = new HttpConnectionDispatcher(manager, LoggerFactory);
|
var dispatcher = new HttpConnectionDispatcher(manager, LoggerFactory);
|
||||||
|
|
||||||
var context = MakeRequest("/foo", connection);
|
var context = MakeRequest("/foo", connection);
|
||||||
|
|
||||||
SetTransport(context, HttpTransportType.ServerSentEvents);
|
SetTransport(context, HttpTransportType.ServerSentEvents);
|
||||||
|
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
|
|
@ -915,7 +955,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
|
|
||||||
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||||
|
|
||||||
var exists = manager.TryGetConnection(connection.ConnectionId, out _);
|
var exists = manager.TryGetConnection(connection.ConnectionToken, out _);
|
||||||
Assert.False(exists);
|
Assert.False(exists);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1279,7 +1319,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
await task;
|
await task;
|
||||||
|
|
||||||
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||||
var exists = manager.TryGetConnection(connection.ConnectionId, out _);
|
var exists = manager.TryGetConnection(connection.ConnectionToken, out _);
|
||||||
Assert.False(exists);
|
Assert.False(exists);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1320,7 +1360,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
await task;
|
await task;
|
||||||
|
|
||||||
Assert.Equal(StatusCodes.Status204NoContent, context.Response.StatusCode);
|
Assert.Equal(StatusCodes.Status204NoContent, context.Response.StatusCode);
|
||||||
var exists = manager.TryGetConnection(connection.ConnectionId, out _);
|
var exists = manager.TryGetConnection(connection.ConnectionToken, out _);
|
||||||
Assert.False(exists);
|
Assert.False(exists);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1422,10 +1462,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Method = "GET";
|
context.Request.Method = "GET";
|
||||||
context.RequestServices = sp;
|
context.RequestServices = sp;
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
var builder = new ConnectionBuilder(sp);
|
var builder = new ConnectionBuilder(sp);
|
||||||
builder.UseConnectionHandler<TestConnectionHandler>();
|
builder.UseConnectionHandler<TestConnectionHandler>();
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
@ -1510,7 +1550,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
// Issue the delete request
|
// Issue the delete request
|
||||||
var deleteContext = new DefaultHttpContext();
|
var deleteContext = new DefaultHttpContext();
|
||||||
deleteContext.Request.Path = "/foo";
|
deleteContext.Request.Path = "/foo";
|
||||||
deleteContext.Request.QueryString = new QueryString($"?id={connection.ConnectionId}");
|
deleteContext.Request.QueryString = new QueryString($"?id={connection.ConnectionToken}");
|
||||||
deleteContext.Request.Method = "DELETE";
|
deleteContext.Request.Method = "DELETE";
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
deleteContext.Response.Body = ms;
|
deleteContext.Response.Body = ms;
|
||||||
|
|
@ -1553,7 +1593,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
// Issue the delete request and make sure the poll completes
|
// Issue the delete request and make sure the poll completes
|
||||||
var deleteContext = new DefaultHttpContext();
|
var deleteContext = new DefaultHttpContext();
|
||||||
deleteContext.Request.Path = "/foo";
|
deleteContext.Request.Path = "/foo";
|
||||||
deleteContext.Request.QueryString = new QueryString($"?id={connection.ConnectionId}");
|
deleteContext.Request.QueryString = new QueryString($"?id={connection.ConnectionToken}");
|
||||||
deleteContext.Request.Method = "DELETE";
|
deleteContext.Request.Method = "DELETE";
|
||||||
|
|
||||||
Assert.False(pollTask.IsCompleted);
|
Assert.False(pollTask.IsCompleted);
|
||||||
|
|
@ -1571,7 +1611,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
Assert.Equal("text/plain", deleteContext.Response.ContentType);
|
Assert.Equal("text/plain", deleteContext.Response.ContentType);
|
||||||
|
|
||||||
// Verify the connection was removed from the manager
|
// Verify the connection was removed from the manager
|
||||||
Assert.False(manager.TryGetConnection(connection.ConnectionId, out _));
|
Assert.False(manager.TryGetConnection(connection.ConnectionToken, out _));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1601,7 +1641,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
// Issue the delete request and make sure the poll completes
|
// Issue the delete request and make sure the poll completes
|
||||||
var deleteContext = new DefaultHttpContext();
|
var deleteContext = new DefaultHttpContext();
|
||||||
deleteContext.Request.Path = "/foo";
|
deleteContext.Request.Path = "/foo";
|
||||||
deleteContext.Request.QueryString = new QueryString($"?id={connection.ConnectionId}");
|
deleteContext.Request.QueryString = new QueryString($"?id={connection.ConnectionToken}");
|
||||||
deleteContext.Request.Method = "DELETE";
|
deleteContext.Request.Method = "DELETE";
|
||||||
|
|
||||||
await dispatcher.ExecuteAsync(deleteContext, options, app).OrTimeout();
|
await dispatcher.ExecuteAsync(deleteContext, options, app).OrTimeout();
|
||||||
|
|
@ -1619,7 +1659,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
await connection.DisposeAndRemoveTask.OrTimeout();
|
await connection.DisposeAndRemoveTask.OrTimeout();
|
||||||
|
|
||||||
// Verify the connection was removed from the manager
|
// Verify the connection was removed from the manager
|
||||||
Assert.False(manager.TryGetConnection(connection.ConnectionId, out _));
|
Assert.False(manager.TryGetConnection(connection.ConnectionToken, out _));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1639,6 +1679,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
context.Response.Body = ms;
|
context.Response.Body = ms;
|
||||||
|
context.Request.QueryString = new QueryString("?negotiateVersion=1");
|
||||||
await dispatcher.ExecuteNegotiateAsync(context, new HttpConnectionDispatcherOptions { Transports = HttpTransportType.WebSockets });
|
await dispatcher.ExecuteNegotiateAsync(context, new HttpConnectionDispatcherOptions { Transports = HttpTransportType.WebSockets });
|
||||||
|
|
||||||
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
var negotiateResponse = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(ms.ToArray()));
|
||||||
|
|
@ -1695,7 +1736,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
var buffer = Encoding.UTF8.GetBytes("Hello, world");
|
var buffer = Encoding.UTF8.GetBytes("Hello, world");
|
||||||
|
|
@ -1751,7 +1793,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
var buffer = Encoding.UTF8.GetBytes("Hello, world");
|
var buffer = Encoding.UTF8.GetBytes("Hello, world");
|
||||||
|
|
@ -1804,7 +1847,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "POST";
|
context.Request.Method = "POST";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
var buffer = Encoding.UTF8.GetBytes("Hello, world");
|
var buffer = Encoding.UTF8.GetBytes("Hello, world");
|
||||||
|
|
@ -1866,7 +1910,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
await pollTask.OrTimeout();
|
await pollTask.OrTimeout();
|
||||||
|
|
||||||
Assert.Equal(StatusCodes.Status500InternalServerError, pollContext.Response.StatusCode);
|
Assert.Equal(StatusCodes.Status500InternalServerError, pollContext.Response.StatusCode);
|
||||||
Assert.False(manager.TryGetConnection(connection.ConnectionId, out var _));
|
Assert.False(manager.TryGetConnection(connection.ConnectionToken, out var _));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1889,7 +1933,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
context.Request.Path = "/foo";
|
context.Request.Path = "/foo";
|
||||||
context.Request.Method = "GET";
|
context.Request.Method = "GET";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
var qs = new QueryCollection(values);
|
var qs = new QueryCollection(values);
|
||||||
context.Request.Query = qs;
|
context.Request.Query = qs;
|
||||||
|
|
||||||
|
|
@ -1911,14 +1956,15 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DefaultHttpContext MakeRequest(string path, ConnectionContext connection, string format = null)
|
private static DefaultHttpContext MakeRequest(string path, HttpConnectionContext connection, string format = null)
|
||||||
{
|
{
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
context.Features.Set<IHttpResponseFeature>(new ResponseFeature());
|
context.Features.Set<IHttpResponseFeature>(new ResponseFeature());
|
||||||
context.Request.Path = path;
|
context.Request.Path = path;
|
||||||
context.Request.Method = "GET";
|
context.Request.Method = "GET";
|
||||||
var values = new Dictionary<string, StringValues>();
|
var values = new Dictionary<string, StringValues>();
|
||||||
values["id"] = connection.ConnectionId;
|
values["id"] = connection.ConnectionToken;
|
||||||
|
values["negotiateVersion"] = "1";
|
||||||
if (format != null)
|
if (format != null)
|
||||||
{
|
{
|
||||||
values["format"] = format;
|
values["format"] = format;
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
|
|
||||||
Assert.NotNull(connection.ConnectionId);
|
Assert.NotNull(connection.ConnectionId);
|
||||||
|
|
||||||
Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection));
|
Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection));
|
||||||
Assert.Same(newConnection, connection);
|
Assert.Same(newConnection, connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,13 +143,13 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
{
|
{
|
||||||
var connectionManager = CreateConnectionManager(LoggerFactory);
|
var connectionManager = CreateConnectionManager(LoggerFactory);
|
||||||
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
|
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
|
||||||
|
|
||||||
var transport = connection.Transport;
|
var transport = connection.Transport;
|
||||||
|
|
||||||
Assert.NotNull(connection.ConnectionId);
|
Assert.NotNull(connection.ConnectionId);
|
||||||
|
Assert.NotNull(connection.ConnectionToken);
|
||||||
Assert.NotNull(transport);
|
Assert.NotNull(transport);
|
||||||
|
|
||||||
Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection));
|
Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection));
|
||||||
Assert.Same(newConnection, connection);
|
Assert.Same(newConnection, connection);
|
||||||
Assert.Same(transport, newConnection.Transport);
|
Assert.Same(transport, newConnection.Transport);
|
||||||
}
|
}
|
||||||
|
|
@ -168,12 +168,55 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
Assert.NotNull(connection.ConnectionId);
|
Assert.NotNull(connection.ConnectionId);
|
||||||
Assert.NotNull(transport);
|
Assert.NotNull(transport);
|
||||||
|
|
||||||
Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection));
|
Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection));
|
||||||
Assert.Same(newConnection, connection);
|
Assert.Same(newConnection, connection);
|
||||||
Assert.Same(transport, newConnection.Transport);
|
Assert.Same(transport, newConnection.Transport);
|
||||||
|
|
||||||
connectionManager.RemoveConnection(connection.ConnectionId);
|
connectionManager.RemoveConnection(connection.ConnectionToken);
|
||||||
Assert.False(connectionManager.TryGetConnection(connection.ConnectionId, out newConnection));
|
Assert.False(connectionManager.TryGetConnection(connection.ConnectionToken, out newConnection));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConnectionIdAndConnectionTokenAreTheSameForNegotiateVersionZero()
|
||||||
|
{
|
||||||
|
using (StartVerifiableLog())
|
||||||
|
{
|
||||||
|
var connectionManager = CreateConnectionManager(LoggerFactory);
|
||||||
|
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default, negotiateVersion: 0);
|
||||||
|
|
||||||
|
var transport = connection.Transport;
|
||||||
|
|
||||||
|
Assert.NotNull(connection.ConnectionId);
|
||||||
|
Assert.NotNull(transport);
|
||||||
|
|
||||||
|
Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection));
|
||||||
|
Assert.Same(newConnection, connection);
|
||||||
|
Assert.Same(transport, newConnection.Transport);
|
||||||
|
Assert.Equal(connection.ConnectionId, connection.ConnectionToken);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConnectionIdAndConnectionTokenAreDifferentForNegotiateVersionOne()
|
||||||
|
{
|
||||||
|
using (StartVerifiableLog())
|
||||||
|
{
|
||||||
|
var connectionManager = CreateConnectionManager(LoggerFactory);
|
||||||
|
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default, negotiateVersion: 1);
|
||||||
|
|
||||||
|
var transport = connection.Transport;
|
||||||
|
|
||||||
|
Assert.NotNull(connection.ConnectionId);
|
||||||
|
Assert.NotNull(transport);
|
||||||
|
|
||||||
|
Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection));
|
||||||
|
Assert.False(connectionManager.TryGetConnection(connection.ConnectionId, out var _));
|
||||||
|
Assert.Same(newConnection, connection);
|
||||||
|
Assert.Same(transport, newConnection.Transport);
|
||||||
|
Assert.NotEqual(connection.ConnectionId, connection.ConnectionToken);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,20 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
public class NegotiateProtocolTests
|
public class NegotiateProtocolTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 0)]
|
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 0, null)]
|
||||||
[InlineData("{\"connectionId\":\"\",\"availableTransports\":[]}", "", new string[0], null, null, 0)]
|
[InlineData("{\"connectionId\":\"\",\"availableTransports\":[]}", "", new string[0], null, null, 0, null)]
|
||||||
[InlineData("{\"url\": \"http://foo.com/chat\"}", null, null, "http://foo.com/chat", null, 0)]
|
[InlineData("{\"url\": \"http://foo.com/chat\"}", null, null, "http://foo.com/chat", null, 0, null)]
|
||||||
[InlineData("{\"url\": \"http://foo.com/chat\", \"accessToken\": \"token\"}", null, null, "http://foo.com/chat", "token", 0)]
|
[InlineData("{\"url\": \"http://foo.com/chat\", \"accessToken\": \"token\"}", null, null, "http://foo.com/chat", "token", 0, null)]
|
||||||
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"transport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 0)]
|
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"transport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 0, null)]
|
||||||
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"\\u0074ransport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 0)]
|
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"\\u0074ransport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 0, null)]
|
||||||
[InlineData("{\"negotiateVersion\":123,\"connectionId\":\"123\",\"availableTransports\":[{\"\\u0074ransport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 123)]
|
[InlineData("{\"negotiateVersion\":123,\"connectionId\":\"123\",\"connectionToken\":\"789\",\"availableTransports\":[{\"\\u0074ransport\":\"test\",\"transferFormats\":[]}]}", "123", new[] { "test" }, null, null, 123, "789")]
|
||||||
[InlineData("{\"negotiateVersion\":123,\"negotiateVersion\":321,\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 321)]
|
[InlineData("{\"negotiateVersion\":123,\"negotiateVersion\":321, \"connectionToken\":\"789\",\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 321, "789")]
|
||||||
[InlineData("{\"ignore\":123,\"negotiateVersion\":123,\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 123)]
|
[InlineData("{\"ignore\":123,\"negotiateVersion\":123, \"connectionToken\":\"789\",\"connectionId\":\"123\",\"availableTransports\":[]}", "123", new string[0], null, null, 123, "789")]
|
||||||
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[],\"negotiateVersion\":123}", "123", new string[0], null, null, 123)]
|
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[],\"negotiateVersion\":123, \"connectionToken\":\"789\"}", "123", new string[0], null, null, 123, "789")]
|
||||||
public void ParsingNegotiateResponseMessageSuccessForValid(string json, string connectionId, string[] availableTransports, string url, string accessToken, int version)
|
[InlineData("{\"connectionId\":\"123\",\"connectionToken\":\"789\",\"availableTransports\":[]}", "123", new string[0], null, null, 0, "789")]
|
||||||
|
[InlineData("{\"connectionToken\":\"789\",\"connectionId\":\"123\",\"availableTransports\":[],\"negotiateVersion\":123}", "123", new string[0], null, null, 123, "789")]
|
||||||
|
[InlineData("{\"connectionToken\":\"789\",\"connectionId\":\"123\",\"availableTransports\":[],\"negotiateVersion\":123, \"connectionToken\":\"987\"}", "123", new string[0], null, null, 123, "987")]
|
||||||
|
public void ParsingNegotiateResponseMessageSuccessForValid(string json, string connectionId, string[] availableTransports, string url, string accessToken, int version, string connectionToken)
|
||||||
{
|
{
|
||||||
var responseData = Encoding.UTF8.GetBytes(json);
|
var responseData = Encoding.UTF8.GetBytes(json);
|
||||||
var response = NegotiateProtocol.ParseResponse(responseData);
|
var response = NegotiateProtocol.ParseResponse(responseData);
|
||||||
|
|
@ -33,6 +36,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
Assert.Equal(url, response.Url);
|
Assert.Equal(url, response.Url);
|
||||||
Assert.Equal(accessToken, response.AccessToken);
|
Assert.Equal(accessToken, response.AccessToken);
|
||||||
Assert.Equal(version, response.Version);
|
Assert.Equal(version, response.Version);
|
||||||
|
Assert.Equal(connectionToken, response.ConnectionToken);
|
||||||
|
|
||||||
if (response.AvailableTransports != null)
|
if (response.AvailableTransports != null)
|
||||||
{
|
{
|
||||||
|
|
@ -50,6 +54,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":null}", "Unexpected JSON Token Type 'Null'. Expected a JSON Array.")]
|
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":null}", "Unexpected JSON Token Type 'Null'. Expected a JSON Array.")]
|
||||||
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"transferFormats\":[]}]}", "Missing required property 'transport'.")]
|
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"transferFormats\":[]}]}", "Missing required property 'transport'.")]
|
||||||
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"transport\":\"test\"}]}", "Missing required property 'transferFormats'.")]
|
[InlineData("{\"connectionId\":\"123\",\"availableTransports\":[{\"transport\":\"test\"}]}", "Missing required property 'transferFormats'.")]
|
||||||
|
[InlineData("{\"connectionId\":\"123\",\"negotiateVersion\":123,\"availableTransports\":[]}", "Missing required property 'connectionToken'.")]
|
||||||
public void ParsingNegotiateResponseMessageThrowsForInvalid(string payload, string expectedMessage)
|
public void ParsingNegotiateResponseMessageThrowsForInvalid(string payload, string expectedMessage)
|
||||||
{
|
{
|
||||||
var responseData = Encoding.UTF8.GetBytes(payload);
|
var responseData = Encoding.UTF8.GetBytes(payload);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue