Transport agnostic kestrel refactoring (#1551)
- Add transport interfaces - Create separate Core and Libuv projects #828
This commit is contained in:
parent
792b71dbcb
commit
7f785588ef
|
|
@ -1,6 +1,6 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26228.4
|
||||
VisualStudioVersion = 15.0.26228.9
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
|
|
@ -40,7 +40,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC
|
|||
test\shared\TestServiceContext.cs = test\shared\TestServiceContext.cs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel", "src\Microsoft.AspNetCore.Server.Kestrel\Microsoft.AspNetCore.Server.Kestrel.csproj", "{F510611A-3BEE-4B88-A613-5F4A74ED82A1}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Core", "src\Microsoft.AspNetCore.Server.Kestrel.Core\Microsoft.AspNetCore.Server.Kestrel.Core.csproj", "{F510611A-3BEE-4B88-A613-5F4A74ED82A1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.KestrelTests", "test\Microsoft.AspNetCore.Server.KestrelTests\Microsoft.AspNetCore.Server.KestrelTests.csproj", "{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}"
|
||||
EndProject
|
||||
|
|
@ -61,6 +61,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestResources", "TestResour
|
|||
test\shared\TestResources\testCert.pfx = test\shared\TestResources\testCert.pfx
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv", "src\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj", "{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel", "src\Microsoft.AspNetCore.Server.Kestrel\Microsoft.AspNetCore.Server.Kestrel.csproj", "{56139957-5C29-4E7D-89BD-7D20598B4EAF}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -167,6 +171,30 @@ Global
|
|||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -182,5 +210,7 @@ Global
|
|||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
|
||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
|
||||
{2822C132-BFFB-4D53-AC5B-E7E47DD81A6E} = {0EF2ACDF-012F-4472-A13A-4272419E2903}
|
||||
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4} = {2D5D5227-4DBD-499A-96B1-76A36B03B750}
|
||||
{56139957-5C29-4E7D-89BD-7D20598B4EAF} = {2D5D5227-4DBD-499A-96B1-76A36B03B750}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.Kestrel\Microsoft.AspNetCore.Server.Kestrel.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.Kestrel.Https\Microsoft.AspNetCore.Server.Kestrel.Https.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -61,9 +61,11 @@ namespace SampleApp
|
|||
|
||||
// The following section should be used to demo sockets
|
||||
//options.ListenUnixSocket("/tmp/kestrel-test.sock");
|
||||
|
||||
})
|
||||
.UseLibuv(options =>
|
||||
{
|
||||
// Uncomment the following line to change the default number of libuv threads for all endpoints.
|
||||
//options.ThreadCount = 4;
|
||||
options.ThreadCount = 4;
|
||||
})
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStartup<Startup>()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// 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.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Adapter
|
||||
|
|
@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
|||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Adapter.Internal
|
||||
{
|
||||
public class AdaptedPipeline : IDisposable
|
||||
public class AdaptedPipeline
|
||||
{
|
||||
private const int MinAllocBufferSize = 2048;
|
||||
|
||||
|
|
@ -31,30 +31,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Adapter.Internal
|
|||
|
||||
public ISocketOutput Output => _output;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Input.Writer.Complete();
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
public async Task RunAsync()
|
||||
{
|
||||
var inputTask = ReadInputAsync();
|
||||
var outputTask = _output.WriteOutputAsync();
|
||||
|
||||
var result = await Task.WhenAny(inputTask, outputTask);
|
||||
await inputTask;
|
||||
|
||||
if (result == inputTask)
|
||||
{
|
||||
// Close output
|
||||
_output.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close input
|
||||
Input.Writer.Complete();
|
||||
}
|
||||
_output.Dispose();
|
||||
|
||||
await Task.WhenAll(inputTask, outputTask);
|
||||
await outputTask;
|
||||
}
|
||||
|
||||
private async Task ReadInputAsync()
|
||||
|
|
@ -7,7 +7,6 @@ using System.IO.Pipelines;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Adapter.Internal
|
||||
{
|
||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Adapter.Internal
|
|||
|
||||
private readonly Stream _outputStream;
|
||||
private readonly IPipe _pipe;
|
||||
private object _sync = new object();
|
||||
private readonly object _sync = new object();
|
||||
private bool _completed;
|
||||
|
||||
public StreamSocketOutput(Stream outputStream, IPipe pipe)
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// 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.Pipelines;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal
|
||||
{
|
||||
public class ConnectionHandler<TContext> : IConnectionHandler
|
||||
{
|
||||
// Base32 encoding - in ascii sort order for easy text based sorting
|
||||
private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
||||
|
||||
// Seed the _lastConnectionId for this application instance with
|
||||
// the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001
|
||||
// for a roughly increasing _requestId over restarts
|
||||
private static long _lastConnectionId = DateTime.UtcNow.Ticks;
|
||||
|
||||
private readonly ServiceContext _serviceContext;
|
||||
private readonly IHttpApplication<TContext> _application;
|
||||
|
||||
public ConnectionHandler(ServiceContext serviceContext, IHttpApplication<TContext> application)
|
||||
{
|
||||
_serviceContext = serviceContext;
|
||||
_application = application;
|
||||
}
|
||||
|
||||
public IConnectionContext OnConnection(IConnectionInformation connectionInfo)
|
||||
{
|
||||
var inputPipe = connectionInfo.PipeFactory.Create(GetInputPipeOptions(connectionInfo.InputWriterScheduler));
|
||||
var outputPipe = connectionInfo.PipeFactory.Create(GetOutputPipeOptions(connectionInfo.OutputWriterScheduler));
|
||||
|
||||
var connectionId = GenerateConnectionId(Interlocked.Increment(ref _lastConnectionId));
|
||||
|
||||
var frameContext = new FrameContext
|
||||
{
|
||||
ConnectionId = connectionId,
|
||||
ConnectionInformation = connectionInfo,
|
||||
ServiceContext = _serviceContext
|
||||
};
|
||||
|
||||
// TODO: Untangle this mess
|
||||
var frame = new Frame<TContext>(_application, frameContext);
|
||||
var outputProducer = new SocketOutputProducer(outputPipe.Writer, frame, connectionId, _serviceContext.Log);
|
||||
frame.LifetimeControl = new ConnectionLifetimeControl(connectionId, outputPipe.Reader, outputProducer, _serviceContext.Log);
|
||||
|
||||
var connection = new FrameConnection(new FrameConnectionContext
|
||||
{
|
||||
ConnectionId = connectionId,
|
||||
ServiceContext = _serviceContext,
|
||||
PipeFactory = connectionInfo.PipeFactory,
|
||||
ConnectionAdapters = connectionInfo.ListenOptions.ConnectionAdapters,
|
||||
Frame = frame,
|
||||
Input = inputPipe,
|
||||
Output = outputPipe,
|
||||
OutputProducer = outputProducer
|
||||
});
|
||||
|
||||
// Since data cannot be added to the inputPipe by the transport until OnConnection returns,
|
||||
// Frame.RequestProcessingAsync is guaranteed to unblock the transport thread before calling
|
||||
// application code.
|
||||
connection.StartRequestProcessing();
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal PipeOptions GetInputPipeOptions(IScheduler writerScheduler) => new PipeOptions
|
||||
{
|
||||
ReaderScheduler = _serviceContext.ThreadPool,
|
||||
WriterScheduler = writerScheduler,
|
||||
MaximumSizeHigh = _serviceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0,
|
||||
MaximumSizeLow = _serviceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0
|
||||
};
|
||||
|
||||
internal PipeOptions GetOutputPipeOptions(IScheduler readerScheduler) => new PipeOptions
|
||||
{
|
||||
ReaderScheduler = readerScheduler,
|
||||
WriterScheduler = _serviceContext.ThreadPool,
|
||||
MaximumSizeHigh = GetOutputResponseBufferSize(),
|
||||
MaximumSizeLow = GetOutputResponseBufferSize()
|
||||
};
|
||||
|
||||
private long GetOutputResponseBufferSize()
|
||||
{
|
||||
var bufferSize = _serviceContext.ServerOptions.Limits.MaxResponseBufferSize;
|
||||
if (bufferSize == 0)
|
||||
{
|
||||
// 0 = no buffering so we need to configure the pipe so the the writer waits on the reader directly
|
||||
return 1;
|
||||
}
|
||||
|
||||
// null means that we have no back pressure
|
||||
return bufferSize ?? 0;
|
||||
}
|
||||
|
||||
private static unsafe string GenerateConnectionId(long id)
|
||||
{
|
||||
// The following routine is ~310% faster than calling long.ToString() on x64
|
||||
// and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations
|
||||
// See: https://github.com/aspnet/Hosting/pull/385
|
||||
|
||||
// stackalloc to allocate array on stack rather than heap
|
||||
char* charBuffer = stackalloc char[13];
|
||||
|
||||
charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31];
|
||||
charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31];
|
||||
charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31];
|
||||
charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31];
|
||||
charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31];
|
||||
charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31];
|
||||
charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31];
|
||||
charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31];
|
||||
charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31];
|
||||
charBuffer[9] = _encode32Chars[(int)(id >> 15) & 31];
|
||||
charBuffer[10] = _encode32Chars[(int)(id >> 10) & 31];
|
||||
charBuffer[11] = _encode32Chars[(int)(id >> 5) & 31];
|
||||
charBuffer[12] = _encode32Chars[(int)id & 31];
|
||||
|
||||
// string ctor overload that takes char*
|
||||
return new string(charBuffer, 0, 13);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Adapter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal
|
||||
{
|
||||
public class FrameConnection : IConnectionContext
|
||||
{
|
||||
private readonly FrameConnectionContext _context;
|
||||
private readonly Frame _frame;
|
||||
private readonly List<IConnectionAdapter> _connectionAdapters;
|
||||
private readonly TaskCompletionSource<object> _frameStartedTcs = new TaskCompletionSource<object>();
|
||||
|
||||
private AdaptedPipeline _adaptedPipeline;
|
||||
private Stream _filteredStream;
|
||||
private Task _adaptedPipelineTask = TaskCache.CompletedTask;
|
||||
|
||||
public FrameConnection(FrameConnectionContext context)
|
||||
{
|
||||
_context = context;
|
||||
_frame = context.Frame;
|
||||
_connectionAdapters = context.ConnectionAdapters;
|
||||
}
|
||||
|
||||
public string ConnectionId => _context.ConnectionId;
|
||||
public IPipeWriter Input => _context.Input.Writer;
|
||||
public IPipeReader Output => _context.Output.Reader;
|
||||
|
||||
private PipeFactory PipeFactory => _context.PipeFactory;
|
||||
|
||||
// Internal for testing
|
||||
internal PipeOptions AdaptedPipeOptions => new PipeOptions
|
||||
{
|
||||
ReaderScheduler = InlineScheduler.Default,
|
||||
WriterScheduler = InlineScheduler.Default,
|
||||
MaximumSizeHigh = _context.ServiceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0,
|
||||
MaximumSizeLow = _context.ServiceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0
|
||||
};
|
||||
|
||||
private IKestrelTrace Log => _context.ServiceContext.Log;
|
||||
|
||||
public void StartRequestProcessing()
|
||||
{
|
||||
_frame.Input = _context.Input.Reader;
|
||||
_frame.Output = _context.OutputProducer;
|
||||
|
||||
if (_connectionAdapters.Count == 0)
|
||||
{
|
||||
_frame.Start();
|
||||
_frameStartedTcs.SetResult(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure that IConnectionAdapter.OnConnectionAsync does not run on the transport thread.
|
||||
_context.ServiceContext.ThreadPool.UnsafeRun(state =>
|
||||
{
|
||||
// ApplyConnectionAdaptersAsync should never throw. If it succeeds, it will call _frame.Start().
|
||||
// Otherwise, it will close the connection.
|
||||
var ignore = ((FrameConnection)state).ApplyConnectionAdaptersAsync();
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
await _frameStartedTcs.Task;
|
||||
await _frame.StopAsync();
|
||||
await _adaptedPipelineTask;
|
||||
}
|
||||
|
||||
public void Abort(Exception ex)
|
||||
{
|
||||
_frame.Abort(ex);
|
||||
}
|
||||
|
||||
public void Timeout()
|
||||
{
|
||||
_frame.SetBadRequestState(RequestRejectionReason.RequestTimeout);
|
||||
}
|
||||
|
||||
private async Task ApplyConnectionAdaptersAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var rawSocketOutput = _frame.Output;
|
||||
var rawStream = new RawStream(_frame.Input, rawSocketOutput);
|
||||
var adapterContext = new ConnectionAdapterContext(rawStream);
|
||||
var adaptedConnections = new IAdaptedConnection[_connectionAdapters.Count];
|
||||
|
||||
for (var i = 0; i < _connectionAdapters.Count; i++)
|
||||
{
|
||||
var adaptedConnection = await _connectionAdapters[i].OnConnectionAsync(adapterContext);
|
||||
adaptedConnections[i] = adaptedConnection;
|
||||
adapterContext = new ConnectionAdapterContext(adaptedConnection.ConnectionStream);
|
||||
}
|
||||
|
||||
if (adapterContext.ConnectionStream != rawStream)
|
||||
{
|
||||
_filteredStream = adapterContext.ConnectionStream;
|
||||
_adaptedPipeline = new AdaptedPipeline(
|
||||
adapterContext.ConnectionStream,
|
||||
PipeFactory.Create(AdaptedPipeOptions),
|
||||
PipeFactory.Create(AdaptedPipeOptions));
|
||||
|
||||
_frame.Input = _adaptedPipeline.Input.Reader;
|
||||
_frame.Output = _adaptedPipeline.Output;
|
||||
|
||||
_adaptedPipelineTask = RunAdaptedPipeline();
|
||||
}
|
||||
|
||||
_frame.AdaptedConnections = adaptedConnections;
|
||||
_frame.Start();
|
||||
_frameStartedTcs.SetResult(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogError(0, ex, $"Uncaught exception from the {nameof(IConnectionAdapter.OnConnectionAsync)} method of an {nameof(IConnectionAdapter)}.");
|
||||
_frameStartedTcs.SetResult(null);
|
||||
CloseRawPipes();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunAdaptedPipeline()
|
||||
{
|
||||
try
|
||||
{
|
||||
await _adaptedPipeline.RunAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// adaptedPipeline.RunAsync() shouldn't throw.
|
||||
Log.LogError(0, ex, $"{nameof(FrameConnection)}.{nameof(ApplyConnectionAdaptersAsync)}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseRawPipes();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseRawPipes()
|
||||
{
|
||||
_filteredStream?.Dispose();
|
||||
_context.OutputProducer.Dispose();
|
||||
_context.Input.Reader.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.IO.Pipelines;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Adapter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal
|
||||
{
|
||||
public class FrameConnectionContext
|
||||
{
|
||||
public string ConnectionId { get; set; }
|
||||
public ServiceContext ServiceContext { get; set; }
|
||||
public PipeFactory PipeFactory { get; set; }
|
||||
public List<IConnectionAdapter> ConnectionAdapters { get; set; }
|
||||
public Frame Frame { get; set; }
|
||||
public SocketOutputProducer OutputProducer { get; set; }
|
||||
|
||||
public IPipe Input { get; set; }
|
||||
public IPipe Output { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.IO.Pipelines;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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.IO.Pipelines;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
public class ConnectionLifetimeControl
|
||||
{
|
||||
public ConnectionLifetimeControl(
|
||||
string connectionId,
|
||||
IPipeReader outputPipeReader,
|
||||
SocketOutputProducer outputProducer,
|
||||
IKestrelTrace log)
|
||||
{
|
||||
ConnectionId = connectionId;
|
||||
OutputReader = outputPipeReader;
|
||||
OutputProducer = outputProducer;
|
||||
Log = log;
|
||||
}
|
||||
|
||||
private string ConnectionId { get; }
|
||||
private IPipeReader OutputReader { get; }
|
||||
private SocketOutputProducer OutputProducer { get; }
|
||||
private IKestrelTrace Log { get; }
|
||||
|
||||
public void End(ProduceEndType endType)
|
||||
{
|
||||
switch (endType)
|
||||
{
|
||||
case ProduceEndType.ConnectionKeepAlive:
|
||||
Log.ConnectionKeepAlive(ConnectionId);
|
||||
break;
|
||||
case ProduceEndType.SocketShutdown:
|
||||
OutputReader.CancelPendingRead();
|
||||
goto case ProduceEndType.SocketDisconnect;
|
||||
case ProduceEndType.SocketDisconnect:
|
||||
OutputProducer.Dispose();
|
||||
Log.ConnectionDisconnect(ConnectionId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
|
|
@ -159,7 +158,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
}
|
||||
|
||||
// No requests since the last timer tick, we need to check if we're beyond the idle threshold
|
||||
if ((now.Ticks - PlatformApis.VolatileRead(ref _lastRequestSeenTicks)) >= _timeWithoutRequestsUntilIdle.Ticks)
|
||||
// TODO: Use PlatformApis.VolatileRead equivalent again
|
||||
if ((now.Ticks - Interlocked.Read(ref _lastRequestSeenTicks)) >= _timeWithoutRequestsUntilIdle.Ticks)
|
||||
{
|
||||
// No requests since idle threshold so stop the timer if it's still running
|
||||
StopTimer();
|
||||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -16,6 +16,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Adapter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
|
@ -52,7 +53,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
protected Stack<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
||||
|
||||
private TaskCompletionSource<object> _frameStartedTcs = new TaskCompletionSource<object>();
|
||||
private Task _requestProcessingTask;
|
||||
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
||||
protected int _requestAborted;
|
||||
|
|
@ -77,37 +77,39 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
protected long _responseBytesWritten;
|
||||
|
||||
private readonly FrameContext _frameContext;
|
||||
private readonly IHttpParser _parser;
|
||||
|
||||
public Frame(ConnectionContext context)
|
||||
public Frame(FrameContext frameContext)
|
||||
{
|
||||
ConnectionContext = context;
|
||||
Input = context.Input;
|
||||
Output = context.Output;
|
||||
_frameContext = frameContext;
|
||||
|
||||
ServerOptions = context.ListenerContext.ServiceContext.ServerOptions;
|
||||
ServerOptions = ServiceContext.ServerOptions;
|
||||
|
||||
_parser = context.ListenerContext.ServiceContext.HttpParserFactory(this);
|
||||
_parser = ServiceContext.HttpParserFactory(this);
|
||||
|
||||
FrameControl = this;
|
||||
_keepAliveMilliseconds = (long)ServerOptions.Limits.KeepAliveTimeout.TotalMilliseconds;
|
||||
_requestHeadersTimeoutMilliseconds = (long)ServerOptions.Limits.RequestHeadersTimeout.TotalMilliseconds;
|
||||
}
|
||||
|
||||
public ConnectionContext ConnectionContext { get; }
|
||||
public IPipe Input { get; set; }
|
||||
public ServiceContext ServiceContext => _frameContext.ServiceContext;
|
||||
public IConnectionInformation ConnectionInformation => _frameContext.ConnectionInformation;
|
||||
|
||||
public IPipeReader Input { get; set; }
|
||||
public ISocketOutput Output { get; set; }
|
||||
public IEnumerable<IAdaptedConnection> AdaptedConnections { get; set; }
|
||||
public ConnectionLifetimeControl LifetimeControl { get; set; }
|
||||
|
||||
protected IConnectionControl ConnectionControl => ConnectionContext.ConnectionControl;
|
||||
protected IKestrelTrace Log => ConnectionContext.ListenerContext.ServiceContext.Log;
|
||||
protected ITimeoutControl TimeoutControl => ConnectionInformation.TimeoutControl;
|
||||
protected IKestrelTrace Log => ServiceContext.Log;
|
||||
|
||||
private DateHeaderValueManager DateHeaderValueManager => ConnectionContext.ListenerContext.ServiceContext.DateHeaderValueManager;
|
||||
private DateHeaderValueManager DateHeaderValueManager => ServiceContext.DateHeaderValueManager;
|
||||
// Hold direct reference to ServerOptions since this is used very often in the request processing path
|
||||
private KestrelServerOptions ServerOptions { get; }
|
||||
private IPEndPoint LocalEndPoint => ConnectionContext.LocalEndPoint;
|
||||
private IPEndPoint RemoteEndPoint => ConnectionContext.RemoteEndPoint;
|
||||
protected string ConnectionId => ConnectionContext.ConnectionId;
|
||||
private IPEndPoint LocalEndPoint => ConnectionInformation.LocalEndPoint;
|
||||
private IPEndPoint RemoteEndPoint => ConnectionInformation.RemoteEndPoint;
|
||||
protected string ConnectionId => _frameContext.ConnectionId;
|
||||
|
||||
public string ConnectionIdFeature { get; set; }
|
||||
public IPAddress RemoteIpAddress { get; set; }
|
||||
|
|
@ -218,8 +220,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
public Stream DuplexStream { get; set; }
|
||||
|
||||
public Task FrameStartedTask => _frameStartedTcs.Task;
|
||||
|
||||
public CancellationToken RequestAborted
|
||||
{
|
||||
get
|
||||
|
|
@ -392,7 +392,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
Reset();
|
||||
_requestProcessingTask = RequestProcessingAsync();
|
||||
_frameStartedTcs.SetResult(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -404,11 +403,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
public Task StopAsync()
|
||||
{
|
||||
_requestProcessingStopping = true;
|
||||
Input.Reader.CancelPendingRead();
|
||||
Input.CancelPendingRead();
|
||||
|
||||
return _requestProcessingTask ?? TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
private void CancelRequestAbortedToken()
|
||||
{
|
||||
try
|
||||
{
|
||||
RequestAbortedSource.Cancel();
|
||||
_abortedCts = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ApplicationError(ConnectionId, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediate kill the connection and poison the request and response streams.
|
||||
/// </summary>
|
||||
|
|
@ -421,24 +433,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
_frameStreams?.RequestBody.Abort(error);
|
||||
_frameStreams?.ResponseBody.Abort();
|
||||
|
||||
try
|
||||
{
|
||||
ConnectionControl.End(ProduceEndType.SocketDisconnect);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogError(0, ex, "Abort");
|
||||
}
|
||||
LifetimeControl.End(ProduceEndType.SocketDisconnect);
|
||||
|
||||
try
|
||||
{
|
||||
RequestAbortedSource.Cancel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogError(0, ex, "Abort");
|
||||
}
|
||||
_abortedCts = null;
|
||||
// Potentially calling user code. CancelRequestAbortedToken logs any exceptions.
|
||||
ServiceContext.ThreadPool.UnsafeRun(state => ((Frame)state).CancelRequestAbortedToken(), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -856,7 +854,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
if (_keepAlive)
|
||||
{
|
||||
ConnectionControl.End(ProduceEndType.ConnectionKeepAlive);
|
||||
LifetimeControl.End(ProduceEndType.ConnectionKeepAlive);
|
||||
}
|
||||
|
||||
if (HttpMethods.IsHead(Method) && _responseBytesWritten > 0)
|
||||
|
|
@ -876,7 +874,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
if (_keepAlive)
|
||||
{
|
||||
ConnectionControl.End(ProduceEndType.ConnectionKeepAlive);
|
||||
LifetimeControl.End(ProduceEndType.ConnectionKeepAlive);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -995,7 +993,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
break;
|
||||
}
|
||||
|
||||
ConnectionControl.ResetTimeout(_requestHeadersTimeoutMilliseconds, TimeoutAction.SendTimeoutResponse);
|
||||
TimeoutControl.ResetTimeout(_requestHeadersTimeoutMilliseconds, TimeoutAction.SendTimeoutResponse);
|
||||
|
||||
_requestProcessingStatus = RequestProcessingStatus.ParsingRequestLine;
|
||||
goto case RequestProcessingStatus.ParsingRequestLine;
|
||||
|
|
@ -1060,7 +1058,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
}
|
||||
if (result)
|
||||
{
|
||||
ConnectionControl.CancelTimeout();
|
||||
TimeoutControl.CancelTimeout();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// 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 Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
public class FrameContext
|
||||
{
|
||||
public string ConnectionId { get; set; }
|
||||
public ServiceContext ServiceContext { get; set; }
|
||||
public IConnectionInformation ConnectionInformation { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,7 @@ using System.IO;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
|
|
@ -16,8 +15,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
private readonly IHttpApplication<TContext> _application;
|
||||
|
||||
public Frame(IHttpApplication<TContext> application, ConnectionContext context)
|
||||
: base(context)
|
||||
public Frame(IHttpApplication<TContext> application, FrameContext frameContext)
|
||||
: base(frameContext)
|
||||
{
|
||||
_application = application;
|
||||
}
|
||||
|
|
@ -34,13 +33,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
while (!_requestProcessingStopping)
|
||||
{
|
||||
ConnectionControl.SetTimeout(_keepAliveMilliseconds, TimeoutAction.CloseConnection);
|
||||
TimeoutControl.SetTimeout(_keepAliveMilliseconds, TimeoutAction.CloseConnection);
|
||||
|
||||
InitializeHeaders();
|
||||
|
||||
while (!_requestProcessingStopping)
|
||||
{
|
||||
var result = await Input.Reader.ReadAsync();
|
||||
var result = await Input.ReadAsync();
|
||||
var examined = result.Buffer.End;
|
||||
var consumed = result.Buffer.End;
|
||||
|
||||
|
|
@ -52,13 +51,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
if (_requestProcessingStatus == RequestProcessingStatus.ParsingHeaders)
|
||||
{
|
||||
throw BadHttpRequestException.GetException(RequestRejectionReason.MalformedRequestInvalidHeaders);
|
||||
throw BadHttpRequestException.GetException(RequestRejectionReason
|
||||
.MalformedRequestInvalidHeaders);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Input.Reader.Advance(consumed, examined);
|
||||
Input.Advance(consumed, examined);
|
||||
}
|
||||
|
||||
if (_requestProcessingStatus == RequestProcessingStatus.AppStarted)
|
||||
|
|
@ -73,9 +73,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
case RequestProcessingStatus.RequestPending:
|
||||
return;
|
||||
case RequestProcessingStatus.ParsingRequestLine:
|
||||
throw BadHttpRequestException.GetException(RequestRejectionReason.InvalidRequestLine);
|
||||
throw BadHttpRequestException.GetException(
|
||||
RequestRejectionReason.InvalidRequestLine);
|
||||
case RequestProcessingStatus.ParsingHeaders:
|
||||
throw BadHttpRequestException.GetException(RequestRejectionReason.MalformedRequestInvalidHeaders);
|
||||
throw BadHttpRequestException.GetException(RequestRejectionReason
|
||||
.MalformedRequestInvalidHeaders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -190,15 +192,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
// SetBadRequestState logs the error.
|
||||
SetBadRequestState(ex);
|
||||
}
|
||||
catch (IOException ex) when (ex.InnerException is UvException)
|
||||
catch (ConnectionResetException ex)
|
||||
{
|
||||
// Don't log ECONNRESET errors made between requests. Browsers like IE will reset connections regularly.
|
||||
if (_requestProcessingStatus != RequestProcessingStatus.RequestPending ||
|
||||
((UvException)ex.InnerException).StatusCode != Constants.ECONNRESET)
|
||||
if (_requestProcessingStatus != RequestProcessingStatus.RequestPending)
|
||||
{
|
||||
Log.RequestProcessingError(ConnectionId, ex);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Log.RequestProcessingError(ConnectionId, ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogWarning(0, ex, "Connection processing ended abnormally");
|
||||
|
|
@ -207,12 +212,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
try
|
||||
{
|
||||
Input.Reader.Complete();
|
||||
Input.Complete();
|
||||
// If _requestAborted is set, the connection has already been closed.
|
||||
if (Volatile.Read(ref _requestAborted) == 0)
|
||||
{
|
||||
await TryProduceInvalidRequestResponse();
|
||||
ConnectionControl.End(ProduceEndType.SocketShutdown);
|
||||
LifetimeControl.End(ProduceEndType.SocketShutdown);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
|
|
@ -6,7 +6,6 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.IO.Pipelines;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
|
|
@ -2,9 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO.Pipelines;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
|
|
@ -3,11 +3,8 @@
|
|||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
public interface IConnectionControl
|
||||
public interface ITimeoutControl
|
||||
{
|
||||
void Pause();
|
||||
void Resume();
|
||||
void End(ProduceEndType endType);
|
||||
void SetTimeout(long milliseconds, TimeoutAction timeoutAction);
|
||||
void ResetTimeout(long milliseconds, TimeoutAction timeoutAction);
|
||||
void CancelTimeout();
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO.Pipelines;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
|
@ -215,9 +215,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
private void ConsumedBytes(int count)
|
||||
{
|
||||
var scan = _context.Input.Reader.ReadAsync().GetResult().Buffer;
|
||||
var scan = _context.Input.ReadAsync().GetResult().Buffer;
|
||||
var consumed = scan.Move(scan.Start, count);
|
||||
_context.Input.Reader.Advance(consumed, consumed);
|
||||
_context.Input.Advance(consumed, consumed);
|
||||
|
||||
OnConsumedBytes(count);
|
||||
}
|
||||
|
|
@ -304,7 +304,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
protected override ValueTask<ArraySegment<byte>> PeekAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return _context.Input.Reader.PeekAsync();
|
||||
return _context.Input.PeekAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +351,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
return new ValueTask<ArraySegment<byte>>();
|
||||
}
|
||||
|
||||
var task = _context.Input.Reader.PeekAsync();
|
||||
var task = _context.Input.PeekAsync();
|
||||
|
||||
if (task.IsCompleted)
|
||||
{
|
||||
|
|
@ -423,7 +423,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
: base(context)
|
||||
{
|
||||
RequestKeepAlive = keepAlive;
|
||||
_input = _context.Input.Reader;
|
||||
_input = _context.Input;
|
||||
_requestHeaders = headers;
|
||||
}
|
||||
|
||||
|
|
@ -6,18 +6,14 @@ using System.IO.Pipelines;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
public class SocketOutput : ISocketOutput
|
||||
public class SocketOutputProducer : ISocketOutput, IDisposable
|
||||
{
|
||||
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
||||
|
||||
private readonly KestrelThread _thread;
|
||||
private readonly UvStreamHandle _socket;
|
||||
private readonly Connection _connection;
|
||||
private readonly string _connectionId;
|
||||
private readonly IKestrelTrace _log;
|
||||
|
||||
|
|
@ -26,10 +22,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
private bool _cancelled = false;
|
||||
private bool _completed = false;
|
||||
private Exception _lastWriteError;
|
||||
private readonly WriteReqPool _writeReqPool;
|
||||
private readonly IPipe _pipe;
|
||||
private Task _writingTask;
|
||||
|
||||
private readonly IPipeWriter _pipe;
|
||||
private readonly Frame _frame;
|
||||
|
||||
// https://github.com/dotnet/corefxlab/issues/1334
|
||||
// Pipelines don't support multiple awaiters on flush
|
||||
|
|
@ -38,24 +33,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
private readonly object _flushLock = new object();
|
||||
private readonly Action _onFlushCallback;
|
||||
|
||||
public SocketOutput(
|
||||
IPipe pipe,
|
||||
KestrelThread thread,
|
||||
UvStreamHandle socket,
|
||||
Connection connection,
|
||||
string connectionId,
|
||||
IKestrelTrace log)
|
||||
public SocketOutputProducer(IPipeWriter pipe, Frame frame, string connectionId, IKestrelTrace log)
|
||||
{
|
||||
_pipe = pipe;
|
||||
// We need to have empty pipe at this moment so callback
|
||||
// get's scheduled
|
||||
_writingTask = StartWrites();
|
||||
_thread = thread;
|
||||
_socket = socket;
|
||||
_connection = connection;
|
||||
_frame = frame;
|
||||
_connectionId = connectionId;
|
||||
_log = log;
|
||||
_writeReqPool = thread.WriteReqPool;
|
||||
_onFlushCallback = OnFlush;
|
||||
}
|
||||
|
||||
|
|
@ -68,19 +51,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
lock (_contextLock)
|
||||
{
|
||||
if (_socket.IsClosed)
|
||||
{
|
||||
_log.ConnectionDisconnectedWrite(_connectionId, buffer.Count, _lastWriteError);
|
||||
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
if (_completed)
|
||||
{
|
||||
// TODO: Get actual notification when the consumer stopped from Pipes,
|
||||
// so we know if the socket is fully closed and why (for logging exceptions);
|
||||
_log.ConnectionDisconnectedWrite(_connectionId, buffer.Count, ex: null);
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
writableBuffer = _pipe.Writer.Alloc();
|
||||
writableBuffer = _pipe.Alloc();
|
||||
|
||||
if (buffer.Count > 0)
|
||||
{
|
||||
|
|
@ -103,23 +82,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
return FlushAsync(writableBuffer);
|
||||
}
|
||||
|
||||
public void End(ProduceEndType endType)
|
||||
{
|
||||
if (endType == ProduceEndType.SocketShutdown)
|
||||
{
|
||||
// Graceful shutdown
|
||||
_pipe.Reader.CancelPendingRead();
|
||||
}
|
||||
|
||||
lock (_contextLock)
|
||||
{
|
||||
_completed = true;
|
||||
}
|
||||
|
||||
// We're done writing
|
||||
_pipe.Writer.Complete();
|
||||
}
|
||||
|
||||
private Task FlushAsync(WritableBuffer writableBuffer)
|
||||
{
|
||||
var awaitable = writableBuffer.FlushAsync();
|
||||
|
|
@ -164,7 +126,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
_connection.AbortAsync();
|
||||
_frame.Abort();
|
||||
_cancelled = true;
|
||||
return Task.FromCanceled(cancellationToken);
|
||||
}
|
||||
|
|
@ -186,7 +148,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
return WriteAsync(_emptyData, cancellationToken);
|
||||
}
|
||||
|
||||
public void Write<T>(Action<WritableBuffer, T> callback, T state)
|
||||
void ISocketOutput.Write<T>(Action<WritableBuffer, T> callback, T state)
|
||||
{
|
||||
lock (_contextLock)
|
||||
{
|
||||
|
|
@ -195,106 +157,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
return;
|
||||
}
|
||||
|
||||
var buffer = _pipe.Writer.Alloc();
|
||||
var buffer = _pipe.Alloc();
|
||||
callback(buffer, state);
|
||||
buffer.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartWrites()
|
||||
public void Dispose()
|
||||
{
|
||||
while (true)
|
||||
lock (_contextLock)
|
||||
{
|
||||
var result = await _pipe.Reader.ReadAsync();
|
||||
var buffer = result.Buffer;
|
||||
|
||||
try
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
var writeReq = _writeReqPool.Allocate();
|
||||
var writeResult = await writeReq.WriteAsync(_socket, buffer);
|
||||
_writeReqPool.Return(writeReq);
|
||||
|
||||
// REVIEW: Locking here, do we need to take the context lock?
|
||||
OnWriteCompleted(writeResult.Status, writeResult.Error);
|
||||
}
|
||||
|
||||
if (result.IsCancelled)
|
||||
{
|
||||
// Send a FIN
|
||||
await ShutdownAsync();
|
||||
}
|
||||
|
||||
if (buffer.IsEmpty && result.IsCompleted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_pipe.Reader.Advance(result.Buffer.End);
|
||||
}
|
||||
_completed = true;
|
||||
_pipe.Complete();
|
||||
}
|
||||
|
||||
// We're done reading
|
||||
_pipe.Reader.Complete();
|
||||
|
||||
_socket.Dispose();
|
||||
_connection.OnSocketClosed();
|
||||
_log.ConnectionStop(_connectionId);
|
||||
}
|
||||
|
||||
private void OnWriteCompleted(int writeStatus, Exception writeError)
|
||||
{
|
||||
// Called inside _contextLock
|
||||
var status = writeStatus;
|
||||
var error = writeError;
|
||||
|
||||
if (error != null)
|
||||
{
|
||||
// Abort the connection for any failed write
|
||||
// Queued on threadpool so get it in as first op.
|
||||
_connection.AbortAsync();
|
||||
_cancelled = true;
|
||||
_lastWriteError = error;
|
||||
}
|
||||
|
||||
if (error == null)
|
||||
{
|
||||
_log.ConnectionWriteCallback(_connectionId, status);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log connection resets at a lower (Debug) level.
|
||||
if (status == Constants.ECONNRESET)
|
||||
{
|
||||
_log.ConnectionReset(_connectionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.ConnectionError(_connectionId, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task ShutdownAsync()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
_log.ConnectionWriteFin(_connectionId);
|
||||
|
||||
var shutdownReq = new UvShutdownReq(_log);
|
||||
shutdownReq.Init(_thread.Loop);
|
||||
shutdownReq.Shutdown(_socket, (req, status, state) =>
|
||||
{
|
||||
req.Dispose();
|
||||
_log.ConnectionWroteFin(_connectionId, status);
|
||||
|
||||
tcs.TrySetResult(null);
|
||||
},
|
||||
this);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public const int MaxExceptionDetailSize = 128;
|
||||
|
||||
/// <summary>
|
||||
/// The IPEndPoint Kestrel will bind to if nothing else is specified.
|
||||
/// </summary>
|
||||
public static readonly string DefaultServerAddress = "http://localhost:5000";
|
||||
|
||||
/// <summary>
|
||||
/// Prefix of host name used to specify Unix sockets in the configuration.
|
||||
/// </summary>
|
||||
public const string UnixPipeHostPrefix = "unix:/";
|
||||
|
||||
/// <summary>
|
||||
/// Prefix of host name used to specify pipe file descriptor in the configuration.
|
||||
/// </summary>
|
||||
public const string PipeDescriptorPrefix = "pipefd:";
|
||||
|
||||
/// <summary>
|
||||
/// Prefix of host name used to specify socket descriptor in the configuration.
|
||||
/// </summary>
|
||||
public const string SocketDescriptorPrefix = "sockfd:";
|
||||
|
||||
public const string ServerName = "Kestrel";
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
|
||||
|
|
@ -10,16 +9,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
|
|||
{
|
||||
public class ServiceContext
|
||||
{
|
||||
public IApplicationLifetime AppLifetime { get; set; }
|
||||
|
||||
public IKestrelTrace Log { get; set; }
|
||||
|
||||
public IThreadPool ThreadPool { get; set; }
|
||||
|
||||
public Func<Frame, IHttpParser> HttpParserFactory { get; set; }
|
||||
|
||||
public Func<ConnectionContext, Frame> FrameFactory { get; set; }
|
||||
|
||||
public DateHeaderValueManager DateHeaderValueManager { get; set; }
|
||||
|
||||
public KestrelServerOptions ServerOptions { get; set; }
|
||||
|
|
@ -7,15 +7,16 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
|
|
@ -23,21 +24,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
{
|
||||
public class KestrelServer : IServer
|
||||
{
|
||||
private Stack<IDisposable> _disposables;
|
||||
private readonly IApplicationLifetime _applicationLifetime;
|
||||
//private Stack<IDisposable> _disposables;
|
||||
private readonly List<ITransport> _transports = new List<ITransport>();
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServerAddressesFeature _serverAddresses;
|
||||
private readonly ITransportFactory _transportFactory;
|
||||
|
||||
public KestrelServer(IOptions<KestrelServerOptions> options, IApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory)
|
||||
private bool _isRunning;
|
||||
private DateHeaderValueManager _dateHeaderValueManager;
|
||||
|
||||
public KestrelServer(
|
||||
IOptions<KestrelServerOptions> options,
|
||||
ITransportFactory transportFactory,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
if (applicationLifetime == null)
|
||||
if (transportFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(applicationLifetime));
|
||||
throw new ArgumentNullException(nameof(transportFactory));
|
||||
}
|
||||
|
||||
if (loggerFactory == null)
|
||||
|
|
@ -47,7 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
|
||||
Options = options.Value ?? new KestrelServerOptions();
|
||||
InternalOptions = new InternalKestrelServerOptions();
|
||||
_applicationLifetime = applicationLifetime;
|
||||
_transportFactory = transportFactory;
|
||||
_logger = loggerFactory.CreateLogger(typeof(KestrelServer).GetTypeInfo().Namespace);
|
||||
Features = new FeatureCollection();
|
||||
_serverAddresses = new ServerAddressesFeature();
|
||||
|
|
@ -72,14 +81,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
|
||||
ValidateOptions();
|
||||
|
||||
if (_disposables != null)
|
||||
if (_isRunning)
|
||||
{
|
||||
// The server has already started and/or has not been cleaned up yet
|
||||
throw new InvalidOperationException("Server has already started.");
|
||||
}
|
||||
_disposables = new Stack<IDisposable>();
|
||||
_isRunning = true;
|
||||
|
||||
var dateHeaderValueManager = new DateHeaderValueManager();
|
||||
_dateHeaderValueManager = new DateHeaderValueManager();
|
||||
var trace = new KestrelTrace(_logger);
|
||||
|
||||
IThreadPool threadPool;
|
||||
|
|
@ -92,43 +101,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
threadPool = new InlineLoggingThreadPool(trace);
|
||||
}
|
||||
|
||||
var engine = new KestrelEngine(new ServiceContext
|
||||
var serviceContext = new ServiceContext
|
||||
{
|
||||
FrameFactory = context =>
|
||||
{
|
||||
return new Frame<TContext>(application, context);
|
||||
},
|
||||
AppLifetime = _applicationLifetime,
|
||||
Log = trace,
|
||||
HttpParserFactory = frame => new KestrelHttpParser(frame.ConnectionContext.ListenerContext.ServiceContext.Log),
|
||||
HttpParserFactory = frame => new KestrelHttpParser(frame.ServiceContext.Log),
|
||||
ThreadPool = threadPool,
|
||||
DateHeaderValueManager = dateHeaderValueManager,
|
||||
DateHeaderValueManager = _dateHeaderValueManager,
|
||||
ServerOptions = Options
|
||||
});
|
||||
};
|
||||
|
||||
_disposables.Push(engine);
|
||||
_disposables.Push(dateHeaderValueManager);
|
||||
|
||||
var threadCount = Options.ThreadCount;
|
||||
|
||||
if (threadCount <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(threadCount),
|
||||
threadCount,
|
||||
"ThreadCount must be positive.");
|
||||
}
|
||||
|
||||
if (!Constants.ECONNRESET.HasValue)
|
||||
{
|
||||
_logger.LogWarning("Unable to determine ECONNRESET value on this platform.");
|
||||
}
|
||||
|
||||
if (!Constants.EADDRINUSE.HasValue)
|
||||
{
|
||||
_logger.LogWarning("Unable to determine EADDRINUSE value on this platform.");
|
||||
}
|
||||
|
||||
engine.Start(threadCount);
|
||||
var connectionHandler = new ConnectionHandler<TContext>(serviceContext, application);
|
||||
|
||||
var listenOptions = Options.ListenOptions;
|
||||
var hasListenOptions = listenOptions.Any();
|
||||
|
|
@ -137,7 +119,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
if (hasListenOptions && hasServerAddresses)
|
||||
{
|
||||
var joined = string.Join(", ", _serverAddresses.Addresses);
|
||||
_logger.LogWarning($"Overriding address(es) '{joined}'. Binding to endpoints defined in {nameof(WebHostBuilderKestrelExtensions.UseKestrel)}() instead.");
|
||||
_logger.LogWarning($"Overriding address(es) '{joined}'. Binding to endpoints defined in UseKestrel() instead.");
|
||||
|
||||
_serverAddresses.Addresses.Clear();
|
||||
}
|
||||
|
|
@ -146,7 +128,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
_logger.LogDebug($"No listening endpoints were configured. Binding to {Constants.DefaultServerAddress} by default.");
|
||||
|
||||
// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
|
||||
StartLocalhost(engine, ServerAddress.FromUrl(Constants.DefaultServerAddress));
|
||||
StartLocalhost(connectionHandler, ServerAddress.FromUrl(Constants.DefaultServerAddress));
|
||||
|
||||
// If StartLocalhost doesn't throw, there is at least one listener.
|
||||
// The port cannot change for "localhost".
|
||||
|
|
@ -181,7 +163,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
if (string.Equals(parsedAddress.Host, "localhost", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
|
||||
StartLocalhost(engine, parsedAddress);
|
||||
StartLocalhost(connectionHandler, parsedAddress);
|
||||
|
||||
// If StartLocalhost doesn't throw, there is at least one listener.
|
||||
// The port cannot change for "localhost".
|
||||
|
|
@ -201,18 +183,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
|
||||
foreach (var endPoint in listenOptions)
|
||||
{
|
||||
var transport = _transportFactory.Create(endPoint, connectionHandler);
|
||||
_transports.Add(transport);
|
||||
|
||||
try
|
||||
{
|
||||
_disposables.Push(engine.CreateServer(endPoint));
|
||||
transport.BindAsync().Wait();
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
catch (AggregateException ex) when (ex.InnerException is AddressInUseException)
|
||||
{
|
||||
if ((ex.InnerException as UvException)?.StatusCode == Constants.EADDRINUSE)
|
||||
{
|
||||
throw new IOException($"Failed to bind to address {endPoint}: address already in use.", ex);
|
||||
}
|
||||
|
||||
throw;
|
||||
throw new IOException($"Failed to bind to address {endPoint}: address already in use.", ex);
|
||||
}
|
||||
|
||||
// If requested port was "0", replace with assigned dynamic port.
|
||||
|
|
@ -229,14 +209,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposables != null)
|
||||
if (_transports != null)
|
||||
{
|
||||
while (_disposables.Count > 0)
|
||||
var tasks = new Task[_transports.Count];
|
||||
for (int i = 0; i < _transports.Count; i++)
|
||||
{
|
||||
_disposables.Pop().Dispose();
|
||||
tasks[i] = _transports[i].UnbindAsync();
|
||||
}
|
||||
_disposables = null;
|
||||
Task.WaitAll(tasks);
|
||||
|
||||
// TODO: Do transport-agnostic connection management/shutdown.
|
||||
for (int i = 0; i < _transports.Count; i++)
|
||||
{
|
||||
tasks[i] = _transports[i].StopAsync();
|
||||
}
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
|
||||
_dateHeaderValueManager?.Dispose();
|
||||
}
|
||||
|
||||
private void ValidateOptions()
|
||||
|
|
@ -256,7 +246,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
}
|
||||
}
|
||||
|
||||
private void StartLocalhost(KestrelEngine engine, ServerAddress parsedAddress)
|
||||
private void StartLocalhost<TContext>(ConnectionHandler<TContext> connectionHandler, ServerAddress parsedAddress)
|
||||
{
|
||||
if (parsedAddress.Port == 0)
|
||||
{
|
||||
|
|
@ -272,20 +262,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
Scheme = parsedAddress.Scheme,
|
||||
};
|
||||
|
||||
_disposables.Push(engine.CreateServer(ipv4ListenOptions));
|
||||
var transport = _transportFactory.Create(ipv4ListenOptions, connectionHandler);
|
||||
_transports.Add(transport);
|
||||
transport.BindAsync().Wait();
|
||||
}
|
||||
catch (AggregateException ex) when (ex.InnerException is UvException)
|
||||
catch (AggregateException ex) when (ex.InnerException is AddressInUseException)
|
||||
{
|
||||
var uvEx = (UvException)ex.InnerException;
|
||||
if (uvEx.StatusCode == Constants.EADDRINUSE)
|
||||
{
|
||||
throw new IOException($"Failed to bind to address {parsedAddress} on the IPv4 loopback interface: port already in use.", ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(0, $"Unable to bind to {parsedAddress} on the IPv4 loopback interface: ({uvEx.Message})");
|
||||
exceptions.Add(uvEx);
|
||||
}
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
_logger.LogWarning(0, $"Unable to bind to {parsedAddress} on the IPv4 loopback interface: ({ex.Message})");
|
||||
exceptions.Add(ex.InnerException);
|
||||
}
|
||||
|
||||
try
|
||||
|
|
@ -295,20 +283,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
Scheme = parsedAddress.Scheme,
|
||||
};
|
||||
|
||||
_disposables.Push(engine.CreateServer(ipv6ListenOptions));
|
||||
var transport = _transportFactory.Create(ipv6ListenOptions, connectionHandler);
|
||||
_transports.Add(transport);
|
||||
transport.BindAsync().Wait();
|
||||
}
|
||||
catch (AggregateException ex) when (ex.InnerException is UvException)
|
||||
catch (AggregateException ex) when (ex.InnerException is AddressInUseException)
|
||||
{
|
||||
var uvEx = (UvException)ex.InnerException;
|
||||
if (uvEx.StatusCode == Constants.EADDRINUSE)
|
||||
{
|
||||
throw new IOException($"Failed to bind to address {parsedAddress} on the IPv6 loopback interface: port already in use.", ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(0, $"Unable to bind to {parsedAddress} on the IPv6 loopback interface: ({uvEx.Message})");
|
||||
exceptions.Add(uvEx);
|
||||
}
|
||||
throw new IOException($"Failed to bind to address {parsedAddress} on the IPv6 loopback interface: port already in use.", ex);
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
_logger.LogWarning(0, $"Unable to bind to {parsedAddress} on the IPv6 loopback interface: ({ex.Message})");
|
||||
exceptions.Add(ex.InnerException);
|
||||
}
|
||||
|
||||
if (exceptions.Count == 2)
|
||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
|
||||
/// <summary>
|
||||
/// Enables the Listen options callback to resolve and use services registered by the application during startup.
|
||||
/// Typically initialized by <see cref="Hosting.WebHostBuilderKestrelExtensions.UseKestrel(Hosting.IWebHostBuilder, Action{KestrelServerOptions})"/>.
|
||||
/// Typically initialized by UseKestrel()"/>.
|
||||
/// </summary>
|
||||
public IServiceProvider ApplicationServices { get; set; }
|
||||
|
||||
|
|
@ -65,50 +65,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
/// </summary>
|
||||
public KestrelServerLimits Limits { get; } = new KestrelServerLimits();
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time after the server begins shutting down before connections will be forcefully closed.
|
||||
/// Kestrel will wait for the duration of the timeout for any ongoing request processing to complete before
|
||||
/// terminating the connection. No new connections or requests will be accepted during this time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to 5 seconds.
|
||||
/// </remarks>
|
||||
public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// The number of libuv I/O threads used to process requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to half of <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16.
|
||||
/// </remarks>
|
||||
public int ThreadCount { get; set; } = ProcessorThreadCount;
|
||||
|
||||
private static int ProcessorThreadCount
|
||||
{
|
||||
get
|
||||
{
|
||||
// Actual core count would be a better number
|
||||
// rather than logical cores which includes hyper-threaded cores.
|
||||
// Divide by 2 for hyper-threading, and good defaults (still need threads to do webserving).
|
||||
var threadCount = Environment.ProcessorCount >> 1;
|
||||
|
||||
if (threadCount < 1)
|
||||
{
|
||||
// Ensure shifted value is at least one
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (threadCount > 16)
|
||||
{
|
||||
// Receive Side Scaling RSS Processor count currently maxes out at 16
|
||||
// would be better to check the NIC's current hardware queues; but xplat...
|
||||
return 16;
|
||||
}
|
||||
|
||||
return threadCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given IP address and port.
|
||||
/// </summary>
|
||||
|
|
@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
/// The <see cref="IPEndPoint"/> to bind to.
|
||||
/// Only set if the <see cref="ListenOptions"/> <see cref="Type"/> is <see cref="ListenType.IPEndPoint"/>.
|
||||
/// </summary>
|
||||
public IPEndPoint IPEndPoint { get; internal set; }
|
||||
public IPEndPoint IPEndPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The absolute path to a Unix domain socket to bind to.
|
||||
|
|
@ -82,7 +82,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
public List<IConnectionAdapter> ConnectionAdapters { get; } = new List<IConnectionAdapter>();
|
||||
|
||||
// Scheme is hopefully only a temporary measure for back compat with IServerAddressesFeature.
|
||||
internal string Scheme { get; set; } = "http";
|
||||
// TODO: Allow connection adapters to configure the scheme
|
||||
public string Scheme { get; set; } = "http";
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\build\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Core components of ASP.NET Core Kestrel cross-platform web server.</Description>
|
||||
<TargetFrameworks>netstandard1.3;net46</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>aspnetcore;kestrel</PackageTags>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn>CS1591;$(NoWarn)</NoWarn>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Libuv" Version="$(LibUvVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.TaskCache.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
|
||||
<PackageReference Include="System.Numerics.Vectors" Version="$(CoreFxVersion)" />
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="$(CoreFxVersion)" />
|
||||
<PackageReference Include="System.IO.Pipelines" Version="$(CoreFxLabsPipelinesVersion)" />
|
||||
<PackageReference Include="System.Text.Encodings.Web.Utf8" Version="$(CoreFxLabsVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
|
||||
<PackageReference Include="System.Diagnostics.Process" Version="$(CoreFxVersion)" />
|
||||
<PackageReference Include="System.Threading.Thread" Version="$(CoreFxVersion)" />
|
||||
<PackageReference Include="System.Threading.ThreadPool" Version="$(CoreFxVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
// 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.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Exceptions
|
||||
{
|
||||
public class AddressInUseException : InvalidOperationException
|
||||
{
|
||||
public AddressInUseException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public AddressInUseException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Exceptions
|
||||
{
|
||||
public class ConnectionResetException : IOException
|
||||
{
|
||||
public ConnectionResetException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ConnectionResetException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// 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.Pipelines;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport
|
||||
{
|
||||
public interface IConnectionContext
|
||||
{
|
||||
string ConnectionId { get; }
|
||||
IPipeWriter Input { get; }
|
||||
IPipeReader Output { get; }
|
||||
|
||||
// TODO: Remove these (Use Pipes instead?)
|
||||
Task StopAsync();
|
||||
void Abort(Exception ex);
|
||||
void Timeout();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport
|
||||
{
|
||||
public interface IConnectionHandler
|
||||
{
|
||||
IConnectionContext OnConnection(IConnectionInformation connectionInfo);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.IO.Pipelines;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport
|
||||
{
|
||||
public interface IConnectionInformation
|
||||
{
|
||||
ListenOptions ListenOptions { get; }
|
||||
IPEndPoint RemoteEndPoint { get; }
|
||||
IPEndPoint LocalEndPoint { get; }
|
||||
|
||||
PipeFactory PipeFactory { get; }
|
||||
IScheduler InputWriterScheduler { get; }
|
||||
IScheduler OutputWriterScheduler { get; }
|
||||
|
||||
// TODO: Remove timeout management from transport
|
||||
ITimeoutControl TimeoutControl { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport
|
||||
{
|
||||
public interface ITransport
|
||||
{
|
||||
// Can only be called once per ITransport
|
||||
Task BindAsync();
|
||||
Task UnbindAsync();
|
||||
Task StopAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport
|
||||
{
|
||||
public interface ITransportFactory
|
||||
{
|
||||
ITransport Create(ListenOptions listenOptions, IConnectionHandler handler);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
// 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.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Server.Kestrel;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@
|
|||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>aspnetcore;kestrel</PackageTags>
|
||||
<NoWarn>CS1591;$(NoWarn)</NoWarn>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Server.Kestrel\Microsoft.AspNetCore.Server.Kestrel.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Server.Kestrel.Core\Microsoft.AspNetCore.Server.Kestrel.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,292 +0,0 @@
|
|||
{
|
||||
"AssemblyIdentity": "Microsoft.AspNetCore.Server.Kestrel.Https, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
|
||||
"Types": [
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Hosting.KestrelServerOptionsHttpsExtensions",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"Abstract": true,
|
||||
"Static": true,
|
||||
"Sealed": true,
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseHttps",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions"
|
||||
},
|
||||
{
|
||||
"Name": "fileName",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseHttps",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions"
|
||||
},
|
||||
{
|
||||
"Name": "fileName",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "password",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseHttps",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions"
|
||||
},
|
||||
{
|
||||
"Name": "serverCertificate",
|
||||
"Type": "System.Security.Cryptography.X509Certificates.X509Certificate2"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseHttps",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions"
|
||||
},
|
||||
{
|
||||
"Name": "httpsOptions",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilterOptions"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Enumeration",
|
||||
"Sealed": true,
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Field",
|
||||
"Name": "NoCertificate",
|
||||
"Parameters": [],
|
||||
"GenericParameter": [],
|
||||
"Literal": "0"
|
||||
},
|
||||
{
|
||||
"Kind": "Field",
|
||||
"Name": "AllowCertificate",
|
||||
"Parameters": [],
|
||||
"GenericParameter": [],
|
||||
"Literal": "1"
|
||||
},
|
||||
{
|
||||
"Kind": "Field",
|
||||
"Name": "RequireCertificate",
|
||||
"Parameters": [],
|
||||
"GenericParameter": [],
|
||||
"Literal": "2"
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilter",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [
|
||||
"Microsoft.AspNetCore.Server.Kestrel.Filter.IConnectionFilter"
|
||||
],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "OnConnectionAsync",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "context",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Filter.ConnectionFilterContext"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Threading.Tasks.Task",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Server.Kestrel.Filter.IConnectionFilter",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilterOptions"
|
||||
},
|
||||
{
|
||||
"Name": "previous",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Filter.IConnectionFilter"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilterOptions",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_ServerCertificate",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Security.Cryptography.X509Certificates.X509Certificate2",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_ServerCertificate",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Security.Cryptography.X509Certificates.X509Certificate2"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_ClientCertificateMode",
|
||||
"Parameters": [],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_ClientCertificateMode",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_ClientCertificateValidation",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Func<System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.X509Certificates.X509Chain, System.Net.Security.SslPolicyErrors, System.Boolean>",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_ClientCertificateValidation",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Func<System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.X509Certificates.X509Chain, System.Net.Security.SslPolicyErrors, System.Boolean>"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_SslProtocols",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Security.Authentication.SslProtocols",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_SslProtocols",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Security.Authentication.SslProtocols"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_CheckCertificateRevocation",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Boolean",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_CheckCertificateRevocation",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Boolean"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,292 +0,0 @@
|
|||
{
|
||||
"AssemblyIdentity": "Microsoft.AspNetCore.Server.Kestrel.Https, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
|
||||
"Types": [
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Hosting.KestrelServerOptionsHttpsExtensions",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"Abstract": true,
|
||||
"Static": true,
|
||||
"Sealed": true,
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseHttps",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions"
|
||||
},
|
||||
{
|
||||
"Name": "fileName",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseHttps",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions"
|
||||
},
|
||||
{
|
||||
"Name": "fileName",
|
||||
"Type": "System.String"
|
||||
},
|
||||
{
|
||||
"Name": "password",
|
||||
"Type": "System.String"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseHttps",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions"
|
||||
},
|
||||
{
|
||||
"Name": "serverCertificate",
|
||||
"Type": "System.Security.Cryptography.X509Certificates.X509Certificate2"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "UseHttps",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions"
|
||||
},
|
||||
{
|
||||
"Name": "httpsOptions",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilterOptions"
|
||||
}
|
||||
],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.KestrelServerOptions",
|
||||
"Static": true,
|
||||
"Extension": true,
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Enumeration",
|
||||
"Sealed": true,
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Field",
|
||||
"Name": "NoCertificate",
|
||||
"Parameters": [],
|
||||
"GenericParameter": [],
|
||||
"Literal": "0"
|
||||
},
|
||||
{
|
||||
"Kind": "Field",
|
||||
"Name": "AllowCertificate",
|
||||
"Parameters": [],
|
||||
"GenericParameter": [],
|
||||
"Literal": "1"
|
||||
},
|
||||
{
|
||||
"Kind": "Field",
|
||||
"Name": "RequireCertificate",
|
||||
"Parameters": [],
|
||||
"GenericParameter": [],
|
||||
"Literal": "2"
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilter",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [
|
||||
"Microsoft.AspNetCore.Server.Kestrel.Filter.IConnectionFilter"
|
||||
],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "OnConnectionAsync",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "context",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Filter.ConnectionFilterContext"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Threading.Tasks.Task",
|
||||
"Sealed": true,
|
||||
"Virtual": true,
|
||||
"ImplementedInterface": "Microsoft.AspNetCore.Server.Kestrel.Filter.IConnectionFilter",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "options",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilterOptions"
|
||||
},
|
||||
{
|
||||
"Name": "previous",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Filter.IConnectionFilter"
|
||||
}
|
||||
],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilterOptions",
|
||||
"Visibility": "Public",
|
||||
"Kind": "Class",
|
||||
"ImplementedInterfaces": [],
|
||||
"Members": [
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_ServerCertificate",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Security.Cryptography.X509Certificates.X509Certificate2",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_ServerCertificate",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Security.Cryptography.X509Certificates.X509Certificate2"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_ClientCertificateMode",
|
||||
"Parameters": [],
|
||||
"ReturnType": "Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_ClientCertificateMode",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_ClientCertificateValidation",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Func<System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.X509Certificates.X509Chain, System.Net.Security.SslPolicyErrors, System.Boolean>",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_ClientCertificateValidation",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Func<System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.X509Certificates.X509Chain, System.Net.Security.SslPolicyErrors, System.Boolean>"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_SslProtocols",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Security.Authentication.SslProtocols",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_SslProtocols",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Security.Authentication.SslProtocols"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "get_CheckCertificateRevocation",
|
||||
"Parameters": [],
|
||||
"ReturnType": "System.Boolean",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Method",
|
||||
"Name": "set_CheckCertificateRevocation",
|
||||
"Parameters": [
|
||||
{
|
||||
"Name": "value",
|
||||
"Type": "System.Boolean"
|
||||
}
|
||||
],
|
||||
"ReturnType": "System.Void",
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
},
|
||||
{
|
||||
"Kind": "Constructor",
|
||||
"Name": ".ctor",
|
||||
"Parameters": [],
|
||||
"Visibility": "Public",
|
||||
"GenericParameter": []
|
||||
}
|
||||
],
|
||||
"GenericParameters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
[
|
||||
{
|
||||
"OldTypeId": "public static class Microsoft.AspNetCore.Hosting.KestrelServerOptionsHttpsExtensions",
|
||||
"Kind": "Removal"
|
||||
},
|
||||
{
|
||||
"OldTypeId": "public class Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilter : Microsoft.AspNetCore.Server.Kestrel.Filter.IConnectionFilter",
|
||||
"Kind": "Removal"
|
||||
},
|
||||
{
|
||||
"OldTypeId": "public class Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilterOptions",
|
||||
"Kind": "Removal"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
[
|
||||
{
|
||||
"OldTypeId": "public static class Microsoft.AspNetCore.Hosting.KestrelServerOptionsHttpsExtensions",
|
||||
"Kind": "Removal"
|
||||
},
|
||||
{
|
||||
"OldTypeId": "public class Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilter : Microsoft.AspNetCore.Server.Kestrel.Filter.IConnectionFilter",
|
||||
"Kind": "Removal"
|
||||
},
|
||||
{
|
||||
"OldTypeId": "public class Microsoft.AspNetCore.Server.Kestrel.Https.HttpsConnectionFilterOptions",
|
||||
"Kind": "Removal"
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
public class Connection : ConnectionContext, ITimeoutControl
|
||||
{
|
||||
private const int MinAllocBufferSize = 2048;
|
||||
|
||||
private static readonly Action<UvStreamHandle, int, object> _readCallback =
|
||||
(handle, status, state) => ReadCallback(handle, status, state);
|
||||
|
||||
private static readonly Func<UvStreamHandle, int, object, LibuvFunctions.uv_buf_t> _allocCallback =
|
||||
(handle, suggestedsize, state) => AllocCallback(handle, suggestedsize, state);
|
||||
|
||||
private readonly UvStreamHandle _socket;
|
||||
private IConnectionContext _connectionContext;
|
||||
|
||||
private TaskCompletionSource<object> _socketClosedTcs = new TaskCompletionSource<object>();
|
||||
|
||||
private long _lastTimestamp;
|
||||
private long _timeoutTimestamp = long.MaxValue;
|
||||
private TimeoutAction _timeoutAction;
|
||||
private WritableBuffer? _currentWritableBuffer;
|
||||
|
||||
public Connection(ListenerContext context, UvStreamHandle socket) : base(context)
|
||||
{
|
||||
_socket = socket;
|
||||
socket.Connection = this;
|
||||
TimeoutControl = this;
|
||||
|
||||
var tcpHandle = _socket as UvTcpHandle;
|
||||
if (tcpHandle != null)
|
||||
{
|
||||
RemoteEndPoint = tcpHandle.GetPeerIPEndPoint();
|
||||
LocalEndPoint = tcpHandle.GetSockIPEndPoint();
|
||||
}
|
||||
|
||||
_lastTimestamp = Thread.Loop.Now();
|
||||
}
|
||||
|
||||
// For testing
|
||||
public Connection()
|
||||
{
|
||||
}
|
||||
|
||||
public string ConnectionId { get; set; }
|
||||
public IPipeWriter Input { get; set; }
|
||||
public SocketOutputConsumer Output { get; set; }
|
||||
|
||||
private IKestrelTrace Log => ListenerContext.TransportContext.Log;
|
||||
private IConnectionHandler ConnectionHandler => ListenerContext.TransportContext.ConnectionHandler;
|
||||
private KestrelThread Thread => ListenerContext.Thread;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
_connectionContext = ConnectionHandler.OnConnection(this);
|
||||
ConnectionId = _connectionContext.ConnectionId;
|
||||
|
||||
Log.ConnectionStart(ConnectionId);
|
||||
KestrelEventSource.Log.ConnectionStart(this);
|
||||
|
||||
Input = _connectionContext.Input;
|
||||
Output = new SocketOutputConsumer(_connectionContext.Output, Thread, _socket, this, ConnectionId, Log);
|
||||
|
||||
// Start socket prior to applying the ConnectionAdapter
|
||||
_socket.ReadStart(_allocCallback, _readCallback, this);
|
||||
_lastTimestamp = Thread.Loop.Now();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.LogError(0, e, "Connection.StartFrame");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
return Task.WhenAll(_connectionContext.StopAsync(), _socketClosedTcs.Task);
|
||||
}
|
||||
|
||||
public virtual Task AbortAsync(Exception error = null)
|
||||
{
|
||||
_connectionContext.Abort(error);
|
||||
return _socketClosedTcs.Task;
|
||||
}
|
||||
|
||||
// Called on Libuv thread
|
||||
public virtual void OnSocketClosed()
|
||||
{
|
||||
KestrelEventSource.Log.ConnectionStop(this);
|
||||
|
||||
Input.Complete(new TaskCanceledException("The request was aborted"));
|
||||
_socketClosedTcs.TrySetResult(null);
|
||||
}
|
||||
|
||||
// Called on Libuv thread
|
||||
public void Tick(long timestamp)
|
||||
{
|
||||
if (timestamp > PlatformApis.VolatileRead(ref _timeoutTimestamp))
|
||||
{
|
||||
TimeoutControl.CancelTimeout();
|
||||
|
||||
if (_timeoutAction == TimeoutAction.SendTimeoutResponse)
|
||||
{
|
||||
_connectionContext.Timeout();
|
||||
}
|
||||
|
||||
StopAsync();
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref _lastTimestamp, timestamp);
|
||||
}
|
||||
|
||||
private static LibuvFunctions.uv_buf_t AllocCallback(UvStreamHandle handle, int suggestedSize, object state)
|
||||
{
|
||||
return ((Connection)state).OnAlloc(handle, suggestedSize);
|
||||
}
|
||||
|
||||
private unsafe LibuvFunctions.uv_buf_t OnAlloc(UvStreamHandle handle, int suggestedSize)
|
||||
{
|
||||
Debug.Assert(_currentWritableBuffer == null);
|
||||
var currentWritableBuffer = Input.Alloc(MinAllocBufferSize);
|
||||
_currentWritableBuffer = currentWritableBuffer;
|
||||
void* dataPtr;
|
||||
var tryGetPointer = currentWritableBuffer.Buffer.TryGetPointer(out dataPtr);
|
||||
Debug.Assert(tryGetPointer);
|
||||
|
||||
return handle.Libuv.buf_init(
|
||||
(IntPtr)dataPtr,
|
||||
currentWritableBuffer.Buffer.Length);
|
||||
}
|
||||
|
||||
private static void ReadCallback(UvStreamHandle handle, int status, object state)
|
||||
{
|
||||
((Connection)state).OnRead(handle, status);
|
||||
}
|
||||
|
||||
private async void OnRead(UvStreamHandle handle, int status)
|
||||
{
|
||||
var normalRead = status >= 0;
|
||||
var normalDone = status == Constants.EOF;
|
||||
var errorDone = !(normalDone || normalRead);
|
||||
var readCount = normalRead ? status : 0;
|
||||
|
||||
if (normalRead)
|
||||
{
|
||||
Log.ConnectionRead(ConnectionId, readCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket.ReadStop();
|
||||
|
||||
if (normalDone)
|
||||
{
|
||||
Log.ConnectionReadFin(ConnectionId);
|
||||
}
|
||||
}
|
||||
|
||||
IOException error = null;
|
||||
WritableBufferAwaitable? flushTask = null;
|
||||
if (errorDone)
|
||||
{
|
||||
Exception uvError;
|
||||
handle.Libuv.Check(status, out uvError);
|
||||
|
||||
// Log connection resets at a lower (Debug) level.
|
||||
if (status == Constants.ECONNRESET)
|
||||
{
|
||||
Log.ConnectionReset(ConnectionId);
|
||||
error = new ConnectionResetException(uvError.Message, uvError);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ConnectionError(ConnectionId, uvError);
|
||||
error = new IOException(uvError.Message, uvError);
|
||||
}
|
||||
|
||||
_currentWritableBuffer?.Commit();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(_currentWritableBuffer != null);
|
||||
|
||||
var currentWritableBuffer = _currentWritableBuffer.Value;
|
||||
currentWritableBuffer.Advance(readCount);
|
||||
flushTask = currentWritableBuffer.FlushAsync();
|
||||
}
|
||||
|
||||
_currentWritableBuffer = null;
|
||||
if (flushTask?.IsCompleted == false)
|
||||
{
|
||||
Pause();
|
||||
var result = await flushTask.Value;
|
||||
// If the reader isn't complete then resume
|
||||
if (!result.IsCompleted)
|
||||
{
|
||||
Resume();
|
||||
}
|
||||
}
|
||||
|
||||
if (!normalRead)
|
||||
{
|
||||
Input.Complete(error);
|
||||
var ignore = AbortAsync(error);
|
||||
}
|
||||
}
|
||||
|
||||
private void Pause()
|
||||
{
|
||||
// It's possible that uv_close was called between the call to Thread.Post() and now.
|
||||
if (!_socket.IsClosed)
|
||||
{
|
||||
_socket.ReadStop();
|
||||
}
|
||||
}
|
||||
|
||||
private void Resume()
|
||||
{
|
||||
// It's possible that uv_close was called even before the call to Resume().
|
||||
if (!_socket.IsClosed)
|
||||
{
|
||||
try
|
||||
{
|
||||
_socket.ReadStart(_allocCallback, _readCallback, this);
|
||||
}
|
||||
catch (UvException)
|
||||
{
|
||||
// ReadStart() can throw a UvException in some cases (e.g. socket is no longer connected).
|
||||
// This should be treated the same as OnRead() seeing a "normalDone" condition.
|
||||
Log.ConnectionReadFin(ConnectionId);
|
||||
Input.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ITimeoutControl.SetTimeout(long milliseconds, TimeoutAction timeoutAction)
|
||||
{
|
||||
Debug.Assert(_timeoutTimestamp == long.MaxValue, "Concurrent timeouts are not supported");
|
||||
|
||||
AssignTimeout(milliseconds, timeoutAction);
|
||||
}
|
||||
|
||||
void ITimeoutControl.ResetTimeout(long milliseconds, TimeoutAction timeoutAction)
|
||||
{
|
||||
AssignTimeout(milliseconds, timeoutAction);
|
||||
}
|
||||
|
||||
void ITimeoutControl.CancelTimeout()
|
||||
{
|
||||
Interlocked.Exchange(ref _timeoutTimestamp, long.MaxValue);
|
||||
}
|
||||
|
||||
private void AssignTimeout(long milliseconds, TimeoutAction timeoutAction)
|
||||
{
|
||||
_timeoutAction = timeoutAction;
|
||||
|
||||
// Add KestrelThread.HeartbeatMilliseconds extra milliseconds since this can be called right before the next heartbeat.
|
||||
Interlocked.Exchange(ref _timeoutTimestamp, _lastTimestamp + milliseconds + KestrelThread.HeartbeatMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
// 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.Net;
|
||||
using System.IO.Pipelines;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
public class ConnectionContext
|
||||
public class ConnectionContext : IConnectionInformation
|
||||
{
|
||||
public ConnectionContext()
|
||||
{
|
||||
|
|
@ -21,16 +20,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
|
||||
public ListenerContext ListenerContext { get; set; }
|
||||
|
||||
public IPipe Input { get; set; }
|
||||
|
||||
public ISocketOutput Output { get; set; }
|
||||
|
||||
public IConnectionControl ConnectionControl { get; set; }
|
||||
|
||||
public ListenOptions ListenOptions => ListenerContext.ListenOptions;
|
||||
public IPEndPoint RemoteEndPoint { get; set; }
|
||||
|
||||
public IPEndPoint LocalEndPoint { get; set; }
|
||||
|
||||
public string ConnectionId { get; set; }
|
||||
public PipeFactory PipeFactory => ListenerContext.Thread.PipelineFactory;
|
||||
public IScheduler InputWriterScheduler => ListenerContext.Thread;
|
||||
public IScheduler OutputWriterScheduler => ListenerContext.Thread;
|
||||
|
||||
public ITimeoutControl TimeoutControl { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
|
|
@ -12,12 +11,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
public class ConnectionManager
|
||||
{
|
||||
private readonly KestrelThread _thread;
|
||||
private readonly IThreadPool _threadPool;
|
||||
|
||||
public ConnectionManager(KestrelThread thread, IThreadPool threadPool)
|
||||
public ConnectionManager(KestrelThread thread)
|
||||
{
|
||||
_thread = thread;
|
||||
_threadPool = threadPool;
|
||||
}
|
||||
|
||||
public async Task<bool> WalkConnectionsAndCloseAsync(TimeSpan timeout)
|
||||
|
|
@ -64,9 +61,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
}
|
||||
});
|
||||
|
||||
_threadPool.Run(() =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
try
|
||||
{
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.SetException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
tcs.SetResult(null);
|
||||
});
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
|
|
@ -16,14 +17,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
private bool _closed;
|
||||
|
||||
public Listener(ServiceContext serviceContext)
|
||||
: base(serviceContext)
|
||||
public Listener(LibuvTransportContext transportContext) : base(transportContext)
|
||||
{
|
||||
}
|
||||
|
||||
protected UvStreamHandle ListenSocket { get; private set; }
|
||||
|
||||
public IKestrelTrace Log => ServiceContext.Log;
|
||||
public IKestrelTrace Log => TransportContext.Log;
|
||||
|
||||
public Task StartAsync(
|
||||
ListenOptions listenOptions,
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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 Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
{
|
||||
public class ListenerContext
|
||||
{
|
||||
public ListenerContext(LibuvTransportContext transportContext)
|
||||
{
|
||||
TransportContext = transportContext;
|
||||
}
|
||||
|
||||
public LibuvTransportContext TransportContext { get; set; }
|
||||
|
||||
public ListenOptions ListenOptions { get; set; }
|
||||
|
||||
public KestrelThread Thread { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a socket which can be used to accept an incoming connection.
|
||||
/// </summary>
|
||||
protected UvStreamHandle CreateAcceptSocket()
|
||||
{
|
||||
switch (ListenOptions.Type)
|
||||
{
|
||||
case ListenType.IPEndPoint:
|
||||
case ListenType.FileHandle:
|
||||
var tcpHandle = new UvTcpHandle(TransportContext.Log);
|
||||
tcpHandle.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||
tcpHandle.NoDelay(ListenOptions.NoDelay);
|
||||
return tcpHandle;
|
||||
case ListenType.SocketPath:
|
||||
var pipeHandle = new UvPipeHandle(TransportContext.Log);
|
||||
pipeHandle.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||
return pipeHandle;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ using System.Runtime.InteropServices;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||
|
|
@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
// but it has no other functional significance
|
||||
private readonly ArraySegment<ArraySegment<byte>> _dummyMessage = new ArraySegment<ArraySegment<byte>>(new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }) });
|
||||
|
||||
public ListenerPrimary(ServiceContext serviceContext) : base(serviceContext)
|
||||
public ListenerPrimary(LibuvTransportContext transportContext) : base(transportContext)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +206,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
_bufPtr = _bufHandle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
public Libuv.uv_buf_t AllocCallback(UvStreamHandle dispatchPipe, int suggestedSize)
|
||||
public LibuvFunctions.uv_buf_t AllocCallback(UvStreamHandle dispatchPipe, int suggestedSize)
|
||||
{
|
||||
return dispatchPipe.Libuv.buf_init(_bufPtr + _bytesRead, _bufferLength - _bytesRead);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue