-
Notifications
You must be signed in to change notification settings - Fork 446
Add error to negotiate #2998
Add error to negotiate #2998
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ Throughout this document, the term `[endpoint-base]` is used to refer to the rou | |
|
||
## `POST [endpoint-base]/negotiate` request | ||
|
||
The `POST [endpoint-base]/negotiate` request is used to establish a connection between the client and the server. The content type of the response is `application/json`. The response to the `POST [endpoint-base]/negotiate` request contains one of two types of responses: | ||
The `POST [endpoint-base]/negotiate` request is used to establish a connection between the client and the server. The content type of the response is `application/json`. The response to the `POST [endpoint-base]/negotiate` request contains one of three types of responses: | ||
|
||
1. A response that contains the `id` which will be used to identify the connection on the server and the list of the transports supported by the server. | ||
|
||
|
@@ -62,6 +62,19 @@ The `POST [endpoint-base]/negotiate` request is used to establish a connection b | |
* The `url` which is the URL the client should connect to. | ||
* The `accessToken` which is an optional bearer token for accessing the specified url. | ||
|
||
|
||
3. A response that contains an `error` which should abort the connection attempt. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change abort to stop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we need a protocol version here @anurse There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it's not a bad idea. But do we want to do the two-part handshake that ASP.NET SignalR does? Where the client reports it's max supported version? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ERRR, I hope we don't need to change it that much. |
||
|
||
``` | ||
{ | ||
"error": "This connection is not allowed." | ||
} | ||
``` | ||
|
||
The payload returned from this endpoint provides the following data: | ||
|
||
* The `error` that gives details about why the negotiate failed. | ||
|
||
## Transfer Formats | ||
|
||
ASP.NET Endpoints support two different transfer formats: `Text` and `Binary`. `Text` refers to UTF-8 text, and `Binary` refers to any arbitrary binary data. The transfer format serves two purposes. First, in the WebSockets transport, it is used to determine if `Text` or `Binary` WebSocket frames should be used to carry data. This is useful in debugging as most browser Dev Tools only show the content of `Text` frames. When using a text-based protocol like JSON, it is preferable for the WebSockets transport to use `Text` frames. How a client/server indicate the transfer format currently being used is implementation-defined. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -169,9 +169,9 @@ internal HttpConnection(HttpConnectionOptions httpConnectionOptions, ILoggerFact | |
/// A connection cannot be restarted after it has stopped. To restart a connection | ||
/// a new instance should be created using the same options. | ||
/// </remarks> | ||
public async Task StartAsync(CancellationToken cancellationToken = default) | ||
public Task StartAsync(CancellationToken cancellationToken = default) | ||
{ | ||
await StartAsync(TransferFormat.Binary, cancellationToken); | ||
return StartAsync(TransferFormat.Binary, cancellationToken); | ||
} | ||
|
||
/// <summary> | ||
|
@@ -428,6 +428,10 @@ private async Task<NegotiationResponse> NegotiateAsync(Uri url, HttpClient httpC | |
{ | ||
negotiateResponse = NegotiateProtocol.ParseResponse(responseStream); | ||
} | ||
if (!string.IsNullOrEmpty(negotiateResponse.Error)) | ||
{ | ||
throw new InvalidOperationException(negotiateResponse.Error); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
Log.ConnectionEstablished(_logger, negotiateResponse.ConnectionId); | ||
return negotiateResponse; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,10 @@ | |
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Connections; | ||
using Microsoft.AspNetCore.Http.Connections; | ||
using Microsoft.AspNetCore.Http.Connections.Client; | ||
using Microsoft.AspNetCore.Http.Connections.Client.Internal; | ||
using Microsoft.AspNetCore.SignalR.Tests; | ||
using Microsoft.Extensions.Logging.Testing; | ||
using Moq; | ||
using Newtonsoft.Json; | ||
using Xunit; | ||
|
@@ -380,6 +382,37 @@ public async Task StartSkipsOverTransportsThatDoNotSupportTheRequredTransferForm | |
}); | ||
} | ||
|
||
[Fact] | ||
public async Task NegotiateThatReturnsErrorThrowsFromStart() | ||
{ | ||
bool ExpectedError(WriteContext writeContext) | ||
{ | ||
return writeContext.LoggerName == typeof(HttpConnection).FullName && | ||
writeContext.EventId.Name == "ErrorWithNegotiation"; | ||
} | ||
|
||
var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false); | ||
testHttpHandler.OnNegotiate((request, cancellationToken) => | ||
{ | ||
return ResponseUtils.CreateResponse(HttpStatusCode.OK, | ||
JsonConvert.SerializeObject(new | ||
{ | ||
error = "Negotiate failed." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Change this error message to something that isn't about negotiation. This seems like something that could be ambiguous |
||
})); | ||
}); | ||
|
||
using (var noErrorScope = new VerifyNoErrorsScope(expectedErrorsFilter: ExpectedError)) | ||
{ | ||
await WithConnectionAsync( | ||
CreateConnection(testHttpHandler, loggerFactory: noErrorScope.LoggerFactory), | ||
async (connection) => | ||
{ | ||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => connection.StartAsync(TransferFormat.Text).OrTimeout()); | ||
Assert.Equal("Negotiate failed.", exception.Message); | ||
}); | ||
} | ||
} | ||
|
||
private async Task RunInvalidNegotiateResponseTest<TException>(string negotiatePayload, string expectedExceptionMessage) where TException : Exception | ||
{ | ||
var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the parsing logic should exit early like this. You're right that an
error
property generally means everything else should be ignored, but let the caller decide that.