Skip to content

Commit 5fcde60

Browse files
authored
Adding accept header to callsites within SignalR (#55271)
1 parent 9203f25 commit 5fcde60

File tree

7 files changed

+196
-2
lines changed

7 files changed

+196
-2
lines changed

src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.IO.Pipelines;
56
using System.Linq;
67
using System.Net;
78
using System.Net.Http;
9+
using System.Net.Http.Headers;
810
using System.Security.Cryptography.X509Certificates;
11+
using System.Text;
912
using System.Threading.Tasks;
1013
using Microsoft.AspNetCore.Connections;
1114
using Microsoft.AspNetCore.Http.Connections;
1215
using Microsoft.AspNetCore.Http.Connections.Client;
16+
using Microsoft.AspNetCore.Http.Connections.Client.Internal;
1317
using Microsoft.AspNetCore.SignalR.Tests;
1418
using Microsoft.AspNetCore.InternalTesting;
1519
using Microsoft.Extensions.Logging;
1620
using Microsoft.Extensions.Logging.Abstractions;
1721
using Microsoft.Extensions.Logging.Testing;
1822
using Moq;
23+
using Moq.Protected;
1924
using Xunit;
2025

2126
namespace Microsoft.AspNetCore.SignalR.Client.Tests;
@@ -157,4 +162,44 @@ await WithConnectionAsync(
157162
Assert.Equal("SendingHttpRequest", writeList[0].EventId.Name);
158163
Assert.Equal("UnsuccessfulHttpResponse", writeList[1].EventId.Name);
159164
}
165+
166+
[Fact]
167+
public async Task NegotiateAsyncAppendsCorrectAcceptHeader()
168+
{
169+
var testHttpHandler = new TestHttpMessageHandler(false);
170+
var negotiateUrlTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
171+
172+
testHttpHandler.OnNegotiate((request, cancellationToken) =>
173+
{
174+
var headerFound = request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("*/*")) == true;
175+
negotiateUrlTcs.SetResult(headerFound);
176+
return ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent());
177+
});
178+
179+
var httpOptions = new HttpConnectionOptions
180+
{
181+
Url = new Uri("http://fakeurl.org/"),
182+
SkipNegotiation = false,
183+
Transports = HttpTransportType.WebSockets,
184+
HttpMessageHandlerFactory = inner => testHttpHandler
185+
};
186+
187+
try
188+
{
189+
await WithConnectionAsync(
190+
CreateConnection(httpOptions),
191+
async (connection) =>
192+
{
193+
await connection.StartAsync().DefaultTimeout();
194+
});
195+
}
196+
catch
197+
{
198+
// ignore connection error
199+
}
200+
201+
Assert.True(negotiateUrlTcs.Task.IsCompleted);
202+
var headerWasFound = await negotiateUrlTcs.Task.DefaultTimeout();
203+
Assert.True(headerWasFound);
204+
}
160205
}

