Implement ITlsHandshakeFeature for HttpSys (#7284)

This commit is contained in:
Chris Ross 2019-02-13 08:39:45 -08:00 committed by GitHub
parent 3fd8a97af2
commit 06d7fe73a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 221 additions and 36 deletions

View File

@ -6,10 +6,12 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Security.Authentication;
using System.Security.Claims; using System.Security.Claims;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.AspNetCore.Http.Features.Authentication;
@ -24,6 +26,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
IHttpResponseFeature, IHttpResponseFeature,
IHttpSendFileFeature, IHttpSendFileFeature,
ITlsConnectionFeature, ITlsConnectionFeature,
ITlsHandshakeFeature,
// ITlsTokenBindingFeature, TODO: https://github.com/aspnet/HttpSysServer/issues/231 // ITlsTokenBindingFeature, TODO: https://github.com/aspnet/HttpSysServer/issues/231
IHttpBufferingFeature, IHttpBufferingFeature,
IHttpRequestLifetimeFeature, IHttpRequestLifetimeFeature,
@ -336,6 +339,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ {
return Request.IsHttps ? this : null; return Request.IsHttps ? this : null;
} }
internal ITlsHandshakeFeature GetTlsHandshakeFeature()
{
return Request.IsHttps ? this : null;
}
/* TODO: https://github.com/aspnet/HttpSysServer/issues/231 /* TODO: https://github.com/aspnet/HttpSysServer/issues/231
byte[] ITlsTokenBindingFeature.GetProvidedTokenBindingId() => Request.GetProvidedTokenBindingId(); byte[] ITlsTokenBindingFeature.GetProvidedTokenBindingId() => Request.GetProvidedTokenBindingId();
@ -482,6 +491,20 @@ namespace Microsoft.AspNetCore.Server.HttpSys
set => Request.MaxRequestBodySize = value; set => Request.MaxRequestBodySize = value;
} }
SslProtocols ITlsHandshakeFeature.Protocol => Request.Protocol;
CipherAlgorithmType ITlsHandshakeFeature.CipherAlgorithm => Request.CipherAlgorithm;
int ITlsHandshakeFeature.CipherStrength => Request.CipherStrength;
HashAlgorithmType ITlsHandshakeFeature.HashAlgorithm => Request.HashAlgorithm;
int ITlsHandshakeFeature.HashStrength => Request.HashStrength;
ExchangeAlgorithmType ITlsHandshakeFeature.KeyExchangeAlgorithm => Request.KeyExchangeAlgorithm;
int ITlsHandshakeFeature.KeyExchangeStrength => Request.KeyExchangeStrength;
internal async Task OnResponseStart() internal async Task OnResponseStart()
{ {
if (_responseStarted) if (_responseStarted)

View File

@ -16,6 +16,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.AspNetCore.Authentication.Core" /> <Reference Include="Microsoft.AspNetCore.Authentication.Core" />
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
<Reference Include="Microsoft.AspNetCore.Hosting" /> <Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.Net.Http.Headers" /> <Reference Include="Microsoft.Net.Http.Headers" />
<Reference Include="Microsoft.Win32.Registry" /> <Reference Include="Microsoft.Win32.Registry" />

View File

@ -5,6 +5,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Security.Principal; using System.Security.Principal;
using System.Threading; using System.Threading;
@ -83,6 +84,11 @@ namespace Microsoft.AspNetCore.Server.HttpSys
User = _nativeRequestContext.GetUser(); User = _nativeRequestContext.GetUser();
if (IsHttps)
{
GetTlsHandshakeResults();
}
// GetTlsTokenBindingInfo(); TODO: https://github.com/aspnet/HttpSysServer/issues/231 // GetTlsTokenBindingInfo(); TODO: https://github.com/aspnet/HttpSysServer/issues/231
// Finished directly accessing the HTTP_REQUEST structure. // Finished directly accessing the HTTP_REQUEST structure.
@ -232,6 +238,60 @@ namespace Microsoft.AspNetCore.Server.HttpSys
internal WindowsPrincipal User { get; } internal WindowsPrincipal User { get; }
public SslProtocols Protocol { get; private set; }
public CipherAlgorithmType CipherAlgorithm { get; private set; }
public int CipherStrength { get; private set; }
public HashAlgorithmType HashAlgorithm { get; private set; }
public int HashStrength { get; private set; }
public ExchangeAlgorithmType KeyExchangeAlgorithm { get; private set; }
public int KeyExchangeStrength { get; private set; }
private void GetTlsHandshakeResults()
{
var handshake = _nativeRequestContext.GetTlsHandshake();
Protocol = handshake.Protocol;
// The OS considers client and server TLS as different enum values. SslProtocols choose to combine those for some reason.
// We need to fill in the client bits so the enum shows the expected protocol.
// https://docs.microsoft.com/en-us/windows/desktop/api/schannel/ns-schannel-_secpkgcontext_connectioninfo
// Compare to https://referencesource.microsoft.com/#System/net/System/Net/SecureProtocols/_SslState.cs,8905d1bf17729de3
#pragma warning disable CS0618 // Type or member is obsolete
if ((Protocol & SslProtocols.Ssl2) != 0)
{
Protocol |= SslProtocols.Ssl2;
}
if ((Protocol & SslProtocols.Ssl3) != 0)
{
Protocol |= SslProtocols.Ssl3;
}
#pragma warning restore CS0618 // Type or member is obsolete
if ((Protocol & SslProtocols.Tls) != 0)
{
Protocol |= SslProtocols.Tls;
}
if ((Protocol & SslProtocols.Tls11) != 0)
{
Protocol |= SslProtocols.Tls11;
}
if ((Protocol & SslProtocols.Tls12) != 0)
{
Protocol |= SslProtocols.Tls12;
}
CipherAlgorithm = handshake.CipherType;
CipherStrength = (int)handshake.CipherStrength;
HashAlgorithm = handshake.HashType;
HashStrength = (int)handshake.HashStrength;
KeyExchangeAlgorithm = handshake.KeyExchangeType;
KeyExchangeStrength = (int)handshake.KeyExchangeStrength;
}
// Populates the client certificate. The result may be null if there is no client cert. // Populates the client certificate. The result may be null if there is no client cert.
// TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to // TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
// enable this, but it's unclear what Http.Sys would do. // enable this, but it's unclear what Http.Sys would do.

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.AspNetCore.Http.Features.Authentication;
@ -19,6 +20,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ typeof(IHttpResponseFeature), _identityFunc }, { typeof(IHttpResponseFeature), _identityFunc },
{ typeof(IHttpSendFileFeature), _identityFunc }, { typeof(IHttpSendFileFeature), _identityFunc },
{ typeof(ITlsConnectionFeature), ctx => ctx.GetTlsConnectionFeature() }, { typeof(ITlsConnectionFeature), ctx => ctx.GetTlsConnectionFeature() },
{ typeof(ITlsHandshakeFeature), ctx => ctx.GetTlsHandshakeFeature() },
// { typeof(ITlsTokenBindingFeature), ctx => ctx.GetTlsTokenBindingFeature() }, TODO: https://github.com/aspnet/HttpSysServer/issues/231 // { typeof(ITlsTokenBindingFeature), ctx => ctx.GetTlsTokenBindingFeature() }, TODO: https://github.com/aspnet/HttpSysServer/issues/231
{ typeof(IHttpBufferingFeature), _identityFunc }, { typeof(IHttpBufferingFeature), _identityFunc },
{ typeof(IHttpRequestLifetimeFeature), _identityFunc }, { typeof(IHttpRequestLifetimeFeature), _identityFunc },

View File

@ -1,12 +1,16 @@
// 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.Net.Http; using System.Net.Http;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
@ -15,40 +19,38 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ {
public class HttpsTests public class HttpsTests
{ {
private const string Address = "https://localhost:9090/"; [ConditionalFact]
[ConditionalFact(Skip = "TODO: Add trait filtering support so these SSL tests don't get run on teamcity or the command line."), Trait("scheme", "https")]
public async Task Https_200OK_Success() public async Task Https_200OK_Success()
{ {
using (Utilities.CreateHttpsServer(httpContext => using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
{ {
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
string response = await SendRequestAsync(Address); string response = await SendRequestAsync(address);
Assert.Equal(string.Empty, response); Assert.Equal(string.Empty, response);
} }
} }
[ConditionalFact(Skip = "TODO: Add trait filtering support so these SSL tests don't get run on teamcity or the command line."), Trait("scheme", "https")] [ConditionalFact]
public async Task Https_SendHelloWorld_Success() public async Task Https_SendHelloWorld_Success()
{ {
using (Utilities.CreateHttpsServer(httpContext => using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
{ {
byte[] body = Encoding.UTF8.GetBytes("Hello World"); byte[] body = Encoding.UTF8.GetBytes("Hello World");
httpContext.Response.ContentLength = body.Length; httpContext.Response.ContentLength = body.Length;
return httpContext.Response.Body.WriteAsync(body, 0, body.Length); return httpContext.Response.Body.WriteAsync(body, 0, body.Length);
})) }))
{ {
string response = await SendRequestAsync(Address); string response = await SendRequestAsync(address);
Assert.Equal("Hello World", response); Assert.Equal("Hello World", response);
} }
} }
[ConditionalFact(Skip = "TODO: Add trait filtering support so these SSL tests don't get run on teamcity or the command line."), Trait("scheme", "https")] [ConditionalFact]
public async Task Https_EchoHelloWorld_Success() public async Task Https_EchoHelloWorld_Success()
{ {
using (Utilities.CreateHttpsServer(httpContext => using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
{ {
string input = new StreamReader(httpContext.Request.Body).ReadToEnd(); string input = new StreamReader(httpContext.Request.Body).ReadToEnd();
Assert.Equal("Hello World", input); Assert.Equal("Hello World", input);
@ -58,15 +60,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
string response = await SendRequestAsync(Address, "Hello World"); string response = await SendRequestAsync(address, "Hello World");
Assert.Equal("Hello World", response); Assert.Equal("Hello World", response);
} }
} }
[ConditionalFact(Skip = "TODO: Add trait filtering support so these SSL tests don't get run on teamcity or the command line."), Trait("scheme", "https")] [ConditionalFact]
public async Task Https_ClientCertNotSent_ClientCertNotPresent() public async Task Https_ClientCertNotSent_ClientCertNotPresent()
{ {
using (Utilities.CreateHttpsServer(async httpContext => using (Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
{ {
var tls = httpContext.Features.Get<ITlsConnectionFeature>(); var tls = httpContext.Features.Get<ITlsConnectionFeature>();
Assert.NotNull(tls); Assert.NotNull(tls);
@ -75,15 +77,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys
Assert.Null(tls.ClientCertificate); Assert.Null(tls.ClientCertificate);
})) }))
{ {
string response = await SendRequestAsync(Address); string response = await SendRequestAsync(address);
Assert.Equal(string.Empty, response); Assert.Equal(string.Empty, response);
} }
} }
[ConditionalFact(Skip = "TODO: Add trait filtering support so these SSL tests don't get run on teamcity or the command line."), Trait("scheme", "https")] [ConditionalFact(Skip = "Manual test only, client certs are not always available.")]
public async Task Https_ClientCertRequested_ClientCertPresent() public async Task Https_ClientCertRequested_ClientCertPresent()
{ {
using (Utilities.CreateHttpsServer(async httpContext => using (Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
{ {
var tls = httpContext.Features.Get<ITlsConnectionFeature>(); var tls = httpContext.Features.Get<ITlsConnectionFeature>();
Assert.NotNull(tls); Assert.NotNull(tls);
@ -94,7 +96,37 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ {
X509Certificate2 cert = FindClientCert(); X509Certificate2 cert = FindClientCert();
Assert.NotNull(cert); Assert.NotNull(cert);
string response = await SendRequestAsync(Address, cert); string response = await SendRequestAsync(address, cert);
Assert.Equal(string.Empty, response);
}
}
[ConditionalFact]
public async Task Https_SetsITlsHandshakeFeature()
{
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
{
try
{
var tlsFeature = httpContext.Features.Get<ITlsHandshakeFeature>();
Assert.NotNull(tlsFeature);
Assert.True(tlsFeature.Protocol > SslProtocols.None, "Protocol");
Assert.True(Enum.IsDefined(typeof(SslProtocols), tlsFeature.Protocol), "Defined"); // Mapping is required, make sure it's current
Assert.True(tlsFeature.CipherAlgorithm > CipherAlgorithmType.Null, "Cipher");
Assert.True(tlsFeature.CipherStrength > 0, "CipherStrength");
Assert.True(tlsFeature.HashAlgorithm > HashAlgorithmType.None, "HashAlgorithm");
Assert.True(tlsFeature.HashStrength >= 0, "HashStrength"); // May be 0 for some algorithms
Assert.True(tlsFeature.KeyExchangeAlgorithm > ExchangeAlgorithmType.None, "KeyExchangeAlgorithm");
Assert.True(tlsFeature.KeyExchangeStrength > 0, "KeyExchangeStrength");
}
catch (Exception ex)
{
return httpContext.Response.WriteAsync(ex.ToString());
}
return Task.FromResult(0);
}))
{
string response = await SendRequestAsync(address);
Assert.Equal(string.Empty, response); Assert.Equal(string.Empty, response);
} }
} }

View File

@ -10,4 +10,9 @@
<Reference Include="System.Net.Http.WinHttpHandler" /> <Reference Include="System.Net.Http.WinHttpHandler" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<!--Imitate IIS Express so we can use it's cert bindings-->
<PackageTags>214124cd-d05b-4309-9af9-9caa44b2b74a</PackageTags>
</PropertyGroup>
</Project> </Project>

View File

@ -23,7 +23,10 @@ namespace Microsoft.AspNetCore.Server.HttpSys
// ports during dynamic port allocation. // ports during dynamic port allocation.
private const int BasePort = 5001; private const int BasePort = 5001;
private const int MaxPort = 8000; private const int MaxPort = 8000;
private const int BaseHttpsPort = 44300;
private const int MaxHttpsPort = 44399;
private static int NextPort = BasePort; private static int NextPort = BasePort;
private static int NextHttpsPort = BaseHttpsPort;
private static object PortLock = new object(); private static object PortLock = new object();
internal static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(15); internal static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(15);
internal static readonly int WriteRetryLimit = 1000; internal static readonly int WriteRetryLimit = 1000;
@ -148,18 +151,38 @@ namespace Microsoft.AspNetCore.Server.HttpSys
throw new Exception("Failed to locate a free port."); throw new Exception("Failed to locate a free port.");
} }
internal static IServer CreateHttpsServer(RequestDelegate app) internal static IServer CreateDynamicHttpsServer(out string baseAddress, RequestDelegate app)
{ {
return CreateServer("https", "localhost", 9090, string.Empty, app); return CreateDynamicHttpsServer("/", out var root, out baseAddress, options => { }, app);
} }
internal static IServer CreateServer(string scheme, string host, int port, string path, RequestDelegate app) internal static IServer CreateDynamicHttpsServer(string basePath, out string root, out string baseAddress, Action<HttpSysOptions> configureOptions, RequestDelegate app)
{ {
lock (PortLock)
{
while (NextHttpsPort < MaxHttpsPort)
{
var port = NextHttpsPort++;
var prefix = UrlPrefix.Create("https", "localhost", port, basePath);
root = prefix.Scheme + "://" + prefix.Host + ":" + prefix.Port;
baseAddress = prefix.ToString();
var server = CreatePump(); var server = CreatePump();
server.Features.Get<IServerAddressesFeature>().Addresses.Add(UrlPrefix.Create(scheme, host, port, path).ToString()); server.Features.Get<IServerAddressesFeature>().Addresses.Add(baseAddress);
configureOptions(server.Listener.Options);
try
{
server.StartAsync(new DummyApplication(app), CancellationToken.None).Wait(); server.StartAsync(new DummyApplication(app), CancellationToken.None).Wait();
return server; return server;
} }
catch (HttpSysException)
{
}
}
NextHttpsPort = BaseHttpsPort;
}
throw new Exception("Failed to locate a free port.");
}
internal static Task WithTimeout(this Task task) => task.TimeoutAfter(DefaultTimeout); internal static Task WithTimeout(this Task task) => task.TimeoutAfter(DefaultTimeout);

View File

@ -1,9 +1,11 @@
// 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Authentication;
namespace Microsoft.AspNetCore.HttpSys.Internal namespace Microsoft.AspNetCore.HttpSys.Internal
{ {
internal static unsafe class HttpApiTypes internal static unsafe class HttpApiTypes
@ -426,12 +428,24 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
internal char* pMutualAuthData; internal char* pMutualAuthData;
} }
[StructLayout(LayoutKind.Sequential)]
internal struct HTTP_SSL_PROTOCOL_INFO
{
internal SslProtocols Protocol;
internal CipherAlgorithmType CipherType;
internal uint CipherStrength;
internal HashAlgorithmType HashType;
internal uint HashStrength;
internal ExchangeAlgorithmType KeyExchangeType;
internal uint KeyExchangeStrength;
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct HTTP_REQUEST_INFO internal struct HTTP_REQUEST_INFO
{ {
internal HTTP_REQUEST_INFO_TYPE InfoType; internal HTTP_REQUEST_INFO_TYPE InfoType;
internal uint InfoLength; internal uint InfoLength;
internal HTTP_REQUEST_AUTH_INFO* pInfo; internal void* pInfo;
} }
[Flags] [Flags]

View File

@ -184,12 +184,15 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
{ {
var info = &requestInfo[i]; var info = &requestInfo[i];
if (info != null if (info != null
&& info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth && info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth)
&& info->pInfo->AuthStatus == HttpApiTypes.HTTP_AUTH_STATUS.HttpAuthStatusSuccess) {
var authInfo = (HttpApiTypes.HTTP_REQUEST_AUTH_INFO*)info->pInfo;
if (authInfo->AuthStatus == HttpApiTypes.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
{ {
return true; return true;
} }
} }
}
return false; return false;
} }
@ -202,22 +205,44 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
{ {
var info = &requestInfo[i]; var info = &requestInfo[i];
if (info != null if (info != null
&& info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth && info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth)
&& info->pInfo->AuthStatus == HttpApiTypes.HTTP_AUTH_STATUS.HttpAuthStatusSuccess) {
var authInfo = (HttpApiTypes.HTTP_REQUEST_AUTH_INFO*)info->pInfo;
if (authInfo->AuthStatus == HttpApiTypes.HTTP_AUTH_STATUS.HttpAuthStatusSuccess)
{ {
// Duplicates AccessToken // Duplicates AccessToken
var identity = new WindowsIdentity(info->pInfo->AccessToken, GetAuthTypeFromRequest(info->pInfo->AuthType)); var identity = new WindowsIdentity(authInfo->AccessToken, GetAuthTypeFromRequest(authInfo->AuthType));
// Close the original // Close the original
UnsafeNclNativeMethods.SafeNetHandles.CloseHandle(info->pInfo->AccessToken); UnsafeNclNativeMethods.SafeNetHandles.CloseHandle(authInfo->AccessToken);
return new WindowsPrincipal(identity); return new WindowsPrincipal(identity);
} }
} }
}
return new WindowsPrincipal(WindowsIdentity.GetAnonymous()); // Anonymous / !IsAuthenticated return new WindowsPrincipal(WindowsIdentity.GetAnonymous()); // Anonymous / !IsAuthenticated
} }
internal HttpApiTypes.HTTP_SSL_PROTOCOL_INFO GetTlsHandshake()
{
var requestInfo = NativeRequestV2->pRequestInfo;
var infoCount = NativeRequestV2->RequestInfoCount;
for (int i = 0; i < infoCount; i++)
{
var info = &requestInfo[i];
if (info != null
&& info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol)
{
var authInfo = (HttpApiTypes.HTTP_SSL_PROTOCOL_INFO*)info->pInfo;
return *authInfo;
}
}
return default;
}
private static string GetAuthTypeFromRequest(HttpApiTypes.HTTP_REQUEST_AUTH_TYPE input) private static string GetAuthTypeFromRequest(HttpApiTypes.HTTP_REQUEST_AUTH_TYPE input)
{ {
switch (input) switch (input)