Set X-Requested-With on all requests (#1848)

This commit is contained in:
BrennanConroy 2018-04-04 17:12:50 -07:00 committed by GitHub
parent 80f87e7730
commit 32b4d5cc6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 179 additions and 2 deletions

View File

@ -428,6 +428,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Client
}
}
httpClient.DefaultRequestHeaders.Remove("X-Requested-With");
// Tell auth middleware to 401 instead of redirecting
httpClient.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest");
return httpClient;
}

View File

@ -82,6 +82,8 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
_closeTimeout = httpOptions.CloseTimeout;
}
_webSocket.Options.SetRequestHeader("X-Requested-With", "XMLHttpRequest");
_logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<WebSocketsTransport>();
}

View File

@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Http.Connections.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -64,7 +63,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
o.Url = url;
o.Transport = transportType;
});
if (configureHttpConnection != null)
{
hubConnectionBuilder.Services.Configure(configureHttpConnection);

View File

@ -66,6 +66,46 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
Assert.True(requestsExecuted);
}
[Theory]
[InlineData(HttpTransportType.LongPolling)]
[InlineData(HttpTransportType.ServerSentEvents)]
public async Task HttpConnectionSetsRequestedWithOnAllRequests(HttpTransportType transportType)
{
var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false);
var requestsExecuted = false;
testHttpHandler.OnRequest((request, next, token) =>
{
return Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
});
testHttpHandler.OnNegotiate((_, cancellationToken) =>
{
return ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent());
});
testHttpHandler.OnRequest(async (request, next, token) =>
{
var requestedWithHeader = request.Headers.GetValues("X-Requested-With");
var requestedWithValue = Assert.Single(requestedWithHeader);
Assert.Equal("XMLHttpRequest", requestedWithValue);
requestsExecuted = true;
return await next();
});
await WithConnectionAsync(
CreateConnection(testHttpHandler, transportType: transportType),
async (connection) =>
{
await connection.StartAsync(TransferFormat.Text).OrTimeout();
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
});
// Fail safe in case the code is modified and some requests don't execute as a result
Assert.True(requestsExecuted);
}
[Fact]
public async Task CanReceiveData()
{

View File

@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Buffers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Connections;
namespace Microsoft.AspNetCore.SignalR.Tests
{
[Authorize]
public class AuthConnectionHandler : ConnectionHandler
{
public override async Task OnConnectedAsync(ConnectionContext connection)
{
while (true)
{
var result = await connection.Transport.Input.ReadAsync();
var buffer = result.Buffer;
if (!buffer.IsEmpty)
{
await connection.Transport.Output.WriteAsync(buffer.ToArray());
}
else if (result.IsCompleted)
{
break;
}
connection.Transport.Input.AdvanceTo(buffer.End);
}
}
}
}

View File

@ -302,6 +302,69 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
[ConditionalFact]
[WebSocketsSupportedCondition]
public async Task UnauthorizedWebSocketsConnectionDoesNotConnect()
{
using (StartLog(out var loggerFactory, LogLevel.Trace))
{
var logger = loggerFactory.CreateLogger<EndToEndTests>();
var url = _serverFixture.Url + "/auth";
var connection = new HttpConnection(new Uri(url), HttpTransportType.WebSockets, loggerFactory);
try
{
logger.LogInformation("Starting connection to {url}", url);
await connection.StartAsync(TransferFormat.Binary).OrTimeout();
Assert.True(false);
}
catch (WebSocketException) { }
catch (Exception ex)
{
logger.LogInformation(ex, "Test threw exception");
throw;
}
finally
{
logger.LogInformation("Disposing Connection");
await connection.DisposeAsync().OrTimeout();
logger.LogInformation("Disposed Connection");
}
}
}
[Theory]
[InlineData(HttpTransportType.LongPolling)]
[InlineData(HttpTransportType.ServerSentEvents)]
public async Task UnauthorizedConnectionDoesNotConnect(HttpTransportType transportType)
{
using (StartLog(out var loggerFactory, LogLevel.Trace, testName: $"{nameof(UnauthorizedConnectionDoesNotConnect)}_{transportType}"))
{
var logger = loggerFactory.CreateLogger<EndToEndTests>();
var url = _serverFixture.Url + "/auth";
var connection = new HttpConnection(new Uri(url), transportType, loggerFactory);
try
{
logger.LogInformation("Starting connection to {url}", url);
await connection.StartAsync(TransferFormat.Binary).OrTimeout();
Assert.True(false);
}
catch (Exception ex)
{
Assert.Equal("Response status code does not indicate success: 401 (Unauthorized).", ex.Message);
}
finally
{
logger.LogInformation("Disposing Connection");
await connection.DisposeAsync().OrTimeout();
logger.LogInformation("Disposed Connection");
}
}
}
[ConditionalFact]
[WebSocketsSupportedCondition]
public async Task ServerClosesConnectionWithErrorIfHubCannotBeCreated_WebSocket()

View File

@ -34,6 +34,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(MicrosoftAspNetCoreHttpAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(MicrosoftAspNetCoreServerIntegrationTestingPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="$(MicrosoftAspNetCoreAuthorizationPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="$(MicrosoftAspNetCoreAuthenticationCookiesPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />

View File

@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
@ -16,6 +18,12 @@ namespace Microsoft.AspNetCore.SignalR.Tests
{
options.EnableDetailedErrors = true;
});
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
@ -25,6 +33,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
routes.MapConnectionHandler<EchoConnectionHandler>("/echo");
routes.MapConnectionHandler<WriteThenCloseConnectionHandler>("/echoAndClose");
routes.MapConnectionHandler<HttpHeaderConnectionHandler>("/httpheader");
routes.MapConnectionHandler<AuthConnectionHandler>("/auth");
});
app.UseSignalR(routes =>

View File

@ -107,6 +107,30 @@ namespace Microsoft.AspNetCore.SignalR.Tests
}
}
[ConditionalFact]
[WebSocketsSupportedCondition]
public async Task WebSocketsTransportSendsXRequestedWithHeader()
{
using (StartLog(out var loggerFactory))
{
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var webSocketsTransport = new WebSocketsTransport(httpOptions: null, loggerFactory: loggerFactory);
await webSocketsTransport.StartAsync(new Uri(_serverFixture.WebSocketsUrl + "/httpheader"), pair.Application,
TransferFormat.Binary, connection: Mock.Of<IConnection>()).OrTimeout();
await pair.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("X-Requested-With"));
// The HTTP header endpoint closes the connection immediately after sending response which should stop the transport
await webSocketsTransport.Running.OrTimeout();
Assert.True(pair.Transport.Input.TryRead(out var result));
string headerValue = Encoding.UTF8.GetString(result.Buffer.ToArray());
Assert.Equal("XMLHttpRequest", headerValue);
}
}
[ConditionalFact]
[WebSocketsSupportedCondition]
public async Task WebSocketsTransportStopsWhenConnectionChannelClosed()