src/SignalR/clients/csharp/Client/test/UnitTests/LongPollingTransportTests.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
using System.Linq;
99
using System.Net;
1010
using System.Net.Http;
11+
using System.Net.Http.Headers;
1112
using System.Text;
1213
using System.Threading;
1314
using System.Threading.Tasks;
1415
using Microsoft.AspNetCore.Connections;
1516
using Microsoft.AspNetCore.Http.Connections.Client.Internal;
1617
using Microsoft.AspNetCore.SignalR.Tests;
1718
using Microsoft.AspNetCore.InternalTesting;
19+
using Microsoft.Extensions.Logging.Abstractions;
1820
using Moq;
1921
using Moq.Protected;
2022
using Xunit;
@@ -692,4 +694,59 @@ public async Task SendsDeleteRequestWhenTransportCompleted()
692694
Assert.Equal(TestUri, deleteRequest.RequestUri);
693695
}
694696
}
697+
698+
[Fact]
699+
public async Task PollRequestsContainCorrectAcceptHeader()
700+
{
701+
var testHttpHandler = new TestHttpMessageHandler();
702+
var responseTaskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
703+
var requestCount = 0;
704+
var allHeadersCorrect = true;
705+
var secondRequestReceived = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
706+
707+
testHttpHandler.OnRequest(async (request, next, cancellationToken) =>
708+
{
709+
if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("*/*")) != true)
710+
{
711+
allHeadersCorrect = false;
712+
}
713+
714+
requestCount++;
715+
716+
if (requestCount == 2)
717+
{
718+
secondRequestReceived.SetResult();
719+
}
720+
721+
if (requestCount >= 2)
722+
{
723+
if (allHeadersCorrect)
724+
{
725+
responseTaskCompletionSource.TrySetResult(new HttpResponseMessage(HttpStatusCode.OK));
726+
}
727+
else
728+
{
729+
responseTaskCompletionSource.TrySetResult(new HttpResponseMessage(HttpStatusCode.NoContent));
730+
}
731+
}
732+
733+
return await Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
734+
});
735+
736+
using (var httpClient = new HttpClient(testHttpHandler))
737+
{
738+
var loggerFactory = NullLoggerFactory.Instance;
739+
var transport = new LongPollingTransport(httpClient, loggerFactory: loggerFactory);
740+
741+
var startTask = transport.StartAsync(TestUri, TransferFormat.Text);
742+
743+
await secondRequestReceived.Task.DefaultTimeout();
744+
745+
await transport.StopAsync();
746+
747+
Assert.True(responseTaskCompletionSource.Task.IsCompleted);
748+
var response = await responseTaskCompletionSource.Task.DefaultTimeout();
749+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
750+
}
751+
}
695752
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.IO.Pipelines;
5+
using System.Net;
6+
using System.Net.Http;
7+
using System.Net.Http.Headers;
8+
using System.Text;
9+
using Microsoft.AspNetCore.Http.Connections.Client.Internal;
10+
using Microsoft.AspNetCore.SignalR.Tests;
11+
using Microsoft.AspNetCore.InternalTesting;
12+
13+
namespace Microsoft.AspNetCore.SignalR.Client.Tests;
14+
public partial class SendUtilsTests : VerifiableLoggedTest
15+
{
16+
[Fact]
17+
public async Task SendMessagesSetsCorrectAcceptHeader()
18+
{
19+
var testHttpHandler = new TestHttpMessageHandler();
20+
var responseTaskCompletionSource = new TaskCompletionSource<HttpResponseMessage>(TaskCreationOptions.RunContinuationsAsynchronously);
21+
22+
testHttpHandler.OnRequest((request, next, cancellationToken) =>
23+
{
24+
if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("*/*")) == true)
25+
{
26+
responseTaskCompletionSource.SetResult(ResponseUtils.CreateResponse(HttpStatusCode.OK));
27+
}
28+
else
29+
{
30+
responseTaskCompletionSource.SetResult(ResponseUtils.CreateResponse(HttpStatusCode.BadRequest));
31+
}
32+
return responseTaskCompletionSource.Task;
33+
});
34+
35+
using (var httpClient = new HttpClient(testHttpHandler))
36+
{
37+
var pipe = new Pipe();
38+
var application = new DuplexPipe(pipe.Reader, pipe.Writer);
39+
40+
// Simulate writing data to send
41+
await application.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
42+
application.Output.Complete();
43+
44+
await SendUtils.SendMessages(new Uri("http://fakeuri.org"), application, httpClient, logger: Logger).DefaultTimeout();
45+
46+
var response = await responseTaskCompletionSource.Task.DefaultTimeout();
47+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
48+
}
49+
}
50+
}

src/SignalR/clients/csharp/Client/test/UnitTests/ServerSentEventsTransportTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using Moq;
1919
using Moq.Protected;
2020
using Xunit;
21+
using System.Net;
2122

2223
namespace Microsoft.AspNetCore.SignalR.Client.Tests;
2324

