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
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26228.4
|
VisualStudioVersion = 15.0.26228.9
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
|
@ -40,7 +40,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC
|
||||||
test\shared\TestServiceContext.cs = test\shared\TestServiceContext.cs
|
test\shared\TestServiceContext.cs = test\shared\TestServiceContext.cs
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
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
|
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}"
|
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
|
EndProject
|
||||||
|
|
@ -61,6 +61,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestResources", "TestResour
|
||||||
test\shared\TestResources\testCert.pfx = test\shared\TestResources\testCert.pfx
|
test\shared\TestResources\testCert.pfx = test\shared\TestResources\testCert.pfx
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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|x64.Build.0 = Release|Any CPU
|
||||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Release|x86.ActiveCfg = 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
|
{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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -182,5 +210,7 @@ Global
|
||||||
{9559A5F1-080C-4909-B6CF-7E4B3DC55748} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
|
{9559A5F1-080C-4909-B6CF-7E4B3DC55748} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
|
||||||
{EBFE9719-A44B-4978-A71F-D5C254E7F35A} = {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}
|
{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
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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" />
|
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.Kestrel.Https\Microsoft.AspNetCore.Server.Kestrel.Https.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,9 +61,11 @@ namespace SampleApp
|
||||||
|
|
||||||
// The following section should be used to demo sockets
|
// The following section should be used to demo sockets
|
||||||
//options.ListenUnixSocket("/tmp/kestrel-test.sock");
|
//options.ListenUnixSocket("/tmp/kestrel-test.sock");
|
||||||
|
})
|
||||||
|
.UseLibuv(options =>
|
||||||
|
{
|
||||||
// Uncomment the following line to change the default number of libuv threads for all endpoints.
|
// Uncomment the following line to change the default number of libuv threads for all endpoints.
|
||||||
//options.ThreadCount = 4;
|
options.ThreadCount = 4;
|
||||||
})
|
})
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Adapter
|
namespace Microsoft.AspNetCore.Server.Kestrel.Adapter
|
||||||
|
|
@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Adapter.Internal
|
namespace Microsoft.AspNetCore.Server.Kestrel.Adapter.Internal
|
||||||
{
|
{
|
||||||
public class AdaptedPipeline : IDisposable
|
public class AdaptedPipeline
|
||||||
{
|
{
|
||||||
private const int MinAllocBufferSize = 2048;
|
private const int MinAllocBufferSize = 2048;
|
||||||
|
|
||||||
|
|
@ -31,30 +31,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Adapter.Internal
|
||||||
|
|
||||||
public ISocketOutput Output => _output;
|
public ISocketOutput Output => _output;
|
||||||
|
|
||||||
public void Dispose()
|
public async Task RunAsync()
|
||||||
{
|
|
||||||
Input.Writer.Complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartAsync()
|
|
||||||
{
|
{
|
||||||
var inputTask = ReadInputAsync();
|
var inputTask = ReadInputAsync();
|
||||||
var outputTask = _output.WriteOutputAsync();
|
var outputTask = _output.WriteOutputAsync();
|
||||||
|
|
||||||
var result = await Task.WhenAny(inputTask, outputTask);
|
await inputTask;
|
||||||
|
|
||||||
if (result == inputTask)
|
_output.Dispose();
|
||||||
{
|
|
||||||
// Close output
|
|
||||||
_output.Dispose();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Close input
|
|
||||||
Input.Writer.Complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.WhenAll(inputTask, outputTask);
|
await outputTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReadInputAsync()
|
private async Task ReadInputAsync()
|
||||||
|
|
@ -7,7 +7,6 @@ using System.IO.Pipelines;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Adapter.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 Stream _outputStream;
|
||||||
private readonly IPipe _pipe;
|
private readonly IPipe _pipe;
|
||||||
private object _sync = new object();
|
private readonly object _sync = new object();
|
||||||
private bool _completed;
|
private bool _completed;
|
||||||
|
|
||||||
public StreamSocketOutput(Stream outputStream, IPipe pipe)
|
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;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
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 System.Threading;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
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
|
// 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
|
// No requests since idle threshold so stop the timer if it's still running
|
||||||
StopTimer();
|
StopTimer();
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -16,6 +16,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Adapter;
|
using Microsoft.AspNetCore.Server.Kestrel.Adapter;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Primitives;
|
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>> _onStarting;
|
||||||
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
||||||
|
|
||||||
private TaskCompletionSource<object> _frameStartedTcs = new TaskCompletionSource<object>();
|
|
||||||
private Task _requestProcessingTask;
|
private Task _requestProcessingTask;
|
||||||
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
||||||
protected int _requestAborted;
|
protected int _requestAborted;
|
||||||
|
|
@ -77,37 +77,39 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
protected long _responseBytesWritten;
|
protected long _responseBytesWritten;
|
||||||
|
|
||||||
|
private readonly FrameContext _frameContext;
|
||||||
private readonly IHttpParser _parser;
|
private readonly IHttpParser _parser;
|
||||||
|
|
||||||
public Frame(ConnectionContext context)
|
public Frame(FrameContext frameContext)
|
||||||
{
|
{
|
||||||
ConnectionContext = context;
|
_frameContext = frameContext;
|
||||||
Input = context.Input;
|
|
||||||
Output = context.Output;
|
|
||||||
|
|
||||||
ServerOptions = context.ListenerContext.ServiceContext.ServerOptions;
|
ServerOptions = ServiceContext.ServerOptions;
|
||||||
|
|
||||||
_parser = context.ListenerContext.ServiceContext.HttpParserFactory(this);
|
_parser = ServiceContext.HttpParserFactory(this);
|
||||||
|
|
||||||
FrameControl = this;
|
FrameControl = this;
|
||||||
_keepAliveMilliseconds = (long)ServerOptions.Limits.KeepAliveTimeout.TotalMilliseconds;
|
_keepAliveMilliseconds = (long)ServerOptions.Limits.KeepAliveTimeout.TotalMilliseconds;
|
||||||
_requestHeadersTimeoutMilliseconds = (long)ServerOptions.Limits.RequestHeadersTimeout.TotalMilliseconds;
|
_requestHeadersTimeoutMilliseconds = (long)ServerOptions.Limits.RequestHeadersTimeout.TotalMilliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConnectionContext ConnectionContext { get; }
|
public ServiceContext ServiceContext => _frameContext.ServiceContext;
|
||||||
public IPipe Input { get; set; }
|
public IConnectionInformation ConnectionInformation => _frameContext.ConnectionInformation;
|
||||||
|
|
||||||
|
public IPipeReader Input { get; set; }
|
||||||
public ISocketOutput Output { get; set; }
|
public ISocketOutput Output { get; set; }
|
||||||
public IEnumerable<IAdaptedConnection> AdaptedConnections { get; set; }
|
public IEnumerable<IAdaptedConnection> AdaptedConnections { get; set; }
|
||||||
|
public ConnectionLifetimeControl LifetimeControl { get; set; }
|
||||||
|
|
||||||
protected IConnectionControl ConnectionControl => ConnectionContext.ConnectionControl;
|
protected ITimeoutControl TimeoutControl => ConnectionInformation.TimeoutControl;
|
||||||
protected IKestrelTrace Log => ConnectionContext.ListenerContext.ServiceContext.Log;
|
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
|
// Hold direct reference to ServerOptions since this is used very often in the request processing path
|
||||||
private KestrelServerOptions ServerOptions { get; }
|
private KestrelServerOptions ServerOptions { get; }
|
||||||
private IPEndPoint LocalEndPoint => ConnectionContext.LocalEndPoint;
|
private IPEndPoint LocalEndPoint => ConnectionInformation.LocalEndPoint;
|
||||||
private IPEndPoint RemoteEndPoint => ConnectionContext.RemoteEndPoint;
|
private IPEndPoint RemoteEndPoint => ConnectionInformation.RemoteEndPoint;
|
||||||
protected string ConnectionId => ConnectionContext.ConnectionId;
|
protected string ConnectionId => _frameContext.ConnectionId;
|
||||||
|
|
||||||
public string ConnectionIdFeature { get; set; }
|
public string ConnectionIdFeature { get; set; }
|
||||||
public IPAddress RemoteIpAddress { get; set; }
|
public IPAddress RemoteIpAddress { get; set; }
|
||||||
|
|
@ -218,8 +220,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
public Stream DuplexStream { get; set; }
|
public Stream DuplexStream { get; set; }
|
||||||
|
|
||||||
public Task FrameStartedTask => _frameStartedTcs.Task;
|
|
||||||
|
|
||||||
public CancellationToken RequestAborted
|
public CancellationToken RequestAborted
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -392,7 +392,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
_requestProcessingTask = RequestProcessingAsync();
|
_requestProcessingTask = RequestProcessingAsync();
|
||||||
_frameStartedTcs.SetResult(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -404,11 +403,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
public Task StopAsync()
|
public Task StopAsync()
|
||||||
{
|
{
|
||||||
_requestProcessingStopping = true;
|
_requestProcessingStopping = true;
|
||||||
Input.Reader.CancelPendingRead();
|
Input.CancelPendingRead();
|
||||||
|
|
||||||
return _requestProcessingTask ?? TaskCache.CompletedTask;
|
return _requestProcessingTask ?? TaskCache.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CancelRequestAbortedToken()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RequestAbortedSource.Cancel();
|
||||||
|
_abortedCts = null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.ApplicationError(ConnectionId, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Immediate kill the connection and poison the request and response streams.
|
/// Immediate kill the connection and poison the request and response streams.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -421,24 +433,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
_frameStreams?.RequestBody.Abort(error);
|
_frameStreams?.RequestBody.Abort(error);
|
||||||
_frameStreams?.ResponseBody.Abort();
|
_frameStreams?.ResponseBody.Abort();
|
||||||
|
|
||||||
try
|
LifetimeControl.End(ProduceEndType.SocketDisconnect);
|
||||||
{
|
|
||||||
ConnectionControl.End(ProduceEndType.SocketDisconnect);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.LogError(0, ex, "Abort");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
// Potentially calling user code. CancelRequestAbortedToken logs any exceptions.
|
||||||
{
|
ServiceContext.ThreadPool.UnsafeRun(state => ((Frame)state).CancelRequestAbortedToken(), this);
|
||||||
RequestAbortedSource.Cancel();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.LogError(0, ex, "Abort");
|
|
||||||
}
|
|
||||||
_abortedCts = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -856,7 +854,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
if (_keepAlive)
|
if (_keepAlive)
|
||||||
{
|
{
|
||||||
ConnectionControl.End(ProduceEndType.ConnectionKeepAlive);
|
LifetimeControl.End(ProduceEndType.ConnectionKeepAlive);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HttpMethods.IsHead(Method) && _responseBytesWritten > 0)
|
if (HttpMethods.IsHead(Method) && _responseBytesWritten > 0)
|
||||||
|
|
@ -876,7 +874,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
if (_keepAlive)
|
if (_keepAlive)
|
||||||
{
|
{
|
||||||
ConnectionControl.End(ProduceEndType.ConnectionKeepAlive);
|
LifetimeControl.End(ProduceEndType.ConnectionKeepAlive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -995,7 +993,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionControl.ResetTimeout(_requestHeadersTimeoutMilliseconds, TimeoutAction.SendTimeoutResponse);
|
TimeoutControl.ResetTimeout(_requestHeadersTimeoutMilliseconds, TimeoutAction.SendTimeoutResponse);
|
||||||
|
|
||||||
_requestProcessingStatus = RequestProcessingStatus.ParsingRequestLine;
|
_requestProcessingStatus = RequestProcessingStatus.ParsingRequestLine;
|
||||||
goto case RequestProcessingStatus.ParsingRequestLine;
|
goto case RequestProcessingStatus.ParsingRequestLine;
|
||||||
|
|
@ -1060,7 +1058,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
}
|
}
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
ConnectionControl.CancelTimeout();
|
TimeoutControl.CancelTimeout();
|
||||||
}
|
}
|
||||||
return result;
|
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;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Hosting.Server;
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Exceptions;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
@ -16,8 +15,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
private readonly IHttpApplication<TContext> _application;
|
private readonly IHttpApplication<TContext> _application;
|
||||||
|
|
||||||
public Frame(IHttpApplication<TContext> application, ConnectionContext context)
|
public Frame(IHttpApplication<TContext> application, FrameContext frameContext)
|
||||||
: base(context)
|
: base(frameContext)
|
||||||
{
|
{
|
||||||
_application = application;
|
_application = application;
|
||||||
}
|
}
|
||||||
|
|
@ -34,13 +33,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
while (!_requestProcessingStopping)
|
while (!_requestProcessingStopping)
|
||||||
{
|
{
|
||||||
ConnectionControl.SetTimeout(_keepAliveMilliseconds, TimeoutAction.CloseConnection);
|
TimeoutControl.SetTimeout(_keepAliveMilliseconds, TimeoutAction.CloseConnection);
|
||||||
|
|
||||||
InitializeHeaders();
|
InitializeHeaders();
|
||||||
|
|
||||||
while (!_requestProcessingStopping)
|
while (!_requestProcessingStopping)
|
||||||
{
|
{
|
||||||
var result = await Input.Reader.ReadAsync();
|
var result = await Input.ReadAsync();
|
||||||
var examined = result.Buffer.End;
|
var examined = result.Buffer.End;
|
||||||
var consumed = result.Buffer.End;
|
var consumed = result.Buffer.End;
|
||||||
|
|
||||||
|
|
@ -52,13 +51,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
if (_requestProcessingStatus == RequestProcessingStatus.ParsingHeaders)
|
if (_requestProcessingStatus == RequestProcessingStatus.ParsingHeaders)
|
||||||
{
|
{
|
||||||
throw BadHttpRequestException.GetException(RequestRejectionReason.MalformedRequestInvalidHeaders);
|
throw BadHttpRequestException.GetException(RequestRejectionReason
|
||||||
|
.MalformedRequestInvalidHeaders);
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Input.Reader.Advance(consumed, examined);
|
Input.Advance(consumed, examined);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_requestProcessingStatus == RequestProcessingStatus.AppStarted)
|
if (_requestProcessingStatus == RequestProcessingStatus.AppStarted)
|
||||||
|
|
@ -73,9 +73,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
case RequestProcessingStatus.RequestPending:
|
case RequestProcessingStatus.RequestPending:
|
||||||
return;
|
return;
|
||||||
case RequestProcessingStatus.ParsingRequestLine:
|
case RequestProcessingStatus.ParsingRequestLine:
|
||||||
throw BadHttpRequestException.GetException(RequestRejectionReason.InvalidRequestLine);
|
throw BadHttpRequestException.GetException(
|
||||||
|
RequestRejectionReason.InvalidRequestLine);
|
||||||
case RequestProcessingStatus.ParsingHeaders:
|
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 logs the error.
|
||||||
SetBadRequestState(ex);
|
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.
|
// Don't log ECONNRESET errors made between requests. Browsers like IE will reset connections regularly.
|
||||||
if (_requestProcessingStatus != RequestProcessingStatus.RequestPending ||
|
if (_requestProcessingStatus != RequestProcessingStatus.RequestPending)
|
||||||
((UvException)ex.InnerException).StatusCode != Constants.ECONNRESET)
|
|
||||||
{
|
{
|
||||||
Log.RequestProcessingError(ConnectionId, ex);
|
Log.RequestProcessingError(ConnectionId, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
Log.RequestProcessingError(ConnectionId, ex);
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.LogWarning(0, ex, "Connection processing ended abnormally");
|
Log.LogWarning(0, ex, "Connection processing ended abnormally");
|
||||||
|
|
@ -207,12 +212,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Input.Reader.Complete();
|
Input.Complete();
|
||||||
// If _requestAborted is set, the connection has already been closed.
|
// If _requestAborted is set, the connection has already been closed.
|
||||||
if (Volatile.Read(ref _requestAborted) == 0)
|
if (Volatile.Read(ref _requestAborted) == 0)
|
||||||
{
|
{
|
||||||
await TryProduceInvalidRequestResponse();
|
await TryProduceInvalidRequestResponse();
|
||||||
ConnectionControl.End(ProduceEndType.SocketShutdown);
|
LifetimeControl.End(ProduceEndType.SocketShutdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
@ -6,7 +6,6 @@ using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO.Pipelines;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.IO.Pipelines;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
|
|
@ -3,11 +3,8 @@
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
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 SetTimeout(long milliseconds, TimeoutAction timeoutAction);
|
||||||
void ResetTimeout(long milliseconds, TimeoutAction timeoutAction);
|
void ResetTimeout(long milliseconds, TimeoutAction timeoutAction);
|
||||||
void CancelTimeout();
|
void CancelTimeout();
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
@ -215,9 +215,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
private void ConsumedBytes(int count)
|
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);
|
var consumed = scan.Move(scan.Start, count);
|
||||||
_context.Input.Reader.Advance(consumed, consumed);
|
_context.Input.Advance(consumed, consumed);
|
||||||
|
|
||||||
OnConsumedBytes(count);
|
OnConsumedBytes(count);
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +304,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
protected override ValueTask<ArraySegment<byte>> PeekAsync(CancellationToken cancellationToken)
|
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>>();
|
return new ValueTask<ArraySegment<byte>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var task = _context.Input.Reader.PeekAsync();
|
var task = _context.Input.PeekAsync();
|
||||||
|
|
||||||
if (task.IsCompleted)
|
if (task.IsCompleted)
|
||||||
{
|
{
|
||||||
|
|
@ -423,7 +423,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
: base(context)
|
: base(context)
|
||||||
{
|
{
|
||||||
RequestKeepAlive = keepAlive;
|
RequestKeepAlive = keepAlive;
|
||||||
_input = _context.Input.Reader;
|
_input = _context.Input;
|
||||||
_requestHeaders = headers;
|
_requestHeaders = headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6,18 +6,14 @@ using System.IO.Pipelines;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
|
||||||
using Microsoft.Extensions.Internal;
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
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 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 string _connectionId;
|
||||||
private readonly IKestrelTrace _log;
|
private readonly IKestrelTrace _log;
|
||||||
|
|
||||||
|
|
@ -26,10 +22,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
private bool _cancelled = false;
|
private bool _cancelled = false;
|
||||||
private bool _completed = false;
|
private bool _completed = false;
|
||||||
private Exception _lastWriteError;
|
|
||||||
private readonly WriteReqPool _writeReqPool;
|
private readonly IPipeWriter _pipe;
|
||||||
private readonly IPipe _pipe;
|
private readonly Frame _frame;
|
||||||
private Task _writingTask;
|
|
||||||
|
|
||||||
// https://github.com/dotnet/corefxlab/issues/1334
|
// https://github.com/dotnet/corefxlab/issues/1334
|
||||||
// Pipelines don't support multiple awaiters on flush
|
// 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 object _flushLock = new object();
|
||||||
private readonly Action _onFlushCallback;
|
private readonly Action _onFlushCallback;
|
||||||
|
|
||||||
public SocketOutput(
|
public SocketOutputProducer(IPipeWriter pipe, Frame frame, string connectionId, IKestrelTrace log)
|
||||||
IPipe pipe,
|
|
||||||
KestrelThread thread,
|
|
||||||
UvStreamHandle socket,
|
|
||||||
Connection connection,
|
|
||||||
string connectionId,
|
|
||||||
IKestrelTrace log)
|
|
||||||
{
|
{
|
||||||
_pipe = pipe;
|
_pipe = pipe;
|
||||||
// We need to have empty pipe at this moment so callback
|
_frame = frame;
|
||||||
// get's scheduled
|
|
||||||
_writingTask = StartWrites();
|
|
||||||
_thread = thread;
|
|
||||||
_socket = socket;
|
|
||||||
_connection = connection;
|
|
||||||
_connectionId = connectionId;
|
_connectionId = connectionId;
|
||||||
_log = log;
|
_log = log;
|
||||||
_writeReqPool = thread.WriteReqPool;
|
|
||||||
_onFlushCallback = OnFlush;
|
_onFlushCallback = OnFlush;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,19 +51,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
lock (_contextLock)
|
lock (_contextLock)
|
||||||
{
|
{
|
||||||
if (_socket.IsClosed)
|
|
||||||
{
|
|
||||||
_log.ConnectionDisconnectedWrite(_connectionId, buffer.Count, _lastWriteError);
|
|
||||||
|
|
||||||
return TaskCache.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_completed)
|
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;
|
return TaskCache.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
writableBuffer = _pipe.Writer.Alloc();
|
writableBuffer = _pipe.Alloc();
|
||||||
|
|
||||||
if (buffer.Count > 0)
|
if (buffer.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -103,23 +82,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
return FlushAsync(writableBuffer);
|
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)
|
private Task FlushAsync(WritableBuffer writableBuffer)
|
||||||
{
|
{
|
||||||
var awaitable = writableBuffer.FlushAsync();
|
var awaitable = writableBuffer.FlushAsync();
|
||||||
|
|
@ -164,7 +126,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
_connection.AbortAsync();
|
_frame.Abort();
|
||||||
_cancelled = true;
|
_cancelled = true;
|
||||||
return Task.FromCanceled(cancellationToken);
|
return Task.FromCanceled(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +148,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
return WriteAsync(_emptyData, cancellationToken);
|
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)
|
lock (_contextLock)
|
||||||
{
|
{
|
||||||
|
|
@ -195,106 +157,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = _pipe.Writer.Alloc();
|
var buffer = _pipe.Alloc();
|
||||||
callback(buffer, state);
|
callback(buffer, state);
|
||||||
buffer.Commit();
|
buffer.Commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartWrites()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
while (true)
|
lock (_contextLock)
|
||||||
{
|
{
|
||||||
var result = await _pipe.Reader.ReadAsync();
|
_completed = true;
|
||||||
var buffer = result.Buffer;
|
_pipe.Complete();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
|
|
||||||
|
|
@ -10,16 +9,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
|
||||||
{
|
{
|
||||||
public class ServiceContext
|
public class ServiceContext
|
||||||
{
|
{
|
||||||
public IApplicationLifetime AppLifetime { get; set; }
|
|
||||||
|
|
||||||
public IKestrelTrace Log { get; set; }
|
public IKestrelTrace Log { get; set; }
|
||||||
|
|
||||||
public IThreadPool ThreadPool { get; set; }
|
public IThreadPool ThreadPool { get; set; }
|
||||||
|
|
||||||
public Func<Frame, IHttpParser> HttpParserFactory { get; set; }
|
public Func<Frame, IHttpParser> HttpParserFactory { get; set; }
|
||||||
|
|
||||||
public Func<ConnectionContext, Frame> FrameFactory { get; set; }
|
|
||||||
|
|
||||||
public DateHeaderValueManager DateHeaderValueManager { get; set; }
|
public DateHeaderValueManager DateHeaderValueManager { get; set; }
|
||||||
|
|
||||||
public KestrelServerOptions ServerOptions { get; set; }
|
public KestrelServerOptions ServerOptions { get; set; }
|
||||||
|
|
@ -7,15 +7,16 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Hosting.Server;
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
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.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
|
@ -23,21 +24,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
{
|
{
|
||||||
public class KestrelServer : IServer
|
public class KestrelServer : IServer
|
||||||
{
|
{
|
||||||
private Stack<IDisposable> _disposables;
|
//private Stack<IDisposable> _disposables;
|
||||||
private readonly IApplicationLifetime _applicationLifetime;
|
private readonly List<ITransport> _transports = new List<ITransport>();
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IServerAddressesFeature _serverAddresses;
|
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)
|
if (options == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applicationLifetime == null)
|
if (transportFactory == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(applicationLifetime));
|
throw new ArgumentNullException(nameof(transportFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loggerFactory == null)
|
if (loggerFactory == null)
|
||||||
|
|
@ -47,7 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
|
||||||
Options = options.Value ?? new KestrelServerOptions();
|
Options = options.Value ?? new KestrelServerOptions();
|
||||||
InternalOptions = new InternalKestrelServerOptions();
|
InternalOptions = new InternalKestrelServerOptions();
|
||||||
_applicationLifetime = applicationLifetime;
|
_transportFactory = transportFactory;
|
||||||
_logger = loggerFactory.CreateLogger(typeof(KestrelServer).GetTypeInfo().Namespace);
|
_logger = loggerFactory.CreateLogger(typeof(KestrelServer).GetTypeInfo().Namespace);
|
||||||
Features = new FeatureCollection();
|
Features = new FeatureCollection();
|
||||||
_serverAddresses = new ServerAddressesFeature();
|
_serverAddresses = new ServerAddressesFeature();
|
||||||
|
|
@ -72,14 +81,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
|
||||||
ValidateOptions();
|
ValidateOptions();
|
||||||
|
|
||||||
if (_disposables != null)
|
if (_isRunning)
|
||||||
{
|
{
|
||||||
// The server has already started and/or has not been cleaned up yet
|
// The server has already started and/or has not been cleaned up yet
|
||||||
throw new InvalidOperationException("Server has already started.");
|
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);
|
var trace = new KestrelTrace(_logger);
|
||||||
|
|
||||||
IThreadPool threadPool;
|
IThreadPool threadPool;
|
||||||
|
|
@ -92,43 +101,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
threadPool = new InlineLoggingThreadPool(trace);
|
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,
|
Log = trace,
|
||||||
HttpParserFactory = frame => new KestrelHttpParser(frame.ConnectionContext.ListenerContext.ServiceContext.Log),
|
HttpParserFactory = frame => new KestrelHttpParser(frame.ServiceContext.Log),
|
||||||
ThreadPool = threadPool,
|
ThreadPool = threadPool,
|
||||||
DateHeaderValueManager = dateHeaderValueManager,
|
DateHeaderValueManager = _dateHeaderValueManager,
|
||||||
ServerOptions = Options
|
ServerOptions = Options
|
||||||
});
|
};
|
||||||
|
|
||||||
_disposables.Push(engine);
|
var connectionHandler = new ConnectionHandler<TContext>(serviceContext, application);
|
||||||
_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 listenOptions = Options.ListenOptions;
|
var listenOptions = Options.ListenOptions;
|
||||||
var hasListenOptions = listenOptions.Any();
|
var hasListenOptions = listenOptions.Any();
|
||||||
|
|
@ -137,7 +119,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
if (hasListenOptions && hasServerAddresses)
|
if (hasListenOptions && hasServerAddresses)
|
||||||
{
|
{
|
||||||
var joined = string.Join(", ", _serverAddresses.Addresses);
|
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();
|
_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.");
|
_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.
|
// "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.
|
// If StartLocalhost doesn't throw, there is at least one listener.
|
||||||
// The port cannot change for "localhost".
|
// The port cannot change for "localhost".
|
||||||
|
|
@ -181,7 +163,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
if (string.Equals(parsedAddress.Host, "localhost", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(parsedAddress.Host, "localhost", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
|
// "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.
|
// If StartLocalhost doesn't throw, there is at least one listener.
|
||||||
// The port cannot change for "localhost".
|
// The port cannot change for "localhost".
|
||||||
|
|
@ -201,18 +183,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
|
||||||
foreach (var endPoint in listenOptions)
|
foreach (var endPoint in listenOptions)
|
||||||
{
|
{
|
||||||
|
var transport = _transportFactory.Create(endPoint, connectionHandler);
|
||||||
|
_transports.Add(transport);
|
||||||
|
|
||||||
try
|
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 new IOException($"Failed to bind to address {endPoint}: address already in use.", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If requested port was "0", replace with assigned dynamic port.
|
// If requested port was "0", replace with assigned dynamic port.
|
||||||
|
|
@ -229,14 +209,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
|
||||||
public void Dispose()
|
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()
|
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)
|
if (parsedAddress.Port == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -272,20 +262,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
Scheme = parsedAddress.Scheme,
|
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);
|
throw new IOException($"Failed to bind to address {parsedAddress} on the IPv4 loopback interface: port already in use.", ex);
|
||||||
}
|
}
|
||||||
else
|
catch (AggregateException ex)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(0, $"Unable to bind to {parsedAddress} on the IPv4 loopback interface: ({uvEx.Message})");
|
_logger.LogWarning(0, $"Unable to bind to {parsedAddress} on the IPv4 loopback interface: ({ex.Message})");
|
||||||
exceptions.Add(uvEx);
|
exceptions.Add(ex.InnerException);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -295,20 +283,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
Scheme = parsedAddress.Scheme,
|
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;
|
throw new IOException($"Failed to bind to address {parsedAddress} on the IPv6 loopback interface: port already in use.", ex);
|
||||||
if (uvEx.StatusCode == Constants.EADDRINUSE)
|
}
|
||||||
{
|
catch (AggregateException ex)
|
||||||
throw new IOException($"Failed to bind to address {parsedAddress} on the IPv6 loopback interface: port already in use.", ex);
|
{
|
||||||
}
|
_logger.LogWarning(0, $"Unable to bind to {parsedAddress} on the IPv6 loopback interface: ({ex.Message})");
|
||||||
else
|
exceptions.Add(ex.InnerException);
|
||||||
{
|
|
||||||
_logger.LogWarning(0, $"Unable to bind to {parsedAddress} on the IPv6 loopback interface: ({uvEx.Message})");
|
|
||||||
exceptions.Add(uvEx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exceptions.Count == 2)
|
if (exceptions.Count == 2)
|
||||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables the Listen options callback to resolve and use services registered by the application during startup.
|
/// 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>
|
/// </summary>
|
||||||
public IServiceProvider ApplicationServices { get; set; }
|
public IServiceProvider ApplicationServices { get; set; }
|
||||||
|
|
||||||
|
|
@ -65,50 +65,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public KestrelServerLimits Limits { get; } = new KestrelServerLimits();
|
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>
|
/// <summary>
|
||||||
/// Bind to given IP address and port.
|
/// Bind to given IP address and port.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
/// The <see cref="IPEndPoint"/> to bind to.
|
/// The <see cref="IPEndPoint"/> to bind to.
|
||||||
/// Only set if the <see cref="ListenOptions"/> <see cref="Type"/> is <see cref="ListenType.IPEndPoint"/>.
|
/// Only set if the <see cref="ListenOptions"/> <see cref="Type"/> is <see cref="ListenType.IPEndPoint"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPEndPoint IPEndPoint { get; internal set; }
|
public IPEndPoint IPEndPoint { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The absolute path to a Unix domain socket to bind to.
|
/// 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>();
|
public List<IConnectionAdapter> ConnectionAdapters { get; } = new List<IConnectionAdapter>();
|
||||||
|
|
||||||
// Scheme is hopefully only a temporary measure for back compat with IServerAddressesFeature.
|
// 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()
|
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.
|
// 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.
|
// 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;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[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.
|
// 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.
|
// 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.IO;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel;
|
using Microsoft.AspNetCore.Server.Kestrel;
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<PackageTags>aspnetcore;kestrel</PackageTags>
|
<PackageTags>aspnetcore;kestrel</PackageTags>
|
||||||
<NoWarn>CS1591;$(NoWarn)</NoWarn>
|
<NoWarn>CS1591;$(NoWarn)</NoWarn>
|
||||||
|
<EnableApiCheck>false</EnableApiCheck>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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>
|
||||||
|
|
||||||
<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.
|
// 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.
|
// 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 System.IO.Pipelines;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Transport;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
public class ConnectionContext
|
public class ConnectionContext : IConnectionInformation
|
||||||
{
|
{
|
||||||
public ConnectionContext()
|
public ConnectionContext()
|
||||||
{
|
{
|
||||||
|
|
@ -21,16 +20,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
||||||
public ListenerContext ListenerContext { get; set; }
|
public ListenerContext ListenerContext { get; set; }
|
||||||
|
|
||||||
public IPipe Input { get; set; }
|
public ListenOptions ListenOptions => ListenerContext.ListenOptions;
|
||||||
|
|
||||||
public ISocketOutput Output { get; set; }
|
|
||||||
|
|
||||||
public IConnectionControl ConnectionControl { get; set; }
|
|
||||||
|
|
||||||
public IPEndPoint RemoteEndPoint { get; set; }
|
public IPEndPoint RemoteEndPoint { get; set; }
|
||||||
|
|
||||||
public IPEndPoint LocalEndPoint { 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
@ -12,12 +11,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
public class ConnectionManager
|
public class ConnectionManager
|
||||||
{
|
{
|
||||||
private readonly KestrelThread _thread;
|
private readonly KestrelThread _thread;
|
||||||
private readonly IThreadPool _threadPool;
|
|
||||||
|
|
||||||
public ConnectionManager(KestrelThread thread, IThreadPool threadPool)
|
public ConnectionManager(KestrelThread thread)
|
||||||
{
|
{
|
||||||
_thread = thread;
|
_thread = thread;
|
||||||
_threadPool = threadPool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> WalkConnectionsAndCloseAsync(TimeSpan timeout)
|
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);
|
tcs.SetResult(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
@ -16,14 +17,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
private bool _closed;
|
private bool _closed;
|
||||||
|
|
||||||
public Listener(ServiceContext serviceContext)
|
public Listener(LibuvTransportContext transportContext) : base(transportContext)
|
||||||
: base(serviceContext)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UvStreamHandle ListenSocket { get; private set; }
|
protected UvStreamHandle ListenSocket { get; private set; }
|
||||||
|
|
||||||
public IKestrelTrace Log => ServiceContext.Log;
|
public IKestrelTrace Log => TransportContext.Log;
|
||||||
|
|
||||||
public Task StartAsync(
|
public Task StartAsync(
|
||||||
ListenOptions listenOptions,
|
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 System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Networking;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
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
|
// 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 }) });
|
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();
|
_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);
|
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