Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding accept header to callsites within SignalR #55271

Merged
merged 47 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
9a06954
Adding accept header within NegotiateAsync() and default headers in C…
MattyLeslie Apr 18, 2024
98f859f
Removing defaultRequestHeader for accept and focusing on setting the …
MattyLeslie Apr 19, 2024
b4bebb5
Removing the logic to clear accept headers before allocating the acce…
MattyLeslie Apr 19, 2024
018350b
Adding accept header to LongPolling Get request within the Poll() method
MattyLeslie Apr 21, 2024
a6fc3e5
Adding the accept header to SendUtils call site.
MattyLeslie Apr 21, 2024
6d793df
Added the accept header to the LongPolling callsite for the inital po…
MattyLeslie Apr 21, 2024
a56e8ed
Merge branch 'dotnet:main' into SignalR--Missing-Accept-Header
MattyLeslie Apr 22, 2024
7576ac9
Removing whitespaces
MattyLeslie Apr 22, 2024
168a108
Removing comment and fixing whitespace
MattyLeslie Apr 22, 2024
c3704ef
Correcting indentations
MattyLeslie Apr 22, 2024
e993ecc
retrigger checks
MattyLeslie Apr 22, 2024
a00806a
retrigger checks
MattyLeslie Apr 22, 2024
baea8dc
retrigger checks
MattyLeslie Apr 22, 2024
d5e1948
Merge branch 'dotnet:main' into SignalR--Missing-Accept-Header
MattyLeslie Apr 22, 2024
335c09e
retrigger checks
MattyLeslie Apr 22, 2024
ead6b97
Merge branch 'dotnet:main' into SignalR--Missing-Accept-Header
MattyLeslie Apr 22, 2024
a0467c2
Removing default headers
MattyLeslie Apr 24, 2024
9c214bb
adding tests for LongPollingTransport to check accept headers are set…
MattyLeslie Apr 24, 2024
feb1330
Adding tests for ServerSentEventsTransport to check accept headers ar…
MattyLeslie Apr 24, 2024
fafbd9f
Merge branch 'dotnet:main' into SignalR--Missing-Accept-Header
MattyLeslie Apr 24, 2024
35614b7
Removing the Accept header addition for the SendDeleteRequestMethod()
MattyLeslie Apr 24, 2024
39ddeb3
HttpConnection tests for NegotiateAsync
MattyLeslie Apr 29, 2024
6c5dd09
Better method naming
MattyLeslie Apr 29, 2024
0204a27
Improving LongPollingTransportTests
MattyLeslie Apr 29, 2024
0b828ca
Creating accept header tests for the SendUtils
MattyLeslie Apr 29, 2024
69dae2a
Improving ServerSentEventTests for accept header
MattyLeslie Apr 29, 2024
bd2609f
Adding an option parameter to HttpConnections contructor used for uni…
MattyLeslie May 2, 2024
d570d08
Addition of Test method to ensure NegotiateAsync() appends the correc…
MattyLeslie May 2, 2024
6f84149
Removing changes related to injecting httpMessageHandler
MattyLeslie May 3, 2024
fe3a78a
Adjusting test to make use of httpmessageHandlerFactory and better pr…
MattyLeslie May 3, 2024
50afbcb
Updating implementation for NegotiateAsyncAppendsCorrectAcceptHeader
MattyLeslie May 6, 2024
0aaf06f
Uncommenting the addition of the accept header in NegotiateAsync()
MattyLeslie May 6, 2024
14387c7
Final implementation for checking the correct header is appended to N…
MattyLeslie May 7, 2024
acbea26
Ensuring the TaskCompletionSource is allocated to
MattyLeslie May 7, 2024
f6c6b14
Merge branch 'dotnet:main' into SignalR--Missing-Accept-Header
MattyLeslie May 7, 2024
824df9a
Merge branch 'SignalR--Missing-Accept-Header' of https://github.com/M…
MattyLeslie May 7, 2024
4c8c275
Updating all TaskCompletionSources to run continuations asynchronously
MattyLeslie May 10, 2024
64b15b9
Sorting headers
MattyLeslie May 10, 2024
fb4d3e2
SendUtils test to use testHttpHandler instead of moq
MattyLeslie May 14, 2024
a226948
ServerSentEvents headers test to use testHttpHandler instead of moq
MattyLeslie May 14, 2024
1a5a21e
Improvement to PollRequestsContainCorrectAcceptHeader method to effec…
MattyLeslie May 14, 2024
72b609a
Merge branch 'dotnet:main' into SignalR--Missing-Accept-Header
MattyLeslie May 14, 2024
d4e3f0c
Adding default timeouts
MattyLeslie May 15, 2024
8df74b9
Sorting using statements and removing Moq
MattyLeslie May 15, 2024
bf623c0
Removing delay, only using secondRequestReceived, adding default time…
MattyLeslie May 15, 2024
f359abf
Merge branch 'SignalR--Missing-Accept-Header' of https://github.com/M…
MattyLeslie May 15, 2024
fe978ae
Adding default time to CompletionSource
MattyLeslie May 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -2,20 +2,25 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO.Pipelines;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Text;
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.AspNetCore.InternalTesting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Testing;
using Moq;
using Moq.Protected;
using Xunit;

