Implement Begin/End Read/Write methods in LoggingStream

- This allows the reads and writes from SslStream to be logged on desktop .NET
This commit is contained in:
Stephen Halter 2016-10-21 16:22:13 -07:00
parent 0d559468f1
commit 194059a198
7 changed files with 137 additions and 14 deletions

View File

@ -1,6 +1,7 @@
// 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;
using System.IO;
using System.Text;
using System.Threading;
@ -125,5 +126,84 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Filter.Internal
_logger.LogDebug(builder.ToString());
}
#if NET451
// The below APM methods call the underlying Read/WriteAsync methods which will still be logged.
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
var task = ReadAsync(buffer, offset, count, default(CancellationToken), state);
if (callback != null)
{
task.ContinueWith(t => callback.Invoke(t));
}
return task;
}
public override int EndRead(IAsyncResult asyncResult)
{
return ((Task<int>)asyncResult).GetAwaiter().GetResult();
}
private Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state)
{
var tcs = new TaskCompletionSource<int>(state);
var task = ReadAsync(buffer, offset, count, cancellationToken);
task.ContinueWith((task2, state2) =>
{
var tcs2 = (TaskCompletionSource<int>)state2;
if (task2.IsCanceled)
{
tcs2.SetCanceled();
}
else if (task2.IsFaulted)
{
tcs2.SetException(task2.Exception);
}
else
{
tcs2.SetResult(task2.Result);
}
}, tcs, cancellationToken);
return tcs.Task;
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
var task = WriteAsync(buffer, offset, count, default(CancellationToken), state);
if (callback != null)
{
task.ContinueWith(t => callback.Invoke(t));
}
return task;
}
public override void EndWrite(IAsyncResult asyncResult)
{
((Task<object>)asyncResult).GetAwaiter().GetResult();
}
private Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state)
{
var tcs = new TaskCompletionSource<object>(state);
var task = WriteAsync(buffer, offset, count, cancellationToken);
task.ContinueWith((task2, state2) =>
{
var tcs2 = (TaskCompletionSource<object>)state2;
if (task2.IsCanceled)
{
tcs2.SetCanceled();
}
else if (task2.IsFaulted)
{
tcs2.SetException(task2.Exception);
}
else
{
tcs2.SetResult(null);
}
}, tcs, cancellationToken);
return tcs.Task;
}
#endif
}
}

View File

@ -0,0 +1,47 @@
// 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;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
public class LoggingConnectionFilterTests
{
[Fact]
public async Task LoggingConnectionFilterCanBeAddedBeforeAndAfterHttpsFilter()
{
var host = new WebHostBuilder()
.UseUrls($"https://127.0.0.1:0")
.UseKestrel(options =>
{
options.UseConnectionLogging();
options.UseHttps(@"TestResources/testCert.pfx", "testPassword");
})
.Configure(app =>
{
app.Run(context =>
{
context.Response.ContentLength = 12;
return context.Response.WriteAsync("Hello World!");
});
})
.Build();
using (host)
{
host.Start();
var response = await HttpClientSlim.GetStringAsync($"https://localhost:{host.GetPort()}/", validateCertificate: false)
.TimeoutAfter(TimeSpan.FromSeconds(10));
Assert.Equal("Hello World!", response);
}
}
}
}

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Internal;
using Xunit;

View File

@ -6,6 +6,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers;
using Microsoft.AspNetCore.Testing;
using Xunit;

View File

@ -88,19 +88,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{
using (var connection = server.CreateConnection())
{
try
{
await connection.SendEnd(
"POST / HTTP/1.0",
"Content-Length: 12",
"",
"Hello World?");
}
catch (IOException)
{
// Will throw because the exception in the connection filter will close the connection.
Assert.True(true);
}
// Will throw because the exception in the connection filter will close the connection.
await Assert.ThrowsAsync<IOException>(async () => await connection.SendEnd(
"POST / HTTP/1.0",
"Content-Length: 12",
"",
"Hello World?"));
}
}
}

View File

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Internal;
using Xunit;

View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Filter.Internal;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Server.KestrelTests
namespace Microsoft.AspNetCore.Server.KestrelTests.TestHelpers
{
public class PassThroughConnectionFilter : IConnectionFilter