From 2f3a00625a8363656e776cbf6011e167933902db Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Wed, 30 Sep 2015 11:15:09 -0700 Subject: [PATCH] Initial work to support HTTPS using SslStream - Add extension method "UseKestrelHttps" to IApplicationBuilder --- KestrelHttpServer.sln | 7 ++ samples/SampleApp/project.json | 8 +- .../HttpsApplicationBuilderExtensions.cs | 29 ++++++ .../HttpsConnectionFilter.cs | 44 +++++++++ ...icrosoft.AspNet.Server.Kestrel.Https.xproj | 20 ++++ .../project.json | 14 +++ .../Filter/ConnectionFilterContext.cs | 13 +++ .../Filter/FilteredStreamAdapter.cs | 63 ++++++++++++ .../Filter/IConnectionFilter.cs | 12 +++ .../Filter/LibuvStream.cs | 91 +++++++++++++++++ .../Filter/NoOpConnectionFilter.cs | 17 ++++ .../Filter/SocketInputStream.cs | 99 +++++++++++++++++++ .../Filter/StreamSocketOutput.cs | 35 +++++++ .../Http/Connection.cs | 74 ++++++++++++-- .../Http/ISocketOutput.cs | 1 - .../Http/Listener.cs | 9 +- .../Http/ListenerContext.cs | 5 +- .../Http/ListenerPrimary.cs | 6 +- .../Http/ListenerSecondary.cs | 2 + .../Http/MessageBody.cs | 58 +++-------- .../Http/PipeListener.cs | 4 +- .../Http/PipeListenerPrimary.cs | 4 +- .../Http/SocketInputExtensions.cs | 33 +++++++ .../Http/SocketOutput.cs | 38 +++---- .../Http/TcpListener.cs | 4 +- .../Http/TcpListenerPrimary.cs | 4 +- .../IKestrelServerInformation.cs | 4 + .../KestrelEngine.cs | 16 ++- .../KestrelServerInformation.cs | 3 + .../Networking/UvTcpHandle.cs | 12 +-- .../ServerAddress.cs | 19 ++++ .../ServerFactory.cs | 7 +- .../ServiceContext.cs | 4 + .../CreateIPEndpointTests.cs | 3 +- .../EngineTests.cs | 6 +- .../MultipleLoopTests.cs | 3 +- .../NetworkingTests.cs | 12 ++- .../SocketOutputTests.cs | 4 +- .../TestServer.cs | 4 +- 39 files changed, 667 insertions(+), 124 deletions(-) create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/Microsoft.AspNet.Server.Kestrel.Https.xproj create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/project.json create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Filter/SocketInputStream.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln index 8b0d78f607..f2da75a88c 100644 --- a/KestrelHttpServer.sln +++ b/KestrelHttpServer.sln @@ -33,6 +33,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.StandardsPolice", EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Server.Kestrel.LibuvCopier", "tools\Microsoft.AspNet.Server.Kestrel.LibuvCopier\Microsoft.AspNet.Server.Kestrel.LibuvCopier.xproj", "{8CBA6FE3-3CC9-4420-8AA3-123E983734C2}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Server.Kestrel.Https", "src\Microsoft.AspNet.Server.Kestrel.Https\Microsoft.AspNet.Server.Kestrel.Https.xproj", "{5F64B3C3-0C2E-431A-B820-A81BBFC863DA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -67,6 +69,10 @@ Global {8CBA6FE3-3CC9-4420-8AA3-123E983734C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {8CBA6FE3-3CC9-4420-8AA3-123E983734C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {8CBA6FE3-3CC9-4420-8AA3-123E983734C2}.Release|Any CPU.Build.0 = Release|Any CPU + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -79,5 +85,6 @@ Global {BD2D4D29-1BD9-40D0-BB31-337D5416B63C} = {327F7880-D9AF-46BD-B45C-3B7E34A01DFD} {82295647-7C1C-4671-BAB6-0FEF58F949EC} = {327F7880-D9AF-46BD-B45C-3B7E34A01DFD} {8CBA6FE3-3CC9-4420-8AA3-123E983734C2} = {327F7880-D9AF-46BD-B45C-3B7E34A01DFD} + {5F64B3C3-0C2E-431A-B820-A81BBFC863DA} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} EndGlobalSection EndGlobal diff --git a/samples/SampleApp/project.json b/samples/SampleApp/project.json index 2a45655ae9..f4b23cfc64 100644 --- a/samples/SampleApp/project.json +++ b/samples/SampleApp/project.json @@ -5,7 +5,11 @@ "Microsoft.Extensions.Logging.Console": "1.0.0-*" }, "frameworks": { - "dnx451": { }, + "dnx451": { + "dependencies": { + "Microsoft.AspNet.Server.Kestrel.Https": "1.0.0-*" + } + }, "dnxcore50": { "dependencies": { "System.Console": "4.0.0-beta-*" @@ -16,6 +20,6 @@ "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel", "run": "Microsoft.AspNet.Server.Kestrel", "run-socket": "Microsoft.AspNet.Server.Kestrel --server.urls http://unix:/tmp/kestrel-test.sock", - "kestrel": "Microsoft.AspNet.Server.Kestrel" + "kestrel": "Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5000;https://localhost:5001" } } diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..844435c152 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http.Features; +using Microsoft.AspNet.Server.Kestrel.Filter; + +namespace Microsoft.AspNet.Server.Kestrel.Https +{ + public static class HttpsApplicationBuilderExtensions + { + public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert) + { + var serverInfo = app.ServerFeatures.Get(); + + if (serverInfo == null) + { + return app; + } + + var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter(); + + serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, prevFilter); + + return app; + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs new file mode 100644 index 0000000000..08cad7161d --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Filter; + +namespace Microsoft.AspNet.Server.Kestrel.Https +{ + public class HttpsConnectionFilter : IConnectionFilter + { + private readonly X509Certificate2 _cert; + private readonly IConnectionFilter _previous; + + public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) + { + if (cert == null) + { + throw new ArgumentNullException(nameof(cert)); + } + if (previous == null) + { + throw new ArgumentNullException(nameof(previous)); + } + + _cert = cert; + _previous = previous; + } + + public async Task OnConnection(ConnectionFilterContext context) + { + await _previous.OnConnection(context); + + if (string.Equals(context.Address.Scheme, "https", StringComparison.OrdinalIgnoreCase)) + { + var sslStream = new SslStream(context.Connection); + await sslStream.AuthenticateAsServerAsync(_cert); + context.Connection = sslStream; + } + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/Microsoft.AspNet.Server.Kestrel.Https.xproj b/src/Microsoft.AspNet.Server.Kestrel.Https/Microsoft.AspNet.Server.Kestrel.Https.xproj new file mode 100644 index 0000000000..4ae185e402 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/Microsoft.AspNet.Server.Kestrel.Https.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 5f64b3c3-0c2e-431a-b820-a81bbfc863da + Microsoft.AspNet.Server.Kestrel.Https + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/project.json b/src/Microsoft.AspNet.Server.Kestrel.Https/project.json new file mode 100644 index 0000000000..2fc29ec1db --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/project.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0-*", + "description": "Adds HTTPS support to Kestrel", + "repository": { + "type": "git", + "url": "git://github.com/aspnet/kestrelhttpserver" + }, + "dependencies": { + "Microsoft.AspNet.Server.Kestrel": "1.0.0-*" + }, + "frameworks": { + "dnx451": { } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs new file mode 100644 index 0000000000..937e92bec3 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; + +namespace Microsoft.AspNet.Server.Kestrel.Filter +{ + public class ConnectionFilterContext + { + public ServerAddress Address { get; set; } + public Stream Connection { get; set; } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs new file mode 100644 index 0000000000..2e0872f704 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs @@ -0,0 +1,63 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Http; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNet.Server.Kestrel.Filter +{ + public class FilteredStreamAdapter + { + private readonly Stream _filteredStream; + private readonly Stream _socketInputStream; + private readonly IKestrelTrace _log; + + public FilteredStreamAdapter( + Stream filteredStream, + MemoryPool2 memory, + IKestrelTrace logger) + { + SocketInput = new SocketInput(memory); + SocketOutput = new StreamSocketOutput(filteredStream); + + _log = logger; + _filteredStream = filteredStream; + _socketInputStream = new SocketInputStream(SocketInput); + + _filteredStream.CopyToAsync(_socketInputStream).ContinueWith((task, state) => + { + ((FilteredStreamAdapter)state).OnStreamClose(task); + }, this); + } + + public SocketInput SocketInput { get; private set; } + + public ISocketOutput SocketOutput { get; private set; } + + private void OnStreamClose(Task copyAsyncTask) + { + if (copyAsyncTask.IsFaulted) + { + _log.LogError("FilteredStreamAdapter.CopyToAsync", copyAsyncTask.Exception); + } + else if (copyAsyncTask.IsCanceled) + { + _log.LogError("FilteredStreamAdapter.CopyToAsync canceled."); + } + + try + { + _filteredStream.Dispose(); + _socketInputStream.Dispose(); + } + catch (Exception ex) + { + _log.LogError("FilteredStreamAdapter.OnStreamClose", ex); + } + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs new file mode 100644 index 0000000000..accaa3b9d9 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs @@ -0,0 +1,12 @@ +// 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.AspNet.Server.Kestrel.Filter +{ + public interface IConnectionFilter + { + Task OnConnection(ConnectionFilterContext context); + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs new file mode 100644 index 0000000000..16f3ce2c1b --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs @@ -0,0 +1,91 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Http; + +namespace Microsoft.AspNet.Server.Kestrel.Filter +{ + public class LibuvStream : Stream + { + private readonly SocketInput _input; + private readonly ISocketOutput _output; + + public LibuvStream(SocketInput input, ISocketOutput output) + { + _input = input; + _output = output; + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length + { + get + { + throw new NotSupportedException(); + } + } + + public override long Position + { + get + { + throw new NotSupportedException(); + } + set + { + throw new NotSupportedException(); + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return ReadAsync(new ArraySegment(buffer, offset, count)).GetAwaiter().GetResult(); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return ReadAsync(new ArraySegment(buffer, offset, count)); + } + + public override void Write(byte[] buffer, int offset, int count) + { + var segment = new ArraySegment(buffer, offset, count); + _output.Write(segment); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token) + { + var segment = new ArraySegment(buffer, offset, count); + return _output.WriteAsync(segment); + } + + public override void Flush() + { + // No-op since writes are immediate. + } + + private Task ReadAsync(ArraySegment buffer) + { + return _input.ReadAsync(buffer); + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs new file mode 100644 index 0000000000..65bb9e99b7 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs @@ -0,0 +1,17 @@ +// 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.AspNet.Server.Kestrel.Filter +{ + public class NoOpConnectionFilter : IConnectionFilter + { + private static Task _empty = Task.FromResult(null); + + public Task OnConnection(ConnectionFilterContext context) + { + return _empty; + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/SocketInputStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/SocketInputStream.cs new file mode 100644 index 0000000000..43f0d0e704 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/SocketInputStream.cs @@ -0,0 +1,99 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Http; + +namespace Microsoft.AspNet.Server.Kestrel.Filter +{ + /// + /// This is a write-only stream that copies what is written into a + /// object. This is used as an argument to + /// so input filtered by a + /// ConnectionFilter (e.g. SslStream) can be consumed by . + /// + public class SocketInputStream : Stream + { + private static Task _emptyTask = Task.FromResult(null); + private static byte[] _emptyBuffer = new byte[0]; + + private readonly SocketInput _socketInput; + + public SocketInputStream(SocketInput socketInput) + { + _socketInput = socketInput; + } + + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length + { + get + { + throw new NotSupportedException(); + } + } + + public override long Position + { + get + { + throw new NotSupportedException(); + } + + set + { + throw new NotSupportedException(); + } + } + + public override void Flush() + { + // No-op + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + var inputBuffer = _socketInput.IncomingStart(count); + + Buffer.BlockCopy(buffer, offset, inputBuffer.Data.Array, inputBuffer.Data.Offset, count); + + _socketInput.IncomingComplete(count, error: null); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token) + { + Write(buffer, offset, count); + return _emptyTask; + } + + protected override void Dispose(bool disposing) + { + // Close _socketInput with a 0-length write. + Write(_emptyBuffer, 0, 0); + base.Dispose(disposing); + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs new file mode 100644 index 0000000000..f5470ae728 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Http; + +namespace Microsoft.AspNet.Server.Kestrel.Filter +{ + public class StreamSocketOutput : ISocketOutput + { + private static readonly Task _emptyTask = Task.FromResult(null); + + private readonly Stream _outputStream; + + public StreamSocketOutput(Stream outputStream) + { + _outputStream = outputStream; + } + + void ISocketOutput.Write(ArraySegment buffer, bool immediate) + { + _outputStream.Write(buffer.Array, buffer.Offset, buffer.Count); + } + + Task ISocketOutput.WriteAsync(ArraySegment buffer, bool immediate, CancellationToken cancellationToken) + { + // TODO: Use _outputStream.WriteAsync + _outputStream.Write(buffer.Array, buffer.Offset, buffer.Count); + return _emptyTask; + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index fadcce592b..48dd7de7a1 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -4,6 +4,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; using Microsoft.Extensions.Logging; @@ -18,9 +19,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private static long _lastConnectionId; private readonly UvStreamHandle _socket; - private readonly Frame _frame; + private Frame _frame; + private ConnectionFilterContext _filterContext; private readonly long _connectionId; + private readonly SocketInput _rawSocketInput; + private readonly SocketOutput _rawSocketOutput; + private readonly object _stateLock = new object(); private ConnectionState _connectionState; @@ -31,16 +36,67 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _connectionId = Interlocked.Increment(ref _lastConnectionId); - SocketInput = new SocketInput(Memory2); - SocketOutput = new SocketOutput(Thread, _socket, _connectionId, Log); - _frame = new Frame(this); + _rawSocketInput = new SocketInput(Memory2); + _rawSocketOutput = new SocketOutput(Thread, _socket, _connectionId, Log); } public void Start() { Log.ConnectionStart(_connectionId); - _frame.Start(); + + // Start socket prior to applying the ConnectionFilter _socket.ReadStart(_allocCallback, _readCallback, this); + + // Don't initialize _frame until SocketInput and SocketOutput are set to their final values. + if (ConnectionFilter == null) + { + SocketInput = _rawSocketInput; + SocketOutput = _rawSocketOutput; + + _frame = new Frame(this); + _frame.Start(); + } + else + { + var libuvStream = new LibuvStream(_rawSocketInput, _rawSocketOutput); + + _filterContext = new ConnectionFilterContext + { + Connection = libuvStream, + Address = ServerAddress + }; + + ConnectionFilter.OnConnection(_filterContext).ContinueWith((task, state) => + { + var connection = (Connection)state; + + if (task.IsFaulted) + { + connection.Log.LogError("ConnectionFilter.OnConnection", task.Exception); + ConnectionControl.End(ProduceEndType.SocketDisconnect); + } + else if (task.IsCanceled) + { + connection.Log.LogError("ConnectionFilter.OnConnection Canceled"); + ConnectionControl.End(ProduceEndType.SocketDisconnect); + } + else + { + connection.ApplyConnectionFilter(); + } + }, this); + } + } + + private void ApplyConnectionFilter() + { + var filteredStreamAdapter = new FilteredStreamAdapter(_filterContext.Connection, Memory2, Log); + + SocketInput = filteredStreamAdapter.SocketInput; + SocketOutput = filteredStreamAdapter.SocketOutput; + + _frame = new Frame(this); + _frame.Start(); } private static Libuv.uv_buf_t AllocCallback(UvStreamHandle handle, int suggestedSize, object state) @@ -50,7 +106,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private Libuv.uv_buf_t OnAlloc(UvStreamHandle handle, int suggestedSize) { - var result = SocketInput.IncomingStart(2048); + var result = _rawSocketInput.IncomingStart(2048); return handle.Libuv.buf_init( result.DataPtr, @@ -78,7 +134,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http Log.ConnectionReadFin(_connectionId); } - SocketInput.IncomingComplete(readCount, errorDone ? error : null); + _rawSocketInput.IncomingComplete(readCount, errorDone ? error : null); } void IConnectionControl.Pause() @@ -107,7 +163,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _connectionState = ConnectionState.Shutdown; Log.ConnectionWriteFin(_connectionId); - SocketOutput.End(endType); + _rawSocketOutput.End(endType); break; case ProduceEndType.ConnectionKeepAlive: if (_connectionState != ConnectionState.Open) @@ -125,7 +181,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _connectionState = ConnectionState.Disconnected; Log.ConnectionDisconnect(_connectionId); - SocketOutput.End(endType); + _rawSocketOutput.End(endType); break; } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs index 00c567d571..4edffa3055 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs @@ -14,6 +14,5 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { void Write(ArraySegment buffer, bool immediate = true); Task WriteAsync(ArraySegment buffer, bool immediate = true, CancellationToken cancellationToken = default(CancellationToken)); - void End(ProduceEndType endType); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs index a6900f6085..8c7e2b6c05 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs @@ -21,12 +21,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http protected UvStreamHandle ListenSocket { get; private set; } public Task StartAsync( - string scheme, - string host, - int port, + ServerAddress address, KestrelThread thread, Func application) { + ServerAddress = address; Thread = thread; Application = application; @@ -35,7 +34,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { try { - ListenSocket = CreateListenSocket(host, port); + ListenSocket = CreateListenSocket(); tcs.SetResult(0); } catch (Exception ex) @@ -49,7 +48,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http /// /// Creates the socket used to listen for incoming connections /// - protected abstract UvStreamHandle CreateListenSocket(string host, int port); + protected abstract UvStreamHandle CreateListenSocket(); protected static void ConnectionCallback(UvStreamHandle stream, int status, Exception error, object state) { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs index 2f6201016b..9e5a8e50ed 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Infrastructure; namespace Microsoft.AspNet.Server.Kestrel.Http @@ -23,13 +24,15 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public ListenerContext(ListenerContext listenerContext) : base(listenerContext) { + ServerAddress = listenerContext.ServerAddress; Thread = listenerContext.Thread; Application = listenerContext.Application; - Memory = listenerContext.Memory; Memory2 = listenerContext.Memory2; Log = listenerContext.Log; } + public ServerAddress ServerAddress { get; set; } + public KestrelThread Thread { get; set; } public Func Application { get; set; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs index 15b0e904e8..41aeeb3bfa 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs @@ -31,13 +31,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public async Task StartAsync( string pipeName, - string scheme, - string host, - int port, + ServerAddress address, KestrelThread thread, Func application) { - await StartAsync(scheme, host, port, thread, application).ConfigureAwait(false); + await StartAsync(address, thread, application).ConfigureAwait(false); await Thread.PostAsync(_ => { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs index 37c9c3cc70..1f4e34a4bf 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs @@ -24,9 +24,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public Task StartAsync( string pipeName, + ServerAddress address, KestrelThread thread, Func application) { + ServerAddress = address; Thread = thread; Application = application; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs index e60647700e..c6afad0e77 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs @@ -106,27 +106,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { } - public override async Task ReadAsyncImplementation(ArraySegment buffer, CancellationToken cancellationToken) + public override Task ReadAsyncImplementation(ArraySegment buffer, CancellationToken cancellationToken) { - var input = _context.SocketInput; - while (true) - { - await input; - - var begin = input.ConsumingStart(); - int actual; - var end = begin.CopyTo(buffer.Array, buffer.Offset, buffer.Count, out actual); - input.ConsumingComplete(end, end); - - if (actual != 0) - { - return actual; - } - if (input.RemoteIntakeFin) - { - return 0; - } - } + return _context.SocketInput.ReadAsync(buffer); } } @@ -147,30 +129,22 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { var input = _context.SocketInput; - while (true) + var limit = Math.Min(buffer.Count, _inputLength); + if (limit == 0) { - var limit = Math.Min(buffer.Count, _inputLength); - if (limit == 0) - { - return 0; - } - - await input; - - var begin = input.ConsumingStart(); - int actual; - var end = begin.CopyTo(buffer.Array, buffer.Offset, limit, out actual); - _inputLength -= actual; - input.ConsumingComplete(end, end); - if (actual != 0) - { - return actual; - } - if (input.RemoteIntakeFin) - { - throw new InvalidDataException("Unexpected end of request content"); - } + return 0; } + + var limitedBuffer = new ArraySegment(buffer.Array, buffer.Offset, limit); + var actual = await _context.SocketInput.ReadAsync(limitedBuffer); + _inputLength -= actual; + + if (actual == 0) + { + throw new InvalidDataException("Unexpected end of request content"); + } + + return actual; } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/PipeListener.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/PipeListener.cs index 3e5a4a5d56..4c3e35f799 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/PipeListener.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/PipeListener.cs @@ -20,11 +20,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http /// /// Creates the socket used to listen for incoming connections /// - protected override UvStreamHandle CreateListenSocket(string host, int port) + protected override UvStreamHandle CreateListenSocket() { var socket = new UvPipeHandle(Log); socket.Init(Thread.Loop, false); - socket.Bind(host); + socket.Bind(ServerAddress.UnixPipePath); socket.Listen(Constants.ListenBacklog, ConnectionCallback, this); return socket; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/PipeListenerPrimary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/PipeListenerPrimary.cs index a9ef09ce75..1f7eb285e9 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/PipeListenerPrimary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/PipeListenerPrimary.cs @@ -20,11 +20,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http /// /// Creates the socket used to listen for incoming connections /// - protected override UvStreamHandle CreateListenSocket(string host, int port) + protected override UvStreamHandle CreateListenSocket() { var socket = new UvPipeHandle(Log); socket.Init(Thread.Loop, false); - socket.Bind(host); + socket.Bind(ServerAddress.UnixPipePath); socket.Listen(Constants.ListenBacklog, ConnectionCallback, this); return socket; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs new file mode 100644 index 0000000000..9c5d690707 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Server.Kestrel.Http +{ + public static class SocketInputExtensions + { + public static async Task ReadAsync(this SocketInput input, ArraySegment buffer) + { + while (true) + { + await input; + + var begin = input.ConsumingStart(); + int actual; + var end = begin.CopyTo(buffer.Array, buffer.Offset, buffer.Count, out actual); + input.ConsumingComplete(end, end); + + if (actual != 0) + { + return actual; + } + if (input.RemoteIntakeFin) + { + return 0; + } + } + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index 0eed51d7d3..d0d218ac5a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -114,6 +114,25 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } + public void End(ProduceEndType endType) + { + switch (endType) + { + case ProduceEndType.SocketShutdownSend: + Write(default(ArraySegment), (error, state, calledInline) => { }, null, + immediate: true, + socketShutdownSend: true, + socketDisconnect: false); + break; + case ProduceEndType.SocketDisconnect: + Write(default(ArraySegment), (error, state, calledInline) => { }, null, + immediate: true, + socketShutdownSend: false, + socketDisconnect: true); + break; + } + } + private void ScheduleWrite() { _thread.Post(_this => _this.WriteAllPending(), this); @@ -295,25 +314,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return tcs.Task; } - void ISocketOutput.End(ProduceEndType endType) - { - switch (endType) - { - case ProduceEndType.SocketShutdownSend: - Write(default(ArraySegment), (error, state, calledInline) => { }, null, - immediate: true, - socketShutdownSend: true, - socketDisconnect: false); - break; - case ProduceEndType.SocketDisconnect: - Write(default(ArraySegment), (error, state, calledInline) => { }, null, - immediate: true, - socketShutdownSend: false, - socketDisconnect: true); - break; - } - } - private class CallbackContext { // callback(error, state, calledInline) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/TcpListener.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/TcpListener.cs index 4d0e250adf..b09d86a43c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/TcpListener.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/TcpListener.cs @@ -21,11 +21,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http /// /// Creates the socket used to listen for incoming connections /// - protected override UvStreamHandle CreateListenSocket(string host, int port) + protected override UvStreamHandle CreateListenSocket() { var socket = new UvTcpHandle(Log); socket.Init(Thread.Loop, Thread.QueueCloseHandle); - socket.Bind(host, port); + socket.Bind(ServerAddress); socket.Listen(Constants.ListenBacklog, ConnectionCallback, this); return socket; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/TcpListenerPrimary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/TcpListenerPrimary.cs index bc46f5bd9a..700e888195 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/TcpListenerPrimary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/TcpListenerPrimary.cs @@ -21,11 +21,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http /// /// Creates the socket used to listen for incoming connections /// - protected override UvStreamHandle CreateListenSocket(string host, int port) + protected override UvStreamHandle CreateListenSocket() { var socket = new UvTcpHandle(Log); socket.Init(Thread.Loop, Thread.QueueCloseHandle); - socket.Bind(host, port); + socket.Bind(ServerAddress); socket.Listen(Constants.ListenBacklog, ConnectionCallback, this); return socket; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/IKestrelServerInformation.cs b/src/Microsoft.AspNet.Server.Kestrel/IKestrelServerInformation.cs index b9bd5641f9..5e90ebdfc7 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/IKestrelServerInformation.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/IKestrelServerInformation.cs @@ -1,10 +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.AspNet.Server.Kestrel.Filter; + namespace Microsoft.AspNet.Server.Kestrel { public interface IKestrelServerInformation { int ThreadCount { get; set; } + + IConnectionFilter ConnectionFilter { get; set; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs b/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs index f65cafb855..e78d6c7e9f 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs @@ -98,15 +98,11 @@ namespace Microsoft.AspNet.Server.Kestrel Threads.Clear(); } - public IDisposable CreateServer(string scheme, string host, int port, Func application) + public IDisposable CreateServer(ServerAddress address, Func application) { var listeners = new List(); - var usingPipes = host.StartsWith(Constants.UnixPipeHostPrefix); - if (usingPipes) - { - // Subtract one because we want to include the '/' character that starts the path. - host = host.Substring(Constants.UnixPipeHostPrefix.Length - 1); - } + + var usingPipes = address.IsUnixPipe; try { @@ -123,7 +119,7 @@ namespace Microsoft.AspNet.Server.Kestrel (Listener) new PipeListener(this) : new TcpListener(this); listeners.Add(listener); - listener.StartAsync(scheme, host, port, thread, application).Wait(); + listener.StartAsync(address, thread, application).Wait(); } else if (first) { @@ -132,7 +128,7 @@ namespace Microsoft.AspNet.Server.Kestrel : new TcpListenerPrimary(this); listeners.Add(listener); - listener.StartAsync(pipeName, scheme, host, port, thread, application).Wait(); + listener.StartAsync(pipeName, address, thread, application).Wait(); } else { @@ -140,7 +136,7 @@ namespace Microsoft.AspNet.Server.Kestrel ? (ListenerSecondary) new PipeListenerSecondary(this) : new TcpListenerSecondary(this); listeners.Add(listener); - listener.StartAsync(pipeName, thread, application).Wait(); + listener.StartAsync(pipeName, address, thread, application).Wait(); } first = false; diff --git a/src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs b/src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs index a6ca56c1ee..3f50057c5d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Server.Features; +using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.Extensions.Configuration; namespace Microsoft.AspNet.Server.Kestrel @@ -14,6 +15,8 @@ namespace Microsoft.AspNet.Server.Kestrel public int ThreadCount { get; set; } + public IConnectionFilter ConnectionFilter { get; set; } + public void Initialize(IConfiguration configuration) { var urls = configuration["server.urls"] ?? string.Empty; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs index 55681bfbe5..c5d4e25e63 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs @@ -33,9 +33,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking _uv.tcp_init(loop, this); } - public void Bind(string host, int port) + public void Bind(ServerAddress address) { - var endpoint = CreateIPEndpoint(host, port); + var endpoint = CreateIPEndpoint(address); Libuv.sockaddr addr; var addressText = endpoint.Address.ToString(); @@ -65,14 +65,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking /// Returns an for the given host an port. /// If the host parameter isn't "localhost" or an IP address, use IPAddress.Any. /// - public static IPEndPoint CreateIPEndpoint(string host, int port) + public static IPEndPoint CreateIPEndpoint(ServerAddress address) { // TODO: IPv6 support IPAddress ip; - if (!IPAddress.TryParse(host, out ip)) + if (!IPAddress.TryParse(address.Host, out ip)) { - if (string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(address.Host, "localhost", StringComparison.OrdinalIgnoreCase)) { ip = IPAddress.Loopback; } @@ -82,7 +82,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking } } - return new IPEndPoint(ip, port); + return new IPEndPoint(ip, address.Port); } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/ServerAddress.cs b/src/Microsoft.AspNet.Server.Kestrel/ServerAddress.cs index 4599e0a061..7f910ce959 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServerAddress.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServerAddress.cs @@ -2,6 +2,7 @@ // 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.Globalization; using Microsoft.AspNet.Server.Kestrel.Infrastructure; @@ -14,6 +15,24 @@ namespace Microsoft.AspNet.Server.Kestrel public int Port { get; private set; } public string Scheme { get; private set; } + public bool IsUnixPipe + { + get + { + return Host.StartsWith(Constants.UnixPipeHostPrefix); + } + } + + public string UnixPipePath + { + get + { + Debug.Assert(IsUnixPipe); + + return Host.Substring(Constants.UnixPipeHostPrefix.Length - 1); + } + } + public override string ToString() { return Scheme.ToLowerInvariant() + "://" + Host.ToLowerInvariant() + ":" + Port.ToString(CultureInfo.InvariantCulture) + Path.ToLowerInvariant(); diff --git a/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs b/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs index f1fc15c88e..ed441bc95d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs @@ -59,7 +59,8 @@ namespace Microsoft.AspNet.Server.Kestrel { AppShutdown = _appShutdownService, Log = new KestrelTrace(_logger), - DateHeaderValueManager = dateHeaderValueManager + DateHeaderValueManager = dateHeaderValueManager, + ConnectionFilter = information.ConnectionFilter }); disposables.Push(engine); @@ -86,9 +87,7 @@ namespace Microsoft.AspNet.Server.Kestrel { atLeastOneListener = true; disposables.Push(engine.CreateServer( - parsedAddress.Scheme, - parsedAddress.Host, - parsedAddress.Port, + parsedAddress, async frame => { var request = new ServerRequest(frame); diff --git a/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs b/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs index 8a29d2e380..317cfcf274 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Http; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Dnx.Runtime; @@ -20,6 +21,7 @@ namespace Microsoft.AspNet.Server.Kestrel Memory = context.Memory; Log = context.Log; DateHeaderValueManager = context.DateHeaderValueManager; + ConnectionFilter = context.ConnectionFilter; } public IApplicationShutdown AppShutdown { get; set; } @@ -29,5 +31,7 @@ namespace Microsoft.AspNet.Server.Kestrel public IKestrelTrace Log { get; set; } public DateHeaderValueManager DateHeaderValueManager { get; set; } + + public IConnectionFilter ConnectionFilter { get; set; } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/CreateIPEndpointTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/CreateIPEndpointTests.cs index 716a528721..5cc5c2345c 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/CreateIPEndpointTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/CreateIPEndpointTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net; +using Microsoft.AspNet.Server.Kestrel; using Microsoft.AspNet.Server.Kestrel.Networking; using Xunit; @@ -16,7 +17,7 @@ namespace Microsoft.AspNet.Server.KestrelTests public void CorrectIPEndpointsAreCreated(string host, string expectedAddress) { // "0.0.0.0" is IPAddress.Any - var endpoint = UvTcpHandle.CreateIPEndpoint(host, 5000); + var endpoint = UvTcpHandle.CreateIPEndpoint(ServerAddress.FromUrl($"http://{host}:5000/")); Assert.NotNull(endpoint); Assert.Equal(IPAddress.Parse(expectedAddress), endpoint.Address); Assert.Equal(5000, endpoint.Port); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs index 0d7ad85fae..10424fbc54 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs @@ -88,7 +88,8 @@ namespace Microsoft.AspNet.Server.KestrelTests { var engine = new KestrelEngine(LibraryManager, new TestServiceContext()); engine.Start(1); - var started = engine.CreateServer("http", "localhost", 54321, App); + var address = ServerAddress.FromUrl("http://localhost:54321/"); + var started = engine.CreateServer(address, App); started.Dispose(); engine.Dispose(); } @@ -99,7 +100,8 @@ namespace Microsoft.AspNet.Server.KestrelTests { var engine = new KestrelEngine(LibraryManager, new TestServiceContext()); engine.Start(1); - var started = engine.CreateServer("http", "localhost", 54321, App); + var address = ServerAddress.FromUrl("http://localhost:54321/"); + var started = engine.CreateServer(address, App); Console.WriteLine("Started"); var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs index c8b8a1de04..224f90f270 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs @@ -166,7 +166,8 @@ namespace Microsoft.AspNet.Server.KestrelTests var serverListenTcp = new UvTcpHandle(_logger); serverListenTcp.Init(loop); - serverListenTcp.Bind("0.0.0.0", 54321); + var address = ServerAddress.FromUrl("http://localhost:54321/"); + serverListenTcp.Bind(address); serverListenTcp.Listen(128, (_1, status, error, _2) => { var serverConnectionTcp = new UvTcpHandle(_logger); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs index 8d9eab7df7..9e4635f175 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs @@ -81,7 +81,8 @@ namespace Microsoft.AspNet.Server.KestrelTests loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop); - tcp.Bind("localhost", 0); + var address = ServerAddress.FromUrl("http://localhost:0/"); + tcp.Bind(address); tcp.Dispose(); loop.Run(); loop.Dispose(); @@ -95,7 +96,8 @@ namespace Microsoft.AspNet.Server.KestrelTests loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop); - tcp.Bind("localhost", 54321); + var address = ServerAddress.FromUrl("http://localhost:54321/"); + tcp.Bind(address); tcp.Listen(10, (stream, status, error, state) => { var tcp2 = new UvTcpHandle(_logger); @@ -132,7 +134,8 @@ namespace Microsoft.AspNet.Server.KestrelTests loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop); - tcp.Bind("localhost", 54321); + var address = ServerAddress.FromUrl("http://localhost:54321/"); + tcp.Bind(address); tcp.Listen(10, (_, status, error, state) => { Console.WriteLine("Connected"); @@ -188,7 +191,8 @@ namespace Microsoft.AspNet.Server.KestrelTests loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop); - tcp.Bind("localhost", 54321); + var address = ServerAddress.FromUrl("http://localhost:54321/"); + tcp.Bind(address); tcp.Listen(10, (_, status, error, state) => { Console.WriteLine("Connected"); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs index b636cbf5cd..8e0425ce52 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs @@ -37,7 +37,7 @@ namespace Microsoft.AspNet.Server.KestrelTests kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; - var socket = new MockSocket(kestrelThread.Loop.ThreadId, new KestrelTrace(new TestKestrelTrace())); + var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var socketOutput = new SocketOutput(kestrelThread, socket, 0, trace); @@ -82,7 +82,7 @@ namespace Microsoft.AspNet.Server.KestrelTests kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; - var socket = new MockSocket(kestrelThread.Loop.ThreadId, new KestrelTrace(new TestKestrelTrace())); + var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var socketOutput = new SocketOutput(kestrelThread, socket, 0, trace); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs index 7ad0141810..667ecdd6c7 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs @@ -52,9 +52,7 @@ namespace Microsoft.AspNet.Server.KestrelTests new TestServiceContext()); _engine.Start(1); _server = _engine.CreateServer( - "http", - "localhost", - 54321, + ServerAddress.FromUrl("http://localhost:54321/"), app); }