This repository has been archived by the owner on Dec 18, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 528
Transport agnostic kestrel refactoring #1551
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
07e7296
Create Libuv and Core projects
halter73 b953daa
Add transport interfaces
halter73 cd39ec4
WIP
halter73 d66b3f2
WIP 2
halter73 8238bac
WIP 3
halter73 4c5b085
WIP 4
halter73 b4c60a9
WIP 5
halter73 2a8ecd6
WIP 6
halter73 0567559
WIP 7
halter73 ef3b033
WIP 8
halter73 a90b1e7
WIP 9
halter73 af243e0
WIP 10
halter73 0a86f0c
WIP 11
halter73 236fea5
WIP 12
halter73 9c2f5d9
WIP 13
halter73 ba1b4c1
WIP 14
halter73 45fd2a5
WIP 15
halter73 86aa930
WIP 16
halter73 711faa4
WIP 16
halter73 da3c028
WIP 17
halter73 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
1 change: 0 additions & 1 deletion
1
...ver.Kestrel/Adapter/IConnectionAdapter.cs → ...estrel.Core/Adapter/IConnectionAdapter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
128 changes: 128 additions & 0 deletions
128
src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/ConnectionHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's weird to have this on the same level as kestrel. This needs to move to options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That just means that hosting services don't get injected into the ITransportFactory for free. For example,
LibuvTransportFactory
depends onIOptions<LibuvTransportOptions>,
IApplicationLifetimeand
ILoggerFactory`.Do you want to use ActivatorUtilities to activate a registered ITransportFactory type on KestrelServerOptions? That seems really gross.