namespace Microsoft.AspNetCore.SignalR.Client.Tests;
Expand Down Expand Up @@ -157,4 +162,44 @@ public async Task HttpRequestAndErrorResponseLogged()
Assert.Equal("SendingHttpRequest", writeList[0].EventId.Name);
Assert.Equal("UnsuccessfulHttpResponse", writeList[1].EventId.Name);
}

[Fact]
public async Task NegotiateAsyncAppendsCorrectAcceptHeader()
{
var testHttpHandler = new TestHttpMessageHandler(false);
var negotiateUrlTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

testHttpHandler.OnNegotiate((request, cancellationToken) =>
{
var headerFound = request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("*/*")) == true;
negotiateUrlTcs.SetResult(headerFound);
return ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent());
});

var httpOptions = new HttpConnectionOptions
{
Url = new Uri("http://fakeurl.org/"),
SkipNegotiation = false,
Transports = HttpTransportType.WebSockets,
HttpMessageHandlerFactory = inner => testHttpHandler
};

try
{
await WithConnectionAsync(
CreateConnection(httpOptions),
async (connection) =>
{
await connection.StartAsync().DefaultTimeout();
});
}
catch
{
// ignore connection error
}

Assert.True(negotiateUrlTcs.Task.IsCompleted);
var headerWasFound = await negotiateUrlTcs.Task.DefaultTimeout();
Assert.True(headerWasFound);
}
}
Expand Up @@ -8,13 +8,15 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Connections.Client.Internal;
using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Moq.Protected;
using Xunit;
Expand Down Expand Up @@ -692,4 +694,59 @@ public async Task SendsDeleteRequestWhenTransportCompleted()
Assert.Equal(TestUri, deleteRequest.RequestUri);
}
}

[Fact]
public async Task PollRequestsContainCorrectAcceptHeader()
{
var testHttpHandler = new TestHttpMessageHandler();
var responseTaskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
var requestCount = 0;
var allHeadersCorrect = true;
var secondRequestReceived = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

testHttpHandler.OnRequest(async (request, next, cancellationToken) =>
{
if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("*/*")) != true)
{
allHeadersCorrect = false;
}

requestCount++;

if (requestCount == 2)
{
secondRequestReceived.SetResult();
}

if (requestCount >= 2)
{
if (allHeadersCorrect)
{
responseTaskCompletionSource.TrySetResult(new HttpResponseMessage(HttpStatusCode.OK));
}
else
{
responseTaskCompletionSource.TrySetResult(new HttpResponseMessage(HttpStatusCode.NoContent));
}
}

return await Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
});

using (var httpClient = new HttpClient(testHttpHandler))
{
var loggerFactory = NullLoggerFactory.Instance;
var transport = new LongPollingTransport(httpClient, loggerFactory: loggerFactory);

var startTask = transport.StartAsync(TestUri, TransferFormat.Text);

await secondRequestReceived.Task.DefaultTimeout();

await transport.StopAsync();

Assert.True(responseTaskCompletionSource.Task.IsCompleted);
var response = await responseTaskCompletionSource.Task.DefaultTimeout();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}
@@ -0,0 +1,50 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO.Pipelines;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Microsoft.AspNetCore.Http.Connections.Client.Internal;
using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.AspNetCore.InternalTesting;
MattyLeslie marked this conversation as resolved.
Show resolved Hide resolved

namespace Microsoft.AspNetCore.SignalR.Client.Tests;
public partial class SendUtilsTests : VerifiableLoggedTest
{
[Fact]
public async Task SendMessagesSetsCorrectAcceptHeader()
{
var testHttpHandler = new TestHttpMessageHandler();
var responseTaskCompletionSource = new TaskCompletionSource<HttpResponseMessage>(TaskCreationOptions.RunContinuationsAsynchronously);

testHttpHandler.OnRequest((request, next, cancellationToken) =>
{
if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("*/*")) == true)
{
responseTaskCompletionSource.SetResult(ResponseUtils.CreateResponse(HttpStatusCode.OK));
}
else
{
responseTaskCompletionSource.SetResult(ResponseUtils.CreateResponse(HttpStatusCode.BadRequest));
}
return responseTaskCompletionSource.Task;
});

