This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

Initial work to support HTTPS using SslStream

- Add extension method "UseKestrelHttps" to IApplicationBuilder
  • Loading branch information...
halter73 committed Sep 30, 2015
1 parent 49451fb commit 2f3a00625a8363656e776cbf6011e167933902db
Showing with 665 additions and 122 deletions.
  1. +7 −0 KestrelHttpServer.sln
  2. +6 −2 samples/SampleApp/project.json
  3. +29 −0 src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs
  4. +44 −0 src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs
  5. +20 −0 src/Microsoft.AspNet.Server.Kestrel.Https/Microsoft.AspNet.Server.Kestrel.Https.xproj
  6. +14 −0 src/Microsoft.AspNet.Server.Kestrel.Https/project.json
  7. +13 −0 src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs
  8. +63 −0 src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs
  9. +12 −0 src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs
  10. +91 −0 src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs
  11. +17 −0 src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs
  12. +99 −0 src/Microsoft.AspNet.Server.Kestrel/Filter/SocketInputStream.cs
  13. +35 −0 src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs
  14. +65 −9 src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs
  15. +0 −1 src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs
  16. +4 −5 src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs
  17. +4 −1 src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs
  18. +2 −4 src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs
  19. +2 −0 src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs
  20. +14 −40 src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs
  21. +2 −2 src/Microsoft.AspNet.Server.Kestrel/Http/PipeListener.cs
  22. +2 −2 src/Microsoft.AspNet.Server.Kestrel/Http/PipeListenerPrimary.cs
  23. +33 −0 src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs
  24. +19 −19 src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs
  25. +2 −2 src/Microsoft.AspNet.Server.Kestrel/Http/TcpListener.cs
  26. +2 −2 src/Microsoft.AspNet.Server.Kestrel/Http/TcpListenerPrimary.cs
  27. +4 −0 src/Microsoft.AspNet.Server.Kestrel/IKestrelServerInformation.cs
  28. +6 −10 src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs
  29. +3 −0 src/Microsoft.AspNet.Server.Kestrel/KestrelServerInformation.cs
  30. +6 −6 src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs
  31. +19 −0 src/Microsoft.AspNet.Server.Kestrel/ServerAddress.cs
  32. +3 −4 src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs
  33. +4 −0 src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs
  34. +2 −1 test/Microsoft.AspNet.Server.KestrelTests/CreateIPEndpointTests.cs
  35. +4 −2 test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs
  36. +2 −1 test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs
  37. +8 −4 test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs
  38. +2 −2 test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs
  39. +1 −3 test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs
@@ -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
@@ -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"
}
}
@@ -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<IKestrelServerInformation>();

if (serverInfo == null)
{
return app;
}

var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter();

serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, prevFilter);

return app;
}
}
}
@@ -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;
}
}
}
}
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>5f64b3c3-0c2e-431a-b820-a81bbfc863da</ProjectGuid>
<RootNamespace>Microsoft.AspNet.Server.Kestrel.Https</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>

<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
@@ -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": { }
}
}
@@ -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; }
}
}
@@ -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);
}
}
}
}
@@ -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);
}
}
@@ -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<byte>(buffer, offset, count)).GetAwaiter().GetResult();
}

public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return ReadAsync(new ArraySegment<byte>(buffer, offset, count));
}

public override void Write(byte[] buffer, int offset, int count)
{
var segment = new ArraySegment<byte>(buffer, offset, count);
_output.Write(segment);
}

public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token)
{
var segment = new ArraySegment<byte>(buffer, offset, count);
return _output.WriteAsync(segment);
}

public override void Flush()
{
// No-op since writes are immediate.
}

private Task<int> ReadAsync(ArraySegment<byte> buffer)
{
return _input.ReadAsync(buffer);
}
}
}
@@ -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<object>(null);

public Task OnConnection(ConnectionFilterContext context)
{
return _empty;
}
}
}
Oops, something went wrong.

0 comments on commit 2f3a006

Please sign in to comment.