@@ -409,4 +410,38 @@ public async Task SSETransportThrowsForInvalidTransferFormat(TransferFormat tran
409410
Assert.Equal("transferFormat", exception.ParamName);
410411
}
411412
}
413+
414+
[Fact]
415+
public async Task StartAsyncSetsCorrectAcceptHeaderForSSE()
416+
{
417+
var testHttpHandler = new TestHttpMessageHandler();
418+
var responseTaskCompletionSource = new TaskCompletionSource<HttpResponseMessage>(TaskCreationOptions.RunContinuationsAsynchronously);
419+
420+
// Setting up the handler to check for 'text/event-stream' Accept header
421+
testHttpHandler.OnRequest((request, next, cancellationToken) =>
422+
{
423+
if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("text/event-stream")) == true)
424+
{
425+
responseTaskCompletionSource.SetResult(new HttpResponseMessage(HttpStatusCode.OK));
426+
}
427+
else
428+
{
429+
responseTaskCompletionSource.SetResult(new HttpResponseMessage(HttpStatusCode.NoContent));
430+
}
431+
return responseTaskCompletionSource.Task;
432+
});
433+
434+
using (var httpClient = new HttpClient(testHttpHandler))
435+
{
436+
var sseTransport = new ServerSentEventsTransport(httpClient, loggerFactory: LoggerFactory);
437+
438+
// Starting the SSE transport and verifying the outcome
439+
await sseTransport.StartAsync(new Uri("http://fakeuri.org"), TransferFormat.Text).DefaultTimeout();
440+
await sseTransport.StopAsync().DefaultTimeout();
441+
442+
Assert.True(responseTaskCompletionSource.Task.IsCompleted);
443+
var response = await responseTaskCompletionSource.Task.DefaultTimeout();
444+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
445+
}
446+
}
412447
}

src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using Microsoft.AspNetCore.Shared;
1919
using Microsoft.Extensions.Logging;
2020
using Microsoft.Extensions.Logging.Abstractions;
21+
using System.Net.Http.Headers;
2122

2223
namespace Microsoft.AspNetCore.Http.Connections.Client;
2324

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

473475
// ResponseHeadersRead instructs SendAsync to return once headers are read
474476
// rather than buffer the entire response. This gives a small perf boost.

src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LongPollingTransport.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.AspNetCore.Connections;
1212
using Microsoft.Extensions.Logging;
1313
using Microsoft.Extensions.Logging.Abstractions;
14+
using System.Net.Http.Headers;
1415

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

@@ -51,6 +52,8 @@ public async Task StartAsync(Uri url, TransferFormat transferFormat, Cancellatio
5152
// Make initial long polling request
5253
// Server uses first long polling request to finish initializing connection and it returns without data
5354
var request = new HttpRequestMessage(HttpMethod.Get, url);
55+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
56+
5457
using (var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false))
5558
{
5659
response.EnsureSuccessStatusCode();
@@ -149,7 +152,7 @@ private async Task Poll(Uri pollUrl, CancellationToken cancellationToken)
149152
while (!cancellationToken.IsCancellationRequested)
150153
{
151154
var request = new HttpRequestMessage(HttpMethod.Get, pollUrl);
152-
155+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
153156
HttpResponseMessage response;
154157

155158
try
@@ -228,6 +231,7 @@ private async Task SendDeleteRequest(Uri url)
228231
{
229232
Log.SendingDeleteRequest(_logger, url);
230233
var request = new HttpRequestMessage(HttpMethod.Delete, url);
234+
231235
var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
232236

233237
if (response.StatusCode == HttpStatusCode.NotFound)

src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Threading;
1111
using System.Threading.Tasks;
1212
using Microsoft.Extensions.Logging;
13+
using System.Net.Http.Headers;
1314

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

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

4142
// Send them in a single post
4243
var request = new HttpRequestMessage(HttpMethod.Post, sendUrl);
43-
44+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
4445
request.Content = new ReadOnlySequenceContent(buffer);
4546

4647
// ResponseHeadersRead instructs SendAsync to return once headers are read

0 commit comments

Comments
 (0)