using (var httpClient = new HttpClient(testHttpHandler))
{
var pipe = new Pipe();
var application = new DuplexPipe(pipe.Reader, pipe.Writer);

// Simulate writing data to send
await application.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
application.Output.Complete();

await SendUtils.SendMessages(new Uri("http://fakeuri.org"), application, httpClient, logger: Logger).DefaultTimeout();

var response = await responseTaskCompletionSource.Task.DefaultTimeout();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}
Expand Up @@ -18,6 +18,7 @@
using Moq;
using Moq.Protected;
using Xunit;
using System.Net;

namespace Microsoft.AspNetCore.SignalR.Client.Tests;

Expand Down Expand Up @@ -409,4 +410,38 @@ public async Task SSETransportThrowsForInvalidTransferFormat(TransferFormat tran
Assert.Equal("transferFormat", exception.ParamName);
}
}

[Fact]
public async Task StartAsyncSetsCorrectAcceptHeaderForSSE()
{
var testHttpHandler = new TestHttpMessageHandler();
var responseTaskCompletionSource = new TaskCompletionSource<HttpResponseMessage>(TaskCreationOptions.RunContinuationsAsynchronously);

// Setting up the handler to check for 'text/event-stream' Accept header
testHttpHandler.OnRequest((request, next, cancellationToken) =>
{
if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("text/event-stream")) == true)
{
responseTaskCompletionSource.SetResult(new HttpResponseMessage(HttpStatusCode.OK));
}
else
{
responseTaskCompletionSource.SetResult(new HttpResponseMessage(HttpStatusCode.NoContent));
}
return responseTaskCompletionSource.Task;
});

using (var httpClient = new HttpClient(testHttpHandler))
{
var sseTransport = new ServerSentEventsTransport(httpClient, loggerFactory: LoggerFactory);

// Starting the SSE transport and verifying the outcome
await sseTransport.StartAsync(new Uri("http://fakeuri.org"), TransferFormat.Text).DefaultTimeout();
await sseTransport.StopAsync().DefaultTimeout();

Assert.True(responseTaskCompletionSource.Task.IsCompleted);
var response = await responseTaskCompletionSource.Task.DefaultTimeout();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}
Expand Up @@ -18,6 +18,7 @@
using Microsoft.AspNetCore.Shared;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System.Net.Http.Headers;

namespace Microsoft.AspNetCore.Http.Connections.Client;

Expand Down Expand Up @@ -469,6 +470,7 @@ private async Task<NegotiationResponse> NegotiateAsync(Uri url, HttpClient httpC
#else
request.Properties.Add("IsNegotiate", true);
#endif
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));

// ResponseHeadersRead instructs SendAsync to return once headers are read
// rather than buffer the entire response. This gives a small perf boost.
Expand Down
Expand Up @@ -11,6 +11,7 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System.Net.Http.Headers;

namespace Microsoft.AspNetCore.Http.Connections.Client.Internal;

Expand Down Expand Up @@ -51,6 +52,8 @@ public async Task StartAsync(Uri url, TransferFormat transferFormat, Cancellatio
// Make initial long polling request
// Server uses first long polling request to finish initializing connection and it returns without data
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));

using (var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
Expand Down Expand Up @@ -149,7 +152,7 @@ private async Task Poll(Uri pollUrl, CancellationToken cancellationToken)
while (!cancellationToken.IsCancellationRequested)
{
var request = new HttpRequestMessage(HttpMethod.Get, pollUrl);

request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
HttpResponseMessage response;

try
Expand Down Expand Up @@ -228,6 +231,7 @@ private async Task SendDeleteRequest(Uri url)
{
Log.SendingDeleteRequest(_logger, url);
var request = new HttpRequestMessage(HttpMethod.Delete, url);

var response = await _httpClient.SendAsync(request).ConfigureAwait(false);

if (response.StatusCode == HttpStatusCode.NotFound)
Expand Down
Expand Up @@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using System.Net.Http.Headers;

namespace Microsoft.AspNetCore.Http.Connections.Client.Internal;

Expand Down Expand Up @@ -40,7 +41,7 @@ public static async Task SendMessages(Uri sendUrl, IDuplexPipe application, Http

// Send them in a single post
var request = new HttpRequestMessage(HttpMethod.Post, sendUrl);

request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
request.Content = new ReadOnlySequenceContent(buffer);

// ResponseHeadersRead instructs SendAsync to return once headers are read
Expand Down