Remove managed InProcess code (#809)

This commit is contained in:
Pavel Krymets 2018-05-03 17:18:33 -07:00 committed by GitHub
parent 73c5e23ffb
commit 361caff1ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1 additions and 4052 deletions

View File

@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.IIS;
using Microsoft.AspNetCore.Server.IISIntegration;
namespace NativeIISSample
@ -66,12 +65,6 @@ namespace NativeIISSample
// accessing IIS server variables
await context.Response.WriteAsync("Server Variables:" + Environment.NewLine);
foreach (var varName in IISServerVarNames)
{
await context.Response.WriteAsync(varName + ": " + context.GetIISServerVariable(varName) + Environment.NewLine);
}
await context.Response.WriteAsync(Environment.NewLine);
if (context.Features.Get<IHttpUpgradeFeature>() != null)
{
await context.Response.WriteAsync("Websocket feature is enabled.");
@ -82,20 +75,6 @@ namespace NativeIISSample
}
});
}
private static readonly string[] IISServerVarNames =
{
"AUTH_TYPE",
"AUTH_USER",
"CONTENT_TYPE",
"HTTP_HOST",
"HTTPS",
"REMOTE_PORT",
"REMOTE_USER",
"REQUEST_METHOD",
"WEBSOCKET_VERSION"
};
public static void Main(string[] args)
{
var host = new WebHostBuilder()

View File

@ -1,38 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Server.IIS
{
/// <summary>
/// Extensions to <see cref="HttpContext"/> that enable access to IIS features.
/// </summary>
public static class IISHttpContextExtensions
{
/// <summary>
/// Gets the value of a server variable for the current request.
/// </summary>
/// <param name="context">The http context for the request.</param>
/// <param name="variableName">The name of the variable.</param>
/// <returns>
/// <c>null</c> if the feature does not support the <see cref="IServerVariablesFeature"/> feature.
/// May return null or empty if the variable does not exist or is not set.
/// </returns>
/// <remarks>
/// For a list of common server variables available in IIS, see http://go.microsoft.com/fwlink/?LinkId=52471.
/// </remarks>
public static string GetIISServerVariable(this HttpContext context, string variableName)
{
var feature = context.Features.Get<IServerVariablesFeature>();
if (feature == null)
{
return null;
}
return feature[variableName];
}
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Http.Features
{
/// <summary>
/// This feature provides access to request server variables set.
/// <para>
/// This feature is only available when hosting ASP.NET Core in-process with IIS or IIS Express.
/// </para>
/// </summary>
/// <remarks>
/// For a list of common server variables available in IIS, see http://go.microsoft.com/fwlink/?LinkId=52471.
/// </remarks>
public interface IServerVariablesFeature
{
/// <summary>
/// Gets the value of a server variable for the current request.
/// </summary>
/// <param name="variableName">The variable name</param>
/// <returns>May return null or empty if the variable does not exist or is not set.</returns>
string this[string variableName] { get; }
}
}

View File

@ -9,264 +9,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal static class NativeMethods
{
private const int HR_NOT_FOUND = unchecked((int)0x80070490);
private const int HR_OK = 0;
private const string KERNEL32 = "kernel32.dll";
private const string AspNetCoreModuleDll = "aspnetcorerh.dll";
[DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
public static bool IsAspNetCoreModuleLoaded()
{
return GetModuleHandle(AspNetCoreModuleDll) != IntPtr.Zero;
}
public enum REQUEST_NOTIFICATION_STATUS
{
RQ_NOTIFICATION_CONTINUE,
RQ_NOTIFICATION_PENDING,
RQ_NOTIFICATION_FINISH_REQUEST
}
public delegate REQUEST_NOTIFICATION_STATUS PFN_REQUEST_HANDLER(IntPtr pInProcessHandler, IntPtr pvRequestContext);
public delegate bool PFN_SHUTDOWN_HANDLER(IntPtr pvRequestContext);
public delegate REQUEST_NOTIFICATION_STATUS PFN_ASYNC_COMPLETION(IntPtr pvManagedHttpContext, int hr, int bytes);
public delegate REQUEST_NOTIFICATION_STATUS PFN_WEBSOCKET_ASYNC_COMPLETION(IntPtr pInProcessHandler, IntPtr completionInfo, IntPtr pvCompletionContext);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_post_completion(IntPtr pInProcessHandler, int cbBytes);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_set_completion_status(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS rquestNotificationStatus);
[DllImport(AspNetCoreModuleDll)]
private static extern void http_indicate_completion(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS notificationStatus);
[DllImport(AspNetCoreModuleDll)]
private static extern void register_callbacks(PFN_REQUEST_HANDLER request_callback, PFN_SHUTDOWN_HANDLER shutdown_callback, PFN_ASYNC_COMPLETION managed_context_handler, IntPtr pvRequestContext, IntPtr pvShutdownContext);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_write_response_bytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_flush_response_bytes(IntPtr pInProcessHandler, out bool fCompletionExpected);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe HttpApiTypes.HTTP_REQUEST_V2* http_get_raw_request(IntPtr pInProcessHandler);
[DllImport(AspNetCoreModuleDll)]
private static extern void http_stop_calls_into_managed();
[DllImport(AspNetCoreModuleDll)]
private static extern void http_stop_incoming_requests();
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe HttpApiTypes.HTTP_RESPONSE_V2* http_get_raw_response(IntPtr pInProcessHandler);
[DllImport(AspNetCoreModuleDll, CharSet = CharSet.Ansi)]
private static extern int http_set_response_status_code(IntPtr pInProcessHandler, ushort statusCode, string pszReason);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_read_request_bytes(IntPtr pInProcessHandler, byte* pvBuffer, int cbBuffer, out int dwBytesReceived, out bool fCompletionExpected);
[DllImport(AspNetCoreModuleDll)]
private static extern void http_get_completion_info(IntPtr pCompletionInfo, out int cbBytes, out int hr);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_set_managed_context(IntPtr pInProcessHandler, IntPtr pvManagedContext);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_get_application_properties(ref IISConfigurationData iiConfigData);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_get_server_variable(
IntPtr pInProcessHandler,
[MarshalAs(UnmanagedType.LPStr)] string variableName,
[MarshalAs(UnmanagedType.BStr)] out string value);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_websockets_read_bytes(
IntPtr pInProcessHandler,
byte* pvBuffer,
int cbBuffer,
PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback,
IntPtr pvCompletionContext,
out int dwBytesReceived,
out bool fCompletionExpected);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_websockets_write_bytes(
IntPtr pInProcessHandler,
HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks,
int nChunks,
PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback,
IntPtr pvCompletionContext,
out bool fCompletionExpected);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_enable_websockets(IntPtr pInProcessHandler);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_cancel_io(IntPtr pInProcessHandler);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_response_set_unknown_header(IntPtr pInProcessHandler, byte* pszHeaderName, byte* pszHeaderValue, ushort usHeaderValueLength, bool fReplace);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_response_set_known_header(IntPtr pInProcessHandler, int headerId, byte* pHeaderValue, ushort length, bool fReplace);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_get_authentication_information(IntPtr pInProcessHandler, [MarshalAs(UnmanagedType.BStr)] out string authType, out IntPtr token);
public static void HttpPostCompletion(IntPtr pInProcessHandler, int cbBytes)
{
Validate(http_post_completion(pInProcessHandler, cbBytes));
}
public static void HttpSetCompletionStatus(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS rquestNotificationStatus)
{
Validate(http_set_completion_status(pInProcessHandler, rquestNotificationStatus));
}
public static void HttpIndicateCompletion(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS notificationStatus)
{
http_indicate_completion(pInProcessHandler, notificationStatus);
}
public static void HttpRegisterCallbacks(PFN_REQUEST_HANDLER request_callback, PFN_SHUTDOWN_HANDLER shutdown_callback, PFN_ASYNC_COMPLETION managed_context_handler, IntPtr pvRequestContext, IntPtr pvShutdownContext)
{
register_callbacks(request_callback, shutdown_callback, managed_context_handler, pvRequestContext, pvShutdownContext);
}
public static unsafe int HttpWriteResponseBytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected)
{
return http_write_response_bytes(pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected);
}
public static int HttpFlushResponseBytes(IntPtr pInProcessHandler, out bool fCompletionExpected)
{
return http_flush_response_bytes(pInProcessHandler, out fCompletionExpected);
}
public static unsafe HttpApiTypes.HTTP_REQUEST_V2* HttpGetRawRequest(IntPtr pInProcessHandler)
{
return http_get_raw_request(pInProcessHandler);
}
public static void HttpStopCallsIntoManaged()
{
http_stop_calls_into_managed();
}
public static void HttpStopIncomingRequests()
{
http_stop_incoming_requests();
}
public static unsafe HttpApiTypes.HTTP_RESPONSE_V2* HttpGetRawResponse(IntPtr pInProcessHandler)
{
return http_get_raw_response(pInProcessHandler);
}
public static void HttpSetResponseStatusCode(IntPtr pInProcessHandler, ushort statusCode, string pszReason)
{
Validate(http_set_response_status_code(pInProcessHandler, statusCode, pszReason));
}
public static unsafe int HttpReadRequestBytes(IntPtr pInProcessHandler, byte* pvBuffer, int cbBuffer, out int dwBytesReceived, out bool fCompletionExpected)
{
return http_read_request_bytes(pInProcessHandler, pvBuffer, cbBuffer, out dwBytesReceived, out fCompletionExpected);
}
public static void HttpGetCompletionInfo(IntPtr pCompletionInfo, out int cbBytes, out int hr)
{
http_get_completion_info(pCompletionInfo, out cbBytes, out hr);
}
public static void HttpSetManagedContext(IntPtr pInProcessHandler, IntPtr pvManagedContext)
{
Validate(http_set_managed_context(pInProcessHandler, pvManagedContext));
}
public static IISConfigurationData HttpGetApplicationProperties()
{
var iisConfigurationData = new IISConfigurationData();
Validate(http_get_application_properties(ref iisConfigurationData));
return iisConfigurationData;
}
public static bool HttpTryGetServerVariable(IntPtr pInProcessHandler, string variableName, out string value)
{
return http_get_server_variable(pInProcessHandler, variableName, out value) == 0;
}
public static unsafe int HttpWebsocketsReadBytes(
IntPtr pInProcessHandler,
byte* pvBuffer,
int cbBuffer,
PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback,
IntPtr pvCompletionContext, out int dwBytesReceived,
out bool fCompletionExpected)
{
return http_websockets_read_bytes(pInProcessHandler, pvBuffer, cbBuffer, pfnCompletionCallback, pvCompletionContext, out dwBytesReceived, out fCompletionExpected);
}
public static unsafe int HttpWebsocketsWriteBytes(
IntPtr pInProcessHandler,
HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks,
int nChunks,
PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback,
IntPtr pvCompletionContext,
out bool fCompletionExpected)
{
return http_websockets_write_bytes(pInProcessHandler, pDataChunks, nChunks, pfnCompletionCallback, pvCompletionContext, out fCompletionExpected);
}
public static void HttpEnableWebsockets(IntPtr pInProcessHandler)
{
Validate(http_enable_websockets(pInProcessHandler));
}
public static bool HttpTryCancelIO(IntPtr pInProcessHandler)
{
var hr = http_cancel_io(pInProcessHandler);
// Async operation finished
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363792(v=vs.85).aspx
if (hr == HR_NOT_FOUND)
{
return false;
}
Validate(hr);
return true;
}
public static unsafe void HttpResponseSetUnknownHeader(IntPtr pInProcessHandler, byte* pszHeaderName, byte* pszHeaderValue, ushort usHeaderValueLength, bool fReplace)
{
Validate(http_response_set_unknown_header(pInProcessHandler, pszHeaderName, pszHeaderValue, usHeaderValueLength, fReplace));
}
public static unsafe void HttpResponseSetKnownHeader(IntPtr pInProcessHandler, int headerId, byte* pHeaderValue, ushort length, bool fReplace)
{
Validate(http_response_set_known_header(pInProcessHandler, headerId, pHeaderValue, length, fReplace));
}
public static void HttpGetAuthenticationInformation(IntPtr pInProcessHandler, out string authType, out IntPtr token)
{
Validate(http_get_authentication_information(pInProcessHandler, out authType, out token));
}
private static void Validate(int hr)
{
if (hr != HR_OK)
{
throw Marshal.GetExceptionForHR(hr);
}
}
}
}

View File

@ -1,69 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
// TODO redudant file, remove
// See https://github.com/aspnet/IISIntegration/issues/426
internal class DuplexStream : Stream
{
private Stream _requestBody;
private Stream _responseBody;
public DuplexStream(Stream requestBody, Stream responseBody)
{
_requestBody = requestBody;
_responseBody = responseBody;
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override void Flush()
{
_responseBody.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _requestBody.Read(buffer, offset, count);
}
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)
{
_responseBody.Write(buffer, offset, count);
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return _requestBody.ReadAsync(buffer, offset, count, cancellationToken);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return _responseBody.WriteAsync(buffer, offset, count, cancellationToken);
}
}
}

View File

@ -1,119 +0,0 @@
// 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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
// Primarily copied from https://github.com/aspnet/KestrelHttpServer/blob/dev/src/Kestrel.Transport.Libuv/Internal/LibuvAwaitable.cs
internal class IISAwaitable : ICriticalNotifyCompletion
{
private readonly static Action _callbackCompleted = () => { };
private Action _callback;
private Exception _exception;
private int _cbBytes;
private int _hr;
public static readonly NativeMethods.PFN_WEBSOCKET_ASYNC_COMPLETION ReadCallback = (IntPtr pHttpContext, IntPtr pCompletionInfo, IntPtr pvCompletionContext) =>
{
var context = (IISHttpContext)GCHandle.FromIntPtr(pvCompletionContext).Target;
NativeMethods.HttpGetCompletionInfo(pCompletionInfo, out int cbBytes, out int hr);
context.CompleteReadWebSockets(hr, cbBytes);
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
};
public static readonly NativeMethods.PFN_WEBSOCKET_ASYNC_COMPLETION WriteCallback = (IntPtr pHttpContext, IntPtr pCompletionInfo, IntPtr pvCompletionContext) =>
{
var context = (IISHttpContext)GCHandle.FromIntPtr(pvCompletionContext).Target;
NativeMethods.HttpGetCompletionInfo(pCompletionInfo, out int cbBytes, out int hr);
context.CompleteWriteWebSockets(hr, cbBytes);
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
};
public IISAwaitable GetAwaiter() => this;
public bool IsCompleted => _callback == _callbackCompleted;
public bool HasContinuation => _callback != null && !IsCompleted;
public int GetResult()
{
var exception = _exception;
var cbBytes = _cbBytes;
var hr = _hr;
// Reset the awaitable state
_exception = null;
_cbBytes = 0;
_callback = null;
_hr = 0;
if (exception != null)
{
// If the exception was an aborted read operation,
// return -1 to notify NativeReadAsync that the write was cancelled.
// E_OPERATIONABORTED == 0x800703e3 == -2147023901
// We also don't throw the exception here as this is expected behavior
// and can negatively impact perf if we catch an exception for each
// cann
if (hr != IISServerConstants.HResultCancelIO)
{
throw exception;
}
else
{
cbBytes = -1;
}
}
return cbBytes;
}
public void OnCompleted(Action continuation)
{
// There should never be a race between IsCompleted and OnCompleted since both operations
// should always be on the libuv thread
if (_callback == _callbackCompleted ||
Interlocked.CompareExchange(ref _callback, continuation, null) == _callbackCompleted)
{
// Just run it inline
Task.Run(continuation);
}
}
public void UnsafeOnCompleted(Action continuation)
{
OnCompleted(continuation);
}
public void Complete(int hr, int cbBytes)
{
_hr = hr;
_exception = Marshal.GetExceptionForHR(hr);
_cbBytes = cbBytes;
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
continuation?.Invoke();
}
public Action GetCompletion(int hr, int cbBytes)
{
_hr = hr;
_exception = Marshal.GetExceptionForHR(hr);
_cbBytes = cbBytes;
return Interlocked.Exchange(ref _callback, _callbackCompleted);
}
}
}

View File

@ -1,19 +0,0 @@
// 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.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct IISConfigurationData
{
[MarshalAs(UnmanagedType.BStr)]
public string pwzFullApplicationPath;
[MarshalAs(UnmanagedType.BStr)]
public string pwzVirtualApplicationPath;
public bool fWindowsAuthEnabled;
public bool fBasicAuthEnabled;
public bool fAnonymousAuthEnable;
}
}

View File

@ -1,276 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.AspNetCore.WebUtilities;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal partial class IISHttpContext : IFeatureCollection,
IHttpRequestFeature,
IHttpResponseFeature,
IHttpUpgradeFeature,
IHttpRequestLifetimeFeature,
IHttpAuthenticationFeature,
IServerVariablesFeature
{
// NOTE: When feature interfaces are added to or removed from this HttpProtocol implementation,
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
private int _featureRevision;
private string _httpProtocolVersion = null;
private List<KeyValuePair<Type, object>> MaybeExtra;
public void ResetFeatureCollection()
{
Initialize();
MaybeExtra?.Clear();
_featureRevision++;
}
private object ExtraFeatureGet(Type key)
{
if (MaybeExtra == null)
{
return null;
}
for (var i = 0; i < MaybeExtra.Count; i++)
{
var kv = MaybeExtra[i];
if (kv.Key == key)
{
return kv.Value;
}
}
return null;
}
private void ExtraFeatureSet(Type key, object value)
{
if (MaybeExtra == null)
{
MaybeExtra = new List<KeyValuePair<Type, object>>(2);
}
for (var i = 0; i < MaybeExtra.Count; i++)
{
if (MaybeExtra[i].Key == key)
{
MaybeExtra[i] = new KeyValuePair<Type, object>(key, value);
return;
}
}
MaybeExtra.Add(new KeyValuePair<Type, object>(key, value));
}
string IHttpRequestFeature.Protocol
{
get
{
if (_httpProtocolVersion == null)
{
var protocol = HttpVersion;
if (protocol.Major == 1 && protocol.Minor == 1)
{
_httpProtocolVersion = "HTTP/1.1";
}
else if (protocol.Major == 1 && protocol.Minor == 0)
{
_httpProtocolVersion = "HTTP/1.0";
}
else
{
_httpProtocolVersion = "HTTP/" + protocol.ToString(2);
}
}
return _httpProtocolVersion;
}
set
{
_httpProtocolVersion = value;
}
}
string IHttpRequestFeature.Scheme
{
get => Scheme;
set => Scheme = value;
}
string IHttpRequestFeature.Method
{
get => Method;
set => Method = value;
}
string IHttpRequestFeature.PathBase
{
get => PathBase;
set => PathBase = value;
}
string IHttpRequestFeature.Path
{
get => Path;
set => Path = value;
}
string IHttpRequestFeature.QueryString
{
get => QueryString;
set => QueryString = value;
}
string IHttpRequestFeature.RawTarget
{
get => RawTarget;
set => RawTarget = value;
}
IHeaderDictionary IHttpRequestFeature.Headers
{
get => RequestHeaders;
set => RequestHeaders = value;
}
Stream IHttpRequestFeature.Body
{
get => RequestBody;
set => RequestBody = value;
}
int IHttpResponseFeature.StatusCode
{
get => StatusCode;
set => StatusCode = value;
}
string IHttpResponseFeature.ReasonPhrase
{
get => ReasonPhrase;
set => ReasonPhrase = value;
}
IHeaderDictionary IHttpResponseFeature.Headers
{
get => ResponseHeaders;
set => ResponseHeaders = value;
}
Stream IHttpResponseFeature.Body
{
get => ResponseBody;
set => ResponseBody = value;
}
CancellationToken IHttpRequestLifetimeFeature.RequestAborted
{
get => RequestAborted;
set => RequestAborted = value;
}
bool IHttpResponseFeature.HasStarted => HasResponseStarted;
bool IHttpUpgradeFeature.IsUpgradableRequest => _server.IsWebSocketAvailible(_pInProcessHandler);
bool IFeatureCollection.IsReadOnly => false;
int IFeatureCollection.Revision => _featureRevision;
ClaimsPrincipal IHttpAuthenticationFeature.User
{
get => User;
set => User = value;
}
public IAuthenticationHandler Handler { get; set; }
string IServerVariablesFeature.this[string variableName]
{
get
{
if (string.IsNullOrEmpty(variableName))
{
return null;
}
return NativeMethods.HttpTryGetServerVariable(_pInProcessHandler, variableName, out var value) ? value : null;
}
}
object IFeatureCollection.this[Type key]
{
get => FastFeatureGet(key);
set => FastFeatureSet(key, value);
}
TFeature IFeatureCollection.Get<TFeature>()
{
return (TFeature)FastFeatureGet(typeof(TFeature));
}
void IFeatureCollection.Set<TFeature>(TFeature instance)
{
FastFeatureSet(typeof(TFeature), instance);
}
void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
{
OnStarting(callback, state);
}
void IHttpResponseFeature.OnCompleted(Func<object, Task> callback, object state)
{
OnCompleted(callback, state);
}
async Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
{
// TODO fix these exceptions strings
if (!((IHttpUpgradeFeature)this).IsUpgradableRequest)
{
throw new InvalidOperationException("CoreStrings.CannotUpgradeNonUpgradableRequest");
}
if (_wasUpgraded)
{
throw new InvalidOperationException("CoreStrings.UpgradeCannotBeCalledMultipleTimes");
}
if (HasResponseStarted)
{
throw new InvalidOperationException("CoreStrings.UpgradeCannotBeCalledMultipleTimes");
}
_wasUpgraded = true;
StatusCode = StatusCodes.Status101SwitchingProtocols;
ReasonPhrase = ReasonPhrases.GetReasonPhrase(StatusCodes.Status101SwitchingProtocols);
_readWebSocketsOperation = new IISAwaitable();
_writeWebSocketsOperation = new IISAwaitable();
NativeMethods.HttpEnableWebsockets(_pInProcessHandler);
// Upgrade async will cause the stream processing to go into duplex mode
await UpgradeAsync();
return new DuplexStream(RequestBody, ResponseBody);
}
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
void IHttpRequestLifetimeFeature.Abort()
{
Abort();
}
}
}

View File

@ -1,336 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal partial class IISHttpContext
{
private static readonly Type IHttpRequestFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestFeature);
private static readonly Type IHttpResponseFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpResponseFeature);
private static readonly Type IHttpRequestIdentifierFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestIdentifierFeature);
private static readonly Type IServiceProvidersFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IServiceProvidersFeature);
private static readonly Type IHttpRequestLifetimeFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpRequestLifetimeFeature);
private static readonly Type IHttpConnectionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature);
private static readonly Type IHttpAuthenticationFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.Authentication.IHttpAuthenticationFeature);
private static readonly Type IQueryFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IQueryFeature);
private static readonly Type IFormFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IFormFeature);
private static readonly Type IHttpUpgradeFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpUpgradeFeature);
private static readonly Type IResponseCookiesFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IResponseCookiesFeature);
private static readonly Type IItemsFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IItemsFeature);
private static readonly Type ITlsConnectionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.ITlsConnectionFeature);
private static readonly Type IHttpWebSocketFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpWebSocketFeature);
private static readonly Type ISessionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.ISessionFeature);
private static readonly Type IHttpBodyControlFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpBodyControlFeature);
private static readonly Type IHttpSendFileFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
private static readonly Type IISHttpContextType = typeof(IISHttpContext);
private static readonly Type IServerVariablesFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IServerVariablesFeature);
private object _currentIHttpRequestFeature;
private object _currentIHttpResponseFeature;
private object _currentIHttpRequestIdentifierFeature;
private object _currentIServiceProvidersFeature;
private object _currentIHttpRequestLifetimeFeature;
private object _currentIHttpConnectionFeature;
private object _currentIHttpAuthenticationFeature;
private object _currentIQueryFeature;
private object _currentIFormFeature;
private object _currentIHttpUpgradeFeature;
private object _currentIResponseCookiesFeature;
private object _currentIItemsFeature;
private object _currentITlsConnectionFeature;
private object _currentIHttpMaxRequestBodySizeFeature;
private object _currentIHttpMinRequestBodyDataRateFeature;
private object _currentIHttpMinResponseDataRateFeature;
private object _currentIHttpWebSocketFeature;
private object _currentISessionFeature;
private object _currentIHttpBodyControlFeature;
private object _currentIHttpSendFileFeature;
private object _currentIServerVariablesFeature;
private void Initialize()
{
_currentIHttpRequestFeature = this;
_currentIHttpResponseFeature = this;
_currentIHttpUpgradeFeature = this;
_currentIHttpRequestIdentifierFeature = this;
_currentIHttpRequestLifetimeFeature = this;
_currentIHttpConnectionFeature = this;
_currentIHttpMaxRequestBodySizeFeature = this;
_currentIHttpMinRequestBodyDataRateFeature = this;
_currentIHttpMinResponseDataRateFeature = this;
_currentIHttpBodyControlFeature = this;
_currentIHttpAuthenticationFeature = this;
_currentIServerVariablesFeature = this;
}
internal object FastFeatureGet(Type key)
{
if (key == IHttpRequestFeatureType)
{
return _currentIHttpRequestFeature;
}
if (key == IHttpResponseFeatureType)
{
return _currentIHttpResponseFeature;
}
if (key == IHttpRequestIdentifierFeatureType)
{
return _currentIHttpRequestIdentifierFeature;
}
if (key == IServiceProvidersFeatureType)
{
return _currentIServiceProvidersFeature;
}
if (key == IHttpRequestLifetimeFeatureType)
{
return _currentIHttpRequestLifetimeFeature;
}
if (key == IHttpConnectionFeatureType)
{
return _currentIHttpConnectionFeature;
}
if (key == IHttpAuthenticationFeatureType)
{
return _currentIHttpAuthenticationFeature;
}
if (key == IQueryFeatureType)
{
return _currentIQueryFeature;
}
if (key == IFormFeatureType)
{
return _currentIFormFeature;
}
if (key == IHttpUpgradeFeatureType)
{
return _currentIHttpUpgradeFeature;
}
if (key == IResponseCookiesFeatureType)
{
return _currentIResponseCookiesFeature;
}
if (key == IItemsFeatureType)
{
return _currentIItemsFeature;
}
if (key == ITlsConnectionFeatureType)
{
return _currentITlsConnectionFeature;
}
if (key == IHttpWebSocketFeatureType)
{
return _currentIHttpWebSocketFeature;
}
if (key == ISessionFeatureType)
{
return _currentISessionFeature;
}
if (key == IHttpBodyControlFeatureType)
{
return _currentIHttpBodyControlFeature;
}
if (key == IHttpSendFileFeatureType)
{
return _currentIHttpSendFileFeature;
}
if (key == IISHttpContextType)
{
return this;
}
if (key == IServerVariablesFeature)
{
return this;
}
return ExtraFeatureGet(key);
}
internal void FastFeatureSet(Type key, object feature)
{
_featureRevision++;
if (key == IHttpRequestFeatureType)
{
_currentIHttpRequestFeature = feature;
return;
}
if (key == IHttpResponseFeatureType)
{
_currentIHttpResponseFeature = feature;
return;
}
if (key == IHttpRequestIdentifierFeatureType)
{
_currentIHttpRequestIdentifierFeature = feature;
return;
}
if (key == IServiceProvidersFeatureType)
{
_currentIServiceProvidersFeature = feature;
return;
}
if (key == IHttpRequestLifetimeFeatureType)
{
_currentIHttpRequestLifetimeFeature = feature;
return;
}
if (key == IHttpConnectionFeatureType)
{
_currentIHttpConnectionFeature = feature;
return;
}
if (key == IHttpAuthenticationFeatureType)
{
_currentIHttpAuthenticationFeature = feature;
return;
}
if (key == IQueryFeatureType)
{
_currentIQueryFeature = feature;
return;
}
if (key == IFormFeatureType)
{
_currentIFormFeature = feature;
return;
}
if (key == IHttpUpgradeFeatureType)
{
_currentIHttpUpgradeFeature = feature;
return;
}
if (key == IResponseCookiesFeatureType)
{
_currentIResponseCookiesFeature = feature;
return;
}
if (key == IItemsFeatureType)
{
_currentIItemsFeature = feature;
return;
}
if (key == ITlsConnectionFeatureType)
{
_currentITlsConnectionFeature = feature;
return;
}
if (key == IHttpWebSocketFeatureType)
{
_currentIHttpWebSocketFeature = feature;
return;
}
if (key == ISessionFeatureType)
{
_currentISessionFeature = feature;
return;
}
if (key == IHttpBodyControlFeatureType)
{
_currentIHttpBodyControlFeature = feature;
return;
}
if (key == IHttpSendFileFeatureType)
{
_currentIHttpSendFileFeature = feature;
return;
}
if (key == IServerVariablesFeature)
{
_currentIServerVariablesFeature = feature;
return;
}
if (key == IISHttpContextType)
{
throw new InvalidOperationException("Cannot set IISHttpContext in feature collection");
};
ExtraFeatureSet(key, feature);
}
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
{
if (_currentIHttpRequestFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpRequestFeatureType, _currentIHttpRequestFeature as global::Microsoft.AspNetCore.Http.Features.IHttpRequestFeature);
}
if (_currentIHttpResponseFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpResponseFeatureType, _currentIHttpResponseFeature as global::Microsoft.AspNetCore.Http.Features.IHttpResponseFeature);
}
if (_currentIHttpRequestIdentifierFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpRequestIdentifierFeatureType, _currentIHttpRequestIdentifierFeature as global::Microsoft.AspNetCore.Http.Features.IHttpRequestIdentifierFeature);
}
if (_currentIServiceProvidersFeature != null)
{
yield return new KeyValuePair<Type, object>(IServiceProvidersFeatureType, _currentIServiceProvidersFeature as global::Microsoft.AspNetCore.Http.Features.IServiceProvidersFeature);
}
if (_currentIHttpRequestLifetimeFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpRequestLifetimeFeatureType, _currentIHttpRequestLifetimeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpRequestLifetimeFeature);
}
if (_currentIHttpConnectionFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpConnectionFeatureType, _currentIHttpConnectionFeature as global::Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature);
}
if (_currentIHttpAuthenticationFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature as global::Microsoft.AspNetCore.Http.Features.Authentication.IHttpAuthenticationFeature);
}
if (_currentIQueryFeature != null)
{
yield return new KeyValuePair<Type, object>(IQueryFeatureType, _currentIQueryFeature as global::Microsoft.AspNetCore.Http.Features.IQueryFeature);
}
if (_currentIFormFeature != null)
{
yield return new KeyValuePair<Type, object>(IFormFeatureType, _currentIFormFeature as global::Microsoft.AspNetCore.Http.Features.IFormFeature);
}
if (_currentIHttpUpgradeFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpUpgradeFeatureType, _currentIHttpUpgradeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpUpgradeFeature);
}
if (_currentIResponseCookiesFeature != null)
{
yield return new KeyValuePair<Type, object>(IResponseCookiesFeatureType, _currentIResponseCookiesFeature as global::Microsoft.AspNetCore.Http.Features.IResponseCookiesFeature);
}
if (_currentIItemsFeature != null)
{
yield return new KeyValuePair<Type, object>(IItemsFeatureType, _currentIItemsFeature as global::Microsoft.AspNetCore.Http.Features.IItemsFeature);
}
if (_currentITlsConnectionFeature != null)
{
yield return new KeyValuePair<Type, object>(ITlsConnectionFeatureType, _currentITlsConnectionFeature as global::Microsoft.AspNetCore.Http.Features.ITlsConnectionFeature);
}
if (_currentIHttpWebSocketFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpWebSocketFeatureType, _currentIHttpWebSocketFeature as global::Microsoft.AspNetCore.Http.Features.IHttpWebSocketFeature);
}
if (_currentISessionFeature != null)
{
yield return new KeyValuePair<Type, object>(ISessionFeatureType, _currentISessionFeature as global::Microsoft.AspNetCore.Http.Features.ISessionFeature);
}
if (_currentIHttpBodyControlFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpBodyControlFeatureType, _currentIHttpBodyControlFeature as global::Microsoft.AspNetCore.Http.Features.IHttpBodyControlFeature);
}
if (_currentIHttpSendFileFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpSendFileFeatureType, _currentIHttpSendFileFeature as global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
}
if (_currentIServerVariablesFeature != null)
{
yield return new KeyValuePair<Type, object>(IServerVariablesFeature, _currentIServerVariablesFeature as global::Microsoft.AspNetCore.Http.Features.IServerVariablesFeature);
}
if (MaybeExtra != null)
{
foreach (var item in MaybeExtra)
{
yield return item;
}
}
}
}
}

View File

@ -1,100 +0,0 @@
// 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.Globalization;
using System.Net;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal partial class IISHttpContext : IHttpConnectionFeature
{
IPAddress IHttpConnectionFeature.RemoteIpAddress
{
get
{
if (RemoteIpAddress == null)
{
InitializeRemoteEndpoint();
}
return RemoteIpAddress;
}
set => RemoteIpAddress = value;
}
IPAddress IHttpConnectionFeature.LocalIpAddress
{
get
{
if (LocalIpAddress == null)
{
InitializeLocalEndpoint();
}
return LocalIpAddress;
}
set => LocalIpAddress = value;
}
int IHttpConnectionFeature.RemotePort
{
get
{
if (RemoteIpAddress == null)
{
InitializeRemoteEndpoint();
}
return RemotePort;
}
set => RemotePort = value;
}
int IHttpConnectionFeature.LocalPort
{
get
{
if (LocalIpAddress == null)
{
InitializeLocalEndpoint();
}
return LocalPort;
}
set => LocalPort = value;
}
string IHttpConnectionFeature.ConnectionId
{
get
{
if (RequestConnectionId == null)
{
InitializeConnectionId();
}
return RequestConnectionId;
}
set => RequestConnectionId = value;
}
private void InitializeLocalEndpoint()
{
var localEndPoint = GetLocalEndPoint();
LocalIpAddress = localEndPoint.GetIPAddress();
LocalPort = localEndPoint.GetPort();
}
private void InitializeRemoteEndpoint()
{
var remoteEndPoint = GetRemoteEndPoint();
RemoteIpAddress = remoteEndPoint.GetIPAddress();
RemotePort = remoteEndPoint.GetPort();
}
private void InitializeConnectionId()
{
RequestConnectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
}
}
}

View File

@ -1,38 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal partial class IISHttpContext : IHttpRequestIdentifierFeature
{
string IHttpRequestIdentifierFeature.TraceIdentifier
{
get
{
if (TraceIdentifier == null)
{
InitializeHttpRequestIdentifierFeature();
}
return TraceIdentifier;
}
set => TraceIdentifier = value;
}
private unsafe void InitializeHttpRequestIdentifierFeature()
{
// Copied from WebListener
// This is the base GUID used by HTTP.SYS for generating the activity ID.
// HTTP.SYS overwrites the first 8 bytes of the base GUID with RequestId to generate ETW activity ID.
// The requestId should be set by the NativeRequestContext
var guid = new Guid(0xffcb4c93, 0xa57f, 0x453c, 0xb6, 0x3f, 0x84, 0x71, 0xc, 0x79, 0x67, 0xbb);
*((ulong*)&guid) = RequestId;
// TODO: Also make this not slow
TraceIdentifier = guid.ToString();
}
}
}

View File

@ -1,475 +0,0 @@
// 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.Buffers;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.HttpSys.Internal;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal partial class IISHttpContext
{
private const int HttpDataChunkStackLimit = 128; // 16 bytes per HTTP_DATA_CHUNK
/// <summary>
/// Reads data from the Input pipe to the user.
/// </summary>
/// <param name="memory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal async Task<int> ReadAsync(Memory<byte> memory, CancellationToken cancellationToken)
{
StartProcessingRequestAndResponseBody();
while (true)
{
var result = await Input.Reader.ReadAsync();
var readableBuffer = result.Buffer;
try
{
if (!readableBuffer.IsEmpty)
{
var actual = Math.Min(readableBuffer.Length, memory.Length);
readableBuffer = readableBuffer.Slice(0, actual);
readableBuffer.CopyTo(memory.Span);
return (int)actual;
}
else if (result.IsCompleted)
{
return 0;
}
}
finally
{
Input.Reader.AdvanceTo(readableBuffer.End, readableBuffer.End);
}
}
}
/// <summary>
/// Writes data to the output pipe.
/// </summary>
/// <param name="memory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal Task WriteAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken = default(CancellationToken))
{
// Want to keep exceptions consistent,
if (!_hasResponseStarted)
{
return WriteAsyncAwaited(memory, cancellationToken);
}
lock (_stateSync)
{
DisableReads();
return Output.WriteAsync(memory, cancellationToken);
}
}
/// <summary>
/// Flushes the data in the output pipe
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (!_hasResponseStarted)
{
return FlushAsyncAwaited(cancellationToken);
}
lock (_stateSync)
{
DisableReads();
return Output.FlushAsync(cancellationToken);
}
}
private void StartProcessingRequestAndResponseBody()
{
if (_processBodiesTask == null)
{
lock (_createReadWriteBodySync)
{
if (_processBodiesTask == null)
{
_processBodiesTask = ConsumeAsync();
}
}
}
}
private async Task FlushAsyncAwaited(CancellationToken cancellationToken)
{
await InitializeResponseAwaited();
Task flushTask;
lock (_stateSync)
{
DisableReads();
// Want to guarantee that data has been written to the pipe before releasing the lock.
flushTask = Output.FlushAsync(cancellationToken: cancellationToken);
}
await flushTask;
}
private async Task WriteAsyncAwaited(ReadOnlyMemory<byte> data, CancellationToken cancellationToken)
{
// WriteAsyncAwaited is only called for the first write to the body.
// Ensure headers are flushed if Write(Chunked)Async isn't called.
await InitializeResponseAwaited();
Task writeTask;
lock (_stateSync)
{
DisableReads();
// Want to guarantee that data has been written to the pipe before releasing the lock.
writeTask = Output.WriteAsync(data, cancellationToken: cancellationToken);
}
await writeTask;
}
// ConsumeAsync is called when either the first read or first write is done.
// There are two modes for reading and writing to the request/response bodies without upgrade.
// 1. Await all reads and try to read from the Output pipe
// 2. Done reading and await all writes.
// If the request is upgraded, we will start bidirectional streams for the input and output.
private async Task ConsumeAsync()
{
await ReadAndWriteLoopAsync();
// The ReadAndWriteLoop can return due to being upgraded. Check if _wasUpgraded is true to determine
// whether we go to a bidirectional stream or only write.
if (_wasUpgraded)
{
await StartBidirectionalStream();
}
}
private unsafe IISAwaitable ReadFromIISAsync(int length)
{
Action completion = null;
lock (_stateSync)
{
// We don't want to read if there is data available in the output pipe
// Therefore, we mark the current operation as cancelled to allow for the read
// to be requeued.
if (Output.Reader.TryRead(out var result))
{
// If the buffer is empty, it is considered a write of zero.
// we still want to cancel and allow the write to occur.
completion = _operation.GetCompletion(hr: IISServerConstants.HResultCancelIO, cbBytes: 0);
Output.Reader.AdvanceTo(result.Buffer.Start);
}
else
{
var hr = NativeMethods.HttpReadRequestBytes(
_pInProcessHandler,
(byte*)_inputHandle.Pointer,
length,
out var dwReceivedBytes,
out bool fCompletionExpected);
// if we complete the read synchronously, there is no need to set the reading flag
// as there is no cancelable operation.
if (!fCompletionExpected)
{
completion = _operation.GetCompletion(hr, dwReceivedBytes);
}
else
{
_reading = true;
}
}
}
// Invoke the completion outside of the lock if the reead finished synchronously.
completion?.Invoke();
return _operation;
}
private unsafe IISAwaitable WriteToIISAsync(ReadOnlySequence<byte> buffer)
{
var fCompletionExpected = false;
var hr = 0;
var nChunks = 0;
// Count the number of chunks in memory.
if (buffer.IsSingleSegment)
{
nChunks = 1;
}
else
{
foreach (var memory in buffer)
{
nChunks++;
}
}
if (nChunks == 1)
{
// If there is only a single chunk, use fixed to get a pointer to the buffer
var pDataChunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[1];
fixed (byte* pBuffer = &MemoryMarshal.GetReference(buffer.First.Span))
{
ref var chunk = ref pDataChunks[0];
chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
chunk.fromMemory.pBuffer = (IntPtr)pBuffer;
chunk.fromMemory.BufferLength = (uint)buffer.Length;
hr = NativeMethods.HttpWriteResponseBytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected);
}
}
else if (nChunks < HttpDataChunkStackLimit)
{
// To avoid stackoverflows, we will only stackalloc if the write size is less than the StackChunkLimit
// The stack size is IIS is by default 128/256 KB, so we are generous with this threshold.
var pDataChunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[nChunks];
hr = WriteSequenceToIIS(nChunks, buffer, pDataChunks, out fCompletionExpected);
}
else
{
// Otherwise allocate the chunks on the heap.
var chunks = new HttpApiTypes.HTTP_DATA_CHUNK[nChunks];
fixed (HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks = chunks)
{
hr = WriteSequenceToIIS(nChunks, buffer, pDataChunks, out fCompletionExpected);
}
}
if (!fCompletionExpected)
{
_operation.Complete(hr, 0);
}
return _operation;
}
private unsafe int WriteSequenceToIIS(int nChunks, ReadOnlySequence<byte> buffer, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, out bool fCompletionExpected)
{
var currentChunk = 0;
var hr = 0;
// REVIEW: We don't really need this list since the memory is already pinned with the default pool,
// but shouldn't assume the pool implementation right now. Unfortunately, this causes a heap allocation...
var handles = new MemoryHandle[nChunks];
foreach (var b in buffer)
{
ref var handle = ref handles[currentChunk];
ref var chunk = ref pDataChunks[currentChunk];
handle = b.Pin();
chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
chunk.fromMemory.BufferLength = (uint)b.Length;
chunk.fromMemory.pBuffer = (IntPtr)handle.Pointer;
currentChunk++;
}
hr = NativeMethods.HttpWriteResponseBytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected);
// Free the handles
foreach (var handle in handles)
{
handle.Dispose();
}
return hr;
}
private unsafe IISAwaitable FlushToIISAsync()
{
// Calls flush
var hr = 0;
hr = NativeMethods.HttpFlushResponseBytes(_pInProcessHandler, out var fCompletionExpected);
if (!fCompletionExpected)
{
_operation.Complete(hr, 0);
}
return _operation;
}
/// <summary>
/// Main function for control flow with IIS.
/// Uses two Pipes (Input and Output) between application calls to Read/Write/FlushAsync
/// Control Flow:
/// Try to see if there is data written by the application code (using TryRead)
/// and write it to IIS.
/// Check if the connection has been upgraded and call StartBidirectionalStreams
/// if it has.
/// Await reading from IIS, which will be cancelled if application code calls Write/FlushAsync.
/// </summary>
/// <returns>The Reading and Writing task.</returns>
private async Task ReadAndWriteLoopAsync()
{
try
{
while (true)
{
// First we check if there is anything to write from the Output pipe
// If there is, we call WriteToIISAsync
// Check if Output pipe has anything to write to IIS.
if (Output.Reader.TryRead(out var readResult))
{
var buffer = readResult.Buffer;
try
{
if (!buffer.IsEmpty)
{
// Write to IIS buffers
// Guaranteed to write the entire buffer to IIS
await WriteToIISAsync(buffer);
}
else if (readResult.IsCompleted)
{
break;
}
else
{
// Flush of zero bytes
await FlushToIISAsync();
}
}
finally
{
// Always Advance the data pointer to the end of the buffer.
Output.Reader.AdvanceTo(buffer.End);
}
}
// Check if there was an upgrade. If there is, we will replace the request and response bodies with
// two seperate loops. These will still be using the same Input and Output pipes here.
if (_upgradeTcs?.TrySetResult(null) == true)
{
// _wasUpgraded will be set at this point, exit the loop and we will check if we upgraded or not
// when going to next read/write type.
return;
}
// Now we handle the read.
var memory = Input.Writer.GetMemory();
_inputHandle = memory.Pin();
try
{
// Lock around invoking ReadFromIISAsync as we don't want to call CancelIo
// when calling read
var read = await ReadFromIISAsync(memory.Length);
// read value of 0 == done reading
// read value of -1 == read cancelled, still allowed to read but we
// need a write to occur first.
if (read == 0)
{
break;
}
else if (read == -1)
{
continue;
}
Input.Writer.Advance(read);
}
finally
{
// Always commit any changes to the Input pipe
_inputHandle.Dispose();
}
// Flush the read data for the Input Pipe writer
var flushResult = await Input.Writer.FlushAsync();
// If the pipe was closed, we are done reading,
if (flushResult.IsCompleted || flushResult.IsCanceled)
{
break;
}
}
// Complete the input writer as we are done reading the request body.
Input.Writer.Complete();
}
catch (Exception ex)
{
Input.Writer.Complete(ex);
}
await WriteLoopAsync();
}
/// <summary>
/// Secondary function for control flow with IIS. This is only called once we are done
/// reading the request body. We now await reading from the Output pipe.
/// </summary>
/// <returns></returns>
private async Task WriteLoopAsync()
{
try
{
while (true)
{
// Reading is done, so we will await all reads from the output pipe
var readResult = await Output.Reader.ReadAsync();
// Get data from pipe
var buffer = readResult.Buffer;
try
{
if (!buffer.IsEmpty)
{
// Write to IIS buffers
// Guaranteed to write the entire buffer to IIS
await WriteToIISAsync(buffer);
}
else if (readResult.IsCompleted)
{
break;
}
else
{
// Flush of zero bytes will
await FlushToIISAsync();
}
}
finally
{
// Always Advance the data pointer to the end of the buffer.
Output.Reader.AdvanceTo(buffer.End);
}
}
// Close the output pipe as we are done reading from it.
Output.Reader.Complete();
}
catch (Exception ex)
{
Output.Reader.Complete(ex);
}
}
// Always called from within a lock
private void DisableReads()
{
// To avoid concurrent reading and writing, if we have a pending read,
// we must cancel it.
// _reading will always be false if we upgrade to websockets, so we don't need to check wasUpgrade
// Also, we set _reading to false after cancelling to detect redundant calls
if (_reading)
{
_reading = false;
// Calls IHttpContext->CancelIo(), which will cause the OnAsyncCompletion handler to fire.
NativeMethods.HttpTryCancelIO(_pInProcessHandler);
}
}
}
}

View File

@ -1,225 +0,0 @@
// 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.Buffers;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.HttpSys.Internal;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
/// <summary>
/// Represents the websocket portion of the <see cref="IISHttpContext"/>
/// </summary>
internal partial class IISHttpContext
{
private bool _wasUpgraded; // Used for detecting repeated upgrades in IISHttpContext
private IISAwaitable _readWebSocketsOperation;
private IISAwaitable _writeWebSocketsOperation;
private TaskCompletionSource<object> _upgradeTcs;
private Task StartBidirectionalStream()
{
// IIS allows for websocket support and duplex channels only on Win8 and above
// This allows us to have two tasks for reading the request and writing the response
var readWebsocketTask = ReadWebSockets();
var writeWebsocketTask = WriteWebSockets();
return Task.WhenAll(readWebsocketTask, writeWebsocketTask);
}
public async Task UpgradeAsync()
{
if (_upgradeTcs == null)
{
_upgradeTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
// Flush any contents of the OutputPipe before upgrading to websockets.
await FlushAsync();
await _upgradeTcs.Task;
}
}
private unsafe IISAwaitable ReadWebSocketsFromIISAsync(int length)
{
var hr = 0;
int dwReceivedBytes;
bool fCompletionExpected;
// For websocket calls, we can directly provide a callback function to be called once the websocket operation completes.
hr = NativeMethods.HttpWebsocketsReadBytes(
_pInProcessHandler,
(byte*)_inputHandle.Pointer,
length,
IISAwaitable.ReadCallback,
(IntPtr)_thisHandle,
out dwReceivedBytes,
out fCompletionExpected);
if (!fCompletionExpected)
{
CompleteReadWebSockets(hr, dwReceivedBytes);
}
return _readWebSocketsOperation;
}
private unsafe IISAwaitable WriteWebSocketsFromIISAsync(ReadOnlySequence<byte> buffer)
{
var fCompletionExpected = false;
var hr = 0;
var nChunks = 0;
if (buffer.IsSingleSegment)
{
nChunks = 1;
}
else
{
foreach (var memory in buffer)
{
nChunks++;
}
}
if (buffer.IsSingleSegment)
{
var pDataChunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[1];
fixed (byte* pBuffer = &MemoryMarshal.GetReference(buffer.First.Span))
{
ref var chunk = ref pDataChunks[0];
chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
chunk.fromMemory.pBuffer = (IntPtr)pBuffer;
chunk.fromMemory.BufferLength = (uint)buffer.Length;
hr = NativeMethods.HttpWebsocketsWriteBytes(_pInProcessHandler, pDataChunks, nChunks, IISAwaitable.WriteCallback, (IntPtr)_thisHandle, out fCompletionExpected);
}
}
else
{
// REVIEW: Do we need to guard against this getting too big? It seems unlikely that we'd have more than say 10 chunks in real life
var pDataChunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[nChunks];
var currentChunk = 0;
// REVIEW: We don't really need this list since the memory is already pinned with the default pool,
// but shouldn't assume the pool implementation right now. Unfortunately, this causes a heap allocation...
var handles = new MemoryHandle[nChunks];
foreach (var b in buffer)
{
ref var handle = ref handles[currentChunk];
ref var chunk = ref pDataChunks[currentChunk];
handle = b.Pin();
chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
chunk.fromMemory.BufferLength = (uint)b.Length;
chunk.fromMemory.pBuffer = (IntPtr)handle.Pointer;
currentChunk++;
}
hr = NativeMethods.HttpWebsocketsWriteBytes(_pInProcessHandler, pDataChunks, nChunks, IISAwaitable.WriteCallback, (IntPtr)_thisHandle, out fCompletionExpected);
foreach (var handle in handles)
{
handle.Dispose();
}
}
if (!fCompletionExpected)
{
CompleteWriteWebSockets(hr, 0);
}
return _writeWebSocketsOperation;
}
internal void CompleteWriteWebSockets(int hr, int cbBytes)
{
_writeWebSocketsOperation.Complete(hr, cbBytes);
}
internal void CompleteReadWebSockets(int hr, int cbBytes)
{
_readWebSocketsOperation.Complete(hr, cbBytes);
}
private async Task ReadWebSockets()
{
try
{
while (true)
{
var memory = Input.Writer.GetMemory();
_inputHandle = memory.Pin();
try
{
int read = 0;
read = await ReadWebSocketsFromIISAsync(memory.Length);
if (read == 0)
{
break;
}
Input.Writer.Advance(read);
}
finally
{
_inputHandle.Dispose();
}
var result = await Input.Writer.FlushAsync();
if (result.IsCompleted || result.IsCanceled)
{
break;
}
}
Input.Writer.Complete();
}
catch (Exception ex)
{
Input.Writer.Complete(ex);
}
}
private async Task WriteWebSockets()
{
try
{
while (true)
{
var result = await Output.Reader.ReadAsync();
var buffer = result.Buffer;
var consumed = buffer.End;
try
{
if (!buffer.IsEmpty)
{
await WriteWebSocketsFromIISAsync(buffer);
}
else if (result.IsCompleted)
{
break;
}
}
finally
{
Output.Reader.AdvanceTo(consumed);
}
}
Output.Reader.Complete();
}
catch (Exception ex)
{
Output.Reader.Complete(ex);
}
}
}
}

View File

@ -1,514 +0,0 @@
// 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.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Pipelines;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpSys.Internal;
using Microsoft.AspNetCore.WebUtilities;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal abstract partial class IISHttpContext : NativeRequestContext, IDisposable
{
private const int MinAllocBufferSize = 2048;
private const int PauseWriterThreshold = 65536;
private const int ResumeWriterTheshold = PauseWriterThreshold / 2;
protected readonly IntPtr _pInProcessHandler;
private readonly IISOptions _options;
private bool _reading; // To know whether we are currently in a read operation.
private volatile bool _hasResponseStarted;
private int _statusCode;
private string _reasonPhrase;
private readonly object _onStartingSync = new object();
private readonly object _onCompletedSync = new object();
private readonly object _stateSync = new object();
protected readonly object _createReadWriteBodySync = new object();
protected Stack<KeyValuePair<Func<object, Task>, object>> _onStarting;
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
protected Exception _applicationException;
private readonly MemoryPool<byte> _memoryPool;
private readonly IISHttpServer _server;
private GCHandle _thisHandle;
private MemoryHandle _inputHandle;
private IISAwaitable _operation = new IISAwaitable();
protected Task _processBodiesTask;
protected int _requestAborted;
private const string NtlmString = "NTLM";
private const string NegotiateString = "Negotiate";
private const string BasicString = "Basic";
internal unsafe IISHttpContext(MemoryPool<byte> memoryPool, IntPtr pInProcessHandler, IISOptions options, IISHttpServer server)
: base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.HttpGetRawRequest(pInProcessHandler))
{
_memoryPool = memoryPool;
_pInProcessHandler = pInProcessHandler;
_options = options;
_server = server;
}
public Version HttpVersion { get; set; }
public string Scheme { get; set; }
public string Method { get; set; }
public string PathBase { get; set; }
public string Path { get; set; }
public string QueryString { get; set; }
public string RawTarget { get; set; }
public CancellationToken RequestAborted { get; set; }
public bool HasResponseStarted => _hasResponseStarted;
public IPAddress RemoteIpAddress { get; set; }
public int RemotePort { get; set; }
public IPAddress LocalIpAddress { get; set; }
public int LocalPort { get; set; }
public string RequestConnectionId { get; set; }
public string TraceIdentifier { get; set; }
public ClaimsPrincipal User { get; set; }
internal WindowsPrincipal WindowsUser { get; set; }
public Stream RequestBody { get; set; }
public Stream ResponseBody { get; set; }
public Pipe Input { get; set; }
public OutputProducer Output { get; set; }
public IHeaderDictionary RequestHeaders { get; set; }
public IHeaderDictionary ResponseHeaders { get; set; }
private HeaderCollection HttpResponseHeaders { get; set; }
internal HttpApiTypes.HTTP_VERB KnownMethod { get; private set; }
protected void InitializeContext()
{
_thisHandle = GCHandle.Alloc(this);
NativeMethods.HttpSetManagedContext(_pInProcessHandler, (IntPtr)_thisHandle);
Method = GetVerb();
RawTarget = GetRawUrl();
// TODO version is slow.
HttpVersion = GetVersion();
Scheme = SslStatus != SslStatus.Insecure ? Constants.HttpsScheme : Constants.HttpScheme;
KnownMethod = VerbId;
StatusCode = 200;
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(GetRawUrlInBytes());
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
{
PathBase = string.Empty;
Path = string.Empty;
}
else
{
// Path and pathbase are unescaped by RequestUriBuilder
// The UsePathBase middleware will modify the pathbase and path correctly
PathBase = string.Empty;
Path = originalPath;
}
var cookedUrl = GetCookedUrl();
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
RequestHeaders = new RequestHeaders(this);
HttpResponseHeaders = new HeaderCollection();
ResponseHeaders = HttpResponseHeaders;
if (_options.ForwardWindowsAuthentication)
{
WindowsUser = GetWindowsPrincipal();
if (_options.AutomaticAuthentication)
{
User = WindowsUser;
}
}
ResetFeatureCollection();
if (!_server.IsWebSocketAvailible(_pInProcessHandler))
{
_currentIHttpUpgradeFeature = null;
}
RequestBody = new IISHttpRequestBody(this);
ResponseBody = new IISHttpResponseBody(this);
Input = new Pipe(new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.ThreadPool, minimumSegmentSize: MinAllocBufferSize));
var pipe = new Pipe(
new PipeOptions(
_memoryPool,
readerScheduler: PipeScheduler.ThreadPool,
pauseWriterThreshold: PauseWriterThreshold,
resumeWriterThreshold: ResumeWriterTheshold,
minimumSegmentSize: MinAllocBufferSize));
Output = new OutputProducer(pipe);
}
public int StatusCode
{
get { return _statusCode; }
set
{
if (_hasResponseStarted)
{
ThrowResponseAlreadyStartedException(nameof(StatusCode));
}
_statusCode = (ushort)value;
}
}
public string ReasonPhrase
{
get { return _reasonPhrase; }
set
{
if (_hasResponseStarted)
{
ThrowResponseAlreadyStartedException(nameof(ReasonPhrase));
}
_reasonPhrase = value;
}
}
internal IISHttpServer Server
{
get { return _server; }
}
private async Task InitializeResponseAwaited()
{
await FireOnStarting();
if (_applicationException != null)
{
ThrowResponseAbortedException();
}
await ProduceStart(appCompleted: false);
}
private void ThrowResponseAbortedException()
{
throw new ObjectDisposedException("Unhandled application exception", _applicationException);
}
private async Task ProduceStart(bool appCompleted)
{
if (_hasResponseStarted)
{
return;
}
_hasResponseStarted = true;
SendResponseHeaders(appCompleted);
// On first flush for websockets, we need to flush the headers such that
// IIS will know that an upgrade occured.
// If we don't have anything on the Output pipe, the TryRead in ReadAndWriteLoopAsync
// will fail and we will signal the upgradeTcs that we are upgrading. However, we still
// didn't flush. To fix this, we flush 0 bytes right after writing the headers.
Task flushTask;
lock (_stateSync)
{
DisableReads();
flushTask = Output.FlushAsync();
}
await flushTask;
StartProcessingRequestAndResponseBody();
}
protected Task ProduceEnd()
{
if (_applicationException != null)
{
if (_hasResponseStarted)
{
// We can no longer change the response, so we simply close the connection.
return Task.CompletedTask;
}
// If the request was rejected, the error state has already been set by SetBadRequestState and
// that should take precedence.
else
{
// 500 Internal Server Error
SetErrorResponseHeaders(statusCode: StatusCodes.Status500InternalServerError);
}
}
if (!_hasResponseStarted)
{
return ProduceEndAwaited();
}
return Task.CompletedTask;
}
private void SetErrorResponseHeaders(int statusCode)
{
StatusCode = statusCode;
ReasonPhrase = string.Empty;
HttpResponseHeaders.Clear();
}
private async Task ProduceEndAwaited()
{
if (_hasResponseStarted)
{
return;
}
_hasResponseStarted = true;
SendResponseHeaders(appCompleted: true);
StartProcessingRequestAndResponseBody();
Task flushAsync;
lock (_stateSync)
{
DisableReads();
flushAsync = Output.FlushAsync();
}
await flushAsync;
}
public unsafe void SendResponseHeaders(bool appCompleted)
{
// Verifies we have sent the statuscode before writing a header
var reasonPhrase = string.IsNullOrEmpty(ReasonPhrase) ? ReasonPhrases.GetReasonPhrase(StatusCode) : ReasonPhrase;
// This copies data into the underlying buffer
NativeMethods.HttpSetResponseStatusCode(_pInProcessHandler, (ushort)StatusCode, reasonPhrase);
HttpResponseHeaders.IsReadOnly = true;
foreach (var headerPair in HttpResponseHeaders)
{
var headerValues = headerPair.Value;
var knownHeaderIndex = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerPair.Key);
if (knownHeaderIndex == -1)
{
var headerNameBytes = Encoding.UTF8.GetBytes(headerPair.Key);
for (var i = 0; i < headerValues.Count; i++)
{
var headerValueBytes = Encoding.UTF8.GetBytes(headerValues[i]);
fixed (byte* pHeaderName = headerNameBytes)
{
fixed (byte* pHeaderValue = headerValueBytes)
{
NativeMethods.HttpResponseSetUnknownHeader(_pInProcessHandler, pHeaderName, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: false);
}
}
}
}
else
{
for (var i = 0; i < headerValues.Count; i++)
{
var headerValueBytes = Encoding.UTF8.GetBytes(headerValues[i]);
fixed (byte* pHeaderValue = headerValueBytes)
{
NativeMethods.HttpResponseSetKnownHeader(_pInProcessHandler, knownHeaderIndex, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: false);
}
}
}
}
}
public void Abort()
{
// TODO
}
public abstract Task<bool> ProcessRequestAsync();
public void OnStarting(Func<object, Task> callback, object state)
{
lock (_onStartingSync)
{
if (_hasResponseStarted)
{
throw new InvalidOperationException("Response already started");
}
if (_onStarting == null)
{
_onStarting = new Stack<KeyValuePair<Func<object, Task>, object>>();
}
_onStarting.Push(new KeyValuePair<Func<object, Task>, object>(callback, state));
}
}
public void OnCompleted(Func<object, Task> callback, object state)
{
lock (_onCompletedSync)
{
if (_onCompleted == null)
{
_onCompleted = new Stack<KeyValuePair<Func<object, Task>, object>>();
}
_onCompleted.Push(new KeyValuePair<Func<object, Task>, object>(callback, state));
}
}
protected async Task FireOnStarting()
{
Stack<KeyValuePair<Func<object, Task>, object>> onStarting = null;
lock (_onStartingSync)
{
onStarting = _onStarting;
_onStarting = null;
}
if (onStarting != null)
{
try
{
foreach (var entry in onStarting)
{
await entry.Key.Invoke(entry.Value);
}
}
catch (Exception ex)
{
ReportApplicationError(ex);
}
}
}
protected async Task FireOnCompleted()
{
Stack<KeyValuePair<Func<object, Task>, object>> onCompleted = null;
lock (_onCompletedSync)
{
onCompleted = _onCompleted;
_onCompleted = null;
}
if (onCompleted != null)
{
foreach (var entry in onCompleted)
{
try
{
await entry.Key.Invoke(entry.Value);
}
catch (Exception ex)
{
ReportApplicationError(ex);
}
}
}
}
protected void ReportApplicationError(Exception ex)
{
if (_applicationException == null)
{
_applicationException = ex;
}
else if (_applicationException is AggregateException)
{
_applicationException = new AggregateException(_applicationException, ex).Flatten();
}
else
{
_applicationException = new AggregateException(_applicationException, ex);
}
}
public void PostCompletion(NativeMethods.REQUEST_NOTIFICATION_STATUS requestNotificationStatus)
{
Debug.Assert(!_operation.HasContinuation, "Pending async operation!");
NativeMethods.HttpSetCompletionStatus(_pInProcessHandler, requestNotificationStatus);
NativeMethods.HttpPostCompletion(_pInProcessHandler, 0);
}
public void IndicateCompletion(NativeMethods.REQUEST_NOTIFICATION_STATUS notificationStatus)
{
NativeMethods.HttpIndicateCompletion(_pInProcessHandler, notificationStatus);
}
internal void OnAsyncCompletion(int hr, int cbBytes)
{
// Must acquire the _stateSync here as anytime we call complete, we need to hold the lock
// to avoid races with cancellation.
Action continuation;
lock (_stateSync)
{
_reading = false;
continuation = _operation.GetCompletion(hr, cbBytes);
}
continuation?.Invoke();
}
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
_thisHandle.Free();
}
if (WindowsUser?.Identity is WindowsIdentity wi)
{
wi.Dispose();
}
disposedValue = true;
}
}
// This code added to correctly implement the disposable pattern.
public override void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(disposing: true);
}
private void ThrowResponseAlreadyStartedException(string value)
{
throw new InvalidOperationException("Response already started");
}
private WindowsPrincipal GetWindowsPrincipal()
{
NativeMethods.HttpGetAuthenticationInformation(_pInProcessHandler, out var authenticationType, out var token);
if (token != IntPtr.Zero && authenticationType != null)
{
if ((authenticationType.Equals(NtlmString, StringComparison.OrdinalIgnoreCase)
|| authenticationType.Equals(NegotiateString, StringComparison.OrdinalIgnoreCase)
|| authenticationType.Equals(BasicString, StringComparison.OrdinalIgnoreCase)))
{
return new WindowsPrincipal(new WindowsIdentity(token, authenticationType));
}
}
return null;
}
}
}

View File

@ -1,99 +0,0 @@
// 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.Buffers;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal class IISHttpContextOfT<TContext> : IISHttpContext
{
private readonly IHttpApplication<TContext> _application;
public IISHttpContextOfT(MemoryPool<byte> memoryPool, IHttpApplication<TContext> application, IntPtr pInProcessHandler, IISOptions options, IISHttpServer server)
: base(memoryPool, pInProcessHandler, options, server)
{
_application = application;
}
public override async Task<bool> ProcessRequestAsync()
{
InitializeContext();
var context = default(TContext);
var success = true;
try
{
context = _application.CreateContext(this);
await _application.ProcessRequestAsync(context);
// TODO Verification of Response
//if (Volatile.Read(ref _requestAborted) == 0)
//{
// VerifyResponseContentLength();
//}
}
catch (Exception ex)
{
ReportApplicationError(ex);
success = false;
}
finally
{
if (!HasResponseStarted && _applicationException == null && _onStarting != null)
{
await FireOnStarting();
// Dispose
}
if (_onCompleted != null)
{
await FireOnCompleted();
}
}
if (Volatile.Read(ref _requestAborted) == 0)
{
await ProduceEnd();
}
else if (!HasResponseStarted)
{
// If the request was aborted and no response was sent, there's no
// meaningful status code to log.
StatusCode = 0;
success = false;
}
try
{
_application.DisposeContext(context, _applicationException);
}
catch (Exception ex)
{
// TODO Log this
_applicationException = _applicationException ?? ex;
success = false;
}
finally
{
// The app is finished and there should be nobody writing to the response pipe
Output.Dispose();
// The app is finished and there should be nobody reading from the request pipe
Input.Reader.Complete();
Task processBodiesTask;
lock (_createReadWriteBodySync)
{
processBodiesTask = _processBodiesTask;
}
await processBodiesTask;
}
return success;
}
}
}

View File

@ -1,62 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal class IISHttpRequestBody : Stream
{
private readonly IISHttpContext _httpContext;
public IISHttpRequestBody(IISHttpContext httpContext)
{
_httpContext = httpContext;
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override void Flush()
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
}
public override unsafe Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var memory = new Memory<byte>(buffer, offset, count);
return _httpContext.ReadAsync(memory, cancellationToken);
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public unsafe override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}

View File

@ -1,65 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal class IISHttpResponseBody : Stream
{
private readonly IISHttpContext _httpContext;
public IISHttpResponseBody(IISHttpContext httpContext)
{
_httpContext = httpContext;
}
public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override void Flush()
{
FlushAsync(CancellationToken.None).GetAwaiter().GetResult();
}
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 unsafe override void Write(byte[] buffer, int offset, int count)
{
WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
}
public override unsafe Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return _httpContext.WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken);
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
return _httpContext.FlushAsync(cancellationToken);
}
}
}

View File

@ -1,229 +0,0 @@
// 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.Buffers;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal class IISHttpServer : IServer
{
private const string WebSocketVersionString = "WEBSOCKET_VERSION";
private static NativeMethods.PFN_REQUEST_HANDLER _requestHandler = HandleRequest;
private static NativeMethods.PFN_SHUTDOWN_HANDLER _shutdownHandler = HandleShutdown;
private static NativeMethods.PFN_ASYNC_COMPLETION _onAsyncCompletion = OnAsyncCompletion;
private IISContextFactory _iisContextFactory;
private readonly MemoryPool<byte> _memoryPool = new SlabMemoryPool();
private GCHandle _httpServerHandle;
private readonly IApplicationLifetime _applicationLifetime;
private readonly IAuthenticationSchemeProvider _authentication;
private readonly IISOptions _options;
private volatile int _stopping;
private bool Stopping => _stopping == 1;
private int _outstandingRequests;
private readonly TaskCompletionSource<object> _shutdownSignal = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private bool? _websocketAvailable;
public IFeatureCollection Features { get; } = new FeatureCollection();
// TODO: Remove pInProcessHandler argument
public bool IsWebSocketAvailible(IntPtr pInProcessHandler)
{
// Check if the Http upgrade feature is available in IIS.
// To check this, we can look at the server variable WEBSOCKET_VERSION
// And see if there is a version. Same check that Katana did:
// https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Host.SystemWeb/OwinAppContext.cs#L125
// Actively not locking here as acquiring a lock on every request will hurt perf more than checking the
// server variables a few extra times if a bunch of requests hit the server at the same time.
if (!_websocketAvailable.HasValue)
{
_websocketAvailable = NativeMethods.HttpTryGetServerVariable(pInProcessHandler, WebSocketVersionString, out var webSocketsSupported)
&& !string.IsNullOrEmpty(webSocketsSupported);
}
return _websocketAvailable.Value;
}
public IISHttpServer(IApplicationLifetime applicationLifetime, IAuthenticationSchemeProvider authentication, IOptions<IISOptions> options)
{
_applicationLifetime = applicationLifetime;
_authentication = authentication;
_options = options.Value;
if (_options.ForwardWindowsAuthentication)
{
authentication.AddScheme(new AuthenticationScheme(IISDefaults.AuthenticationScheme, _options.AuthenticationDisplayName, typeof(IISServerAuthenticationHandler)));
}
}
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
{
_httpServerHandle = GCHandle.Alloc(this);
_iisContextFactory = new IISContextFactory<TContext>(_memoryPool, application, _options, this);
// Start the server by registering the callback
NativeMethods.HttpRegisterCallbacks(_requestHandler, _shutdownHandler, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
void RegisterCancelation()
{
cancellationToken.Register(() =>
{
NativeMethods.HttpStopCallsIntoManaged();
_shutdownSignal.TrySetResult(null);
});
}
if (Interlocked.Exchange(ref _stopping, 1) == 1)
{
RegisterCancelation();
return _shutdownSignal.Task;
}
// First call back into native saying "DON'T SEND ME ANY MORE REQUESTS"
NativeMethods.HttpStopIncomingRequests();
try
{
// Wait for active requests to drain
if (_outstandingRequests > 0)
{
RegisterCancelation();
}
else
{
// We have drained all requests. Block any callbacks into managed at this point.
NativeMethods.HttpStopCallsIntoManaged();
_shutdownSignal.TrySetResult(null);
}
}
catch (Exception ex)
{
_shutdownSignal.TrySetException(ex);
}
return _shutdownSignal.Task;
}
public void Dispose()
{
_stopping = 1;
// Block any more calls into managed from native as we are unloading.
NativeMethods.HttpStopCallsIntoManaged();
_shutdownSignal.TrySetResult(null);
if (_httpServerHandle.IsAllocated)
{
_httpServerHandle.Free();
}
_memoryPool.Dispose();
}
private static NativeMethods.REQUEST_NOTIFICATION_STATUS HandleRequest(IntPtr pInProcessHandler, IntPtr pvRequestContext)
{
// Unwrap the server so we can create an http context and process the request
var server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
Interlocked.Increment(ref server._outstandingRequests);
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
ThreadPool.QueueUserWorkItem(state => _ = HandleRequest((IISHttpContext)state), context);
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
}
private static async Task HandleRequest(IISHttpContext context)
{
var result = await context.ProcessRequestAsync();
CompleteRequest(context, result);
}
private static bool HandleShutdown(IntPtr pvRequestContext)
{
var server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
server._applicationLifetime.StopApplication();
return true;
}
private static NativeMethods.REQUEST_NOTIFICATION_STATUS OnAsyncCompletion(IntPtr pvManagedHttpContext, int hr, int bytes)
{
var context = (IISHttpContext)GCHandle.FromIntPtr(pvManagedHttpContext).Target;
context.OnAsyncCompletion(hr, bytes);
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
}
private static void CompleteRequest(IISHttpContext context, bool result)
{
// Post completion after completing the request to resume the state machine
context.PostCompletion(ConvertRequestCompletionResults(result));
if (Interlocked.Decrement(ref context.Server._outstandingRequests) == 0 && context.Server.Stopping)
{
// All requests have been drained.
NativeMethods.HttpStopCallsIntoManaged();
context.Server._shutdownSignal.TrySetResult(null);
}
// Dispose the context
context.Dispose();
}
private static NativeMethods.REQUEST_NOTIFICATION_STATUS ConvertRequestCompletionResults(bool success)
{
return success ? NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_CONTINUE
: NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_FINISH_REQUEST;
}
private class IISContextFactory<T> : IISContextFactory
{
private readonly IHttpApplication<T> _application;
private readonly MemoryPool<byte> _memoryPool;
private readonly IISOptions _options;
private readonly IISHttpServer _server;
public IISContextFactory(MemoryPool<byte> memoryPool, IHttpApplication<T> application, IISOptions options, IISHttpServer server)
{
_application = application;
_memoryPool = memoryPool;
_options = options;
_server = server;
}
public IISHttpContext CreateHttpContext(IntPtr pInProcessHandler)
{
return new IISHttpContextOfT<T>(_memoryPool, _application, pInProcessHandler, _options, _server);
}
}
~IISHttpServer()
{
// If this finalize is invoked, try our best to block all calls into managed.
NativeMethods.HttpStopCallsIntoManaged();
}
}
// Over engineering to avoid allocations...
internal interface IISContextFactory
{
IISHttpContext CreateHttpContext(IntPtr pInProcessHandler);
}
}

View File

@ -1,58 +0,0 @@
// 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;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
public class IISServerAuthenticationHandler : IAuthenticationHandler
{
private HttpContext _context;
private IISHttpContext _iisHttpContext;
internal AuthenticationScheme Scheme { get; private set; }
public Task<AuthenticateResult> AuthenticateAsync()
{
var user = _iisHttpContext.WindowsUser;
if (user != null && user.Identity != null && user.Identity.IsAuthenticated)
{
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(user, Scheme.Name)));
}
else
{
return Task.FromResult(AuthenticateResult.NoResult());
}
}
public Task ChallengeAsync(AuthenticationProperties properties)
{
// We would normally set the www-authenticate header here, but IIS does that for us.
_context.Response.StatusCode = 401;
return Task.CompletedTask;
}
public Task ForbidAsync(AuthenticationProperties properties)
{
_context.Response.StatusCode = 403;
return Task.CompletedTask;
}
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
_iisHttpContext = context.Features.Get<IISHttpContext>();
if (_iisHttpContext == null)
{
throw new InvalidOperationException("No IISHttpContext found.");
}
Scheme = scheme;
_context = context;
return Task.CompletedTask;
}
}
}

View File

@ -1,10 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal static class IISServerConstants
{
internal const int HResultCancelIO = -2147023901;
}
}

View File

@ -1,36 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal class IISServerSetupFilter : IStartupFilter
{
private string _virtualPath;
public IISServerSetupFilter(string virtualPath)
{
_virtualPath = virtualPath;
}
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
var server = app.ApplicationServices.GetService<IServer>();
if (server?.GetType() != typeof(IISHttpServer))
{
throw new InvalidOperationException("Application is running inside IIS process but is not configured to use IIS server.");
}
app.UsePathBase(_virtualPath);
next(app);
};
}
}
}

View File

@ -1,149 +0,0 @@
// 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.Buffers;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal class OutputProducer
{
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
// This locks access to to all of the below fields
private readonly object _contextLock = new object();
private ValueTask<FlushResult> _flushTask;
private bool _completed = false;
private readonly Pipe _pipe;
// https://github.com/dotnet/corefxlab/issues/1334
// Pipelines don't support multiple awaiters on flush
// this is temporary until it does
private TaskCompletionSource<object> _flushTcs;
private readonly object _flushLock = new object();
private Action _flushCompleted;
public OutputProducer(Pipe pipe)
{
_pipe = pipe;
_flushCompleted = OnFlushCompleted;
}
public PipeReader Reader => _pipe.Reader;
public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
{
return WriteAsync(_emptyData, cancellationToken);
}
public void Dispose()
{
lock (_contextLock)
{
if (_completed)
{
return;
}
_completed = true;
_pipe.Writer.Complete();
}
}
public void Abort(Exception error)
{
lock (_contextLock)
{
if (_completed)
{
return;
}
_completed = true;
_pipe.Reader.CancelPendingRead();
_pipe.Writer.Complete(error);
}
}
public Task WriteAsync(
ReadOnlyMemory<byte> buffer,
CancellationToken cancellationToken)
{
lock (_contextLock)
{
if (_completed)
{
throw new ObjectDisposedException("Response is already completed");
}
_pipe.Writer.Write(buffer.Span);
}
return FlushAsync(_pipe.Writer, cancellationToken);
}
private Task FlushAsync(PipeWriter pipeWriter,
CancellationToken cancellationToken)
{
var awaitable = pipeWriter.FlushAsync(cancellationToken);
if (awaitable.IsCompleted)
{
// The flush task can't fail today
return Task.CompletedTask;
}
return FlushAsyncAwaited(awaitable, cancellationToken);
}
private async Task FlushAsyncAwaited(ValueTask<FlushResult> awaitable, CancellationToken cancellationToken)
{
// https://github.com/dotnet/corefxlab/issues/1334
// Since the flush awaitable doesn't currently support multiple awaiters
// we need to use a task to track the callbacks.
// All awaiters get the same task
lock (_flushLock)
{
_flushTask = awaitable;
if (_flushTcs == null || _flushTcs.Task.IsCompleted)
{
_flushTcs = new TaskCompletionSource<object>();
_flushTask.GetAwaiter().OnCompleted(_flushCompleted);
}
}
try
{
await _flushTcs.Task;
cancellationToken.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
{
_completed = true;
throw;
}
}
private void OnFlushCompleted()
{
try
{
_flushTask.GetAwaiter().GetResult();
_flushTcs.TrySetResult(null);
}
catch (Exception exception)
{
_flushTcs.TrySetResult(exception);
}
finally
{
_flushTask = default;
}
}
}
}

View File

@ -40,13 +40,6 @@ namespace Microsoft.AspNetCore.Hosting
return hostBuilder;
}
// Check if in process
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && NativeMethods.IsAspNetCoreModuleLoaded())
{
return SetupInProcessServer(hostBuilder);
}
var port = hostBuilder.GetSetting(ServerPort) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPort}");
var path = hostBuilder.GetSetting(ServerPath) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPath}");
var pairingToken = hostBuilder.GetSetting(PairingToken) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{PairingToken}");
@ -107,23 +100,5 @@ namespace Microsoft.AspNetCore.Hosting
return hostBuilder;
}
private static IWebHostBuilder SetupInProcessServer(IWebHostBuilder hostBuilder)
{
hostBuilder.UseSetting(nameof(UseIISIntegration), "true");
hostBuilder.CaptureStartupErrors(true);
var iisConfigData = NativeMethods.HttpGetApplicationProperties();
hostBuilder.UseContentRoot(iisConfigData.pwzFullApplicationPath);
return hostBuilder.ConfigureServices(
services => {
services.AddSingleton<IServer, IISHttpServer>();
services.AddSingleton<IStartupFilter>(new IISServerSetupFilter(iisConfigData.pwzVirtualApplicationPath));
services.AddAuthenticationCore();
services.Configure<IISOptions>(
options => { options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled; }
);
});
}
}
}

View File

@ -1,67 +0,0 @@
// 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.
#if NET461
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class AuthenticationTests
{
private readonly IISTestSiteFixture _fixture;
public AuthenticationTests(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task Authentication_InProcess_IISExpressAsync()
{
var response = await _fixture.Client.GetAsync("/AuthenticationAnonymous");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("Anonymous?True", responseText);
response = await _fixture.Client.GetAsync("/AuthenticationRestricted");
responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());
response = await _fixture.Client.GetAsync("/AuthenticationRestrictedNTLM");
responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
// Note we can't restrict a challenge to a specific auth type, the native auth modules always add themselves.
Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());
response = await _fixture.Client.GetAsync("/AuthenticationForbidden");
responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
var httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
var httpClient = _fixture.DeploymentResult.CreateHttpClient(httpClientHandler);
response = await httpClient.GetAsync("/AuthenticationAnonymous");
responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("Anonymous?True", responseText);
response = await httpClient.GetAsync("/AuthenticationRestricted");
responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotEmpty(responseText);
}
}
}
#elif NETCOREAPP2_0 || NETCOREAPP2_1
#else
#error Target frameworks need to be updated
#endif

View File

@ -1,51 +0,0 @@
// 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;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class EnvironmentVariableTests
{
private readonly IISTestSiteFixture _fixture;
public EnvironmentVariableTests(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task GetUniqueEnvironmentVariable()
{
Assert.Equal("foobar", await _fixture.Client.GetStringAsync("/CheckEnvironmentVariable"));
}
[ConditionalFact]
public async Task GetLongEnvironmentVariable()
{
Assert.Equal(
"AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" +
"AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" +
"AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" +
"AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" +
"AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" +
"AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative",
await _fixture.Client.GetStringAsync("/CheckEnvironmentLongValueVariable"));
}
[ConditionalFact]
public async Task GetExistingEnvironmentVariable()
{
Assert.Contains(";foobarbaz", await _fixture.Client.GetStringAsync("/CheckAppendedEnvironmentVariable"));
}
[ConditionalFact]
public async Task AuthHeaderEnvironmentVariableRemoved()
{
Assert.DoesNotContain("shouldberemoved", await _fixture.Client.GetStringAsync("/CheckRemoveAuthEnvironmentVariable"));
}
}
}

View File

@ -1,30 +0,0 @@
// 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;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class FeatureCollectionTest
{
private readonly IISTestSiteFixture _fixture;
public FeatureCollectionTest(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalTheory]
[InlineData("FeatureCollectionSetRequestFeatures")]
[InlineData("FeatureCollectionSetResponseFeatures")]
[InlineData("FeatureCollectionSetConnectionFeatures")]
public async Task FeatureCollectionTest_SetHttpContextFeatures(string path)
{
Assert.Equal("Success", await _fixture.Client.GetStringAsync(path + "/path" + "?query"));
}
}
}

View File

@ -1,31 +0,0 @@
// 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;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class HelloWorldInProcessTests
{
private readonly IISTestSiteFixture _fixture;
public HelloWorldInProcessTests(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task HelloWorld_InProcess_IISExpress_CoreClr_X64_Portable()
{
Assert.Equal("Hello World", await _fixture.Client.GetStringAsync("/HelloWorld"));
Assert.Equal("/Path??", await _fixture.Client.GetStringAsync("/HelloWorld/Path%3F%3F?query"));
Assert.Equal("?query", await _fixture.Client.GetStringAsync("/HelloWorld/Query%3F%3F?query"));
}
}
}

View File

@ -1,84 +0,0 @@
// 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.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class InvalidReadWriteOperationTests
{
private readonly IISTestSiteFixture _fixture;
public InvalidReadWriteOperationTests(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task TestReadOffsetWorks()
{
var result = await _fixture.Client.PostAsync($"/TestReadOffsetWorks", new StringContent("Hello World"));
Assert.Equal("Hello World", await result.Content.ReadAsStringAsync());
}
[ConditionalTheory]
[InlineData("/InvalidOffsetSmall")]
[InlineData("/InvalidOffsetLarge")]
[InlineData("/InvalidCountSmall")]
[InlineData("/InvalidCountLarge")]
[InlineData("/InvalidCountWithOffset")]
public async Task TestInvalidReadOperations(string operation)
{
var result = await _fixture.Client.GetStringAsync($"/TestInvalidReadOperations{operation}");
Assert.Equal("Success", result);
}
[ConditionalTheory]
[InlineData("/NullBuffer")]
[InlineData("/InvalidCountZeroRead")]
public async Task TestValidReadOperations(string operation)
{
var result = await _fixture.Client.GetStringAsync($"/TestValidReadOperations{operation}");
Assert.Equal("Success", result);
}
[ConditionalTheory]
[InlineData("/NullBufferPost")]
[InlineData("/InvalidCountZeroReadPost")]
public async Task TestValidReadOperationsPost(string operation)
{
var result = await _fixture.Client.PostAsync($"/TestValidReadOperations{operation}", new StringContent("hello"));
Assert.Equal("Success", await result.Content.ReadAsStringAsync());
}
[ConditionalTheory]
[InlineData("/InvalidOffsetSmall")]
[InlineData("/InvalidOffsetLarge")]
[InlineData("/InvalidCountSmall")]
[InlineData("/InvalidCountLarge")]
[InlineData("/InvalidCountWithOffset")]
public async Task TestInvalidWriteOperations(string operation)
{
var result = await _fixture.Client.GetStringAsync($"/TestInvalidWriteOperations{operation}");
Assert.Equal("Success", result);
}
[ConditionalFact]
public async Task TestValidWriteOperations()
{
var result = await _fixture.Client.GetStringAsync($"/TestValidWriteOperations/NullBuffer");
Assert.Equal("Success", result);
}
[ConditionalFact]
public async Task TestValidWriteOperationsPost()
{
var result = await _fixture.Client.PostAsync($"/TestValidWriteOperations/NullBufferPost", new StringContent("hello"));
Assert.Equal("Success", await result.Content.ReadAsStringAsync());
}
}
}

View File

@ -1,36 +0,0 @@
// 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;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class LargeResponseBodyTests
{
private readonly IISTestSiteFixture _fixture;
public LargeResponseBodyTests(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalTheory]
[InlineData(65000)]
[InlineData(1000000)]
[InlineData(10000000)]
[InlineData(100000000)]
public async Task LargeResponseBodyTest_CheckAllResponseBodyBytesWritten(int query)
{
Assert.Equal(new string('a', query), await _fixture.Client.GetStringAsync($"/LargeResponseBody?length={query}"));
}
[ConditionalFact]
public async Task LargeResponseBodyFromFile_CheckAllResponseBodyBytesWritten()
{
Assert.Equal(200000000, (await _fixture.Client.GetStringAsync($"/LargeResponseFile")).Length);
}
}
}

View File

@ -1,64 +0,0 @@
// 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.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class ResponseHeaders
{
private readonly IISTestSiteFixture _fixture;
public ResponseHeaders(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task AddResponseHeaders_HeaderValuesAreSetCorrectly()
{
var response = await _fixture.Client.GetAsync("ResponseHeaders");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("Request Complete", responseText);
Assert.True(response.Headers.TryGetValues("UnknownHeader", out var headerValues));
Assert.Equal("test123=foo", headerValues.First());
Assert.True(response.Content.Headers.TryGetValues(HeaderNames.ContentType, out headerValues));
Assert.Equal("text/plain", headerValues.First());
Assert.True(response.Headers.TryGetValues("MultiHeader", out headerValues));
Assert.Equal(2, headerValues.Count());
Assert.Equal("1", headerValues.First());
Assert.Equal("2", headerValues.Last());
}
[ConditionalFact]
public async Task ErrorCodeIsSetForExceptionDuringRequest()
{
var response = await _fixture.Client.GetAsync("Throw");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
Assert.Equal("Internal Server Error", response.ReasonPhrase);
}
[ConditionalTheory]
[InlineData(200, "custom", "custom")]
[InlineData(500, "", "Internal Server Error")]
[InlineData(999, "", "")]
public async Task CustomErrorCodeWorks(int code, string reason, string expectedReason)
{
var response = await _fixture.Client.GetAsync($"SetCustomErorCode?code={code}&reason={reason}");
Assert.Equal((HttpStatusCode)code, response.StatusCode);
Assert.Equal(expectedReason, response.ReasonPhrase);
Assert.Equal("Body", await response.Content.ReadAsStringAsync());
}
}
}

View File

@ -1,28 +0,0 @@
// 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;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class ResponseInvalidOrderingTest
{
private readonly IISTestSiteFixture _fixture;
public ResponseInvalidOrderingTest(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalTheory]
[InlineData("SetStatusCodeAfterWrite")]
[InlineData("SetHeaderAfterWrite")]
public async Task ResponseInvalidOrderingTests_ExpectFailure(string path)
{
Assert.Equal($"Started_{path}Threw_Finished", await _fixture.Client.GetStringAsync("/ResponseInvalidOrdering/" + path));
}
}
}

View File

@ -1,40 +0,0 @@
// 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;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class ServerVariablesTest
{
private readonly IISTestSiteFixture _fixture;
public ServerVariablesTest(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task ProvidesAccessToServerVariables()
{
var port = _fixture.Client.BaseAddress.Port;
Assert.Equal("SERVER_PORT: " + port, await _fixture.Client.GetStringAsync("/ServerVariable?q=SERVER_PORT"));
Assert.Equal("QUERY_STRING: q=QUERY_STRING", await _fixture.Client.GetStringAsync("/ServerVariable?q=QUERY_STRING"));
}
[ConditionalFact]
public async Task ReturnsNullForUndefinedServerVariable()
{
Assert.Equal("THIS_VAR_IS_UNDEFINED: (null)", await _fixture.Client.GetStringAsync("/ServerVariable?q=THIS_VAR_IS_UNDEFINED"));
}
[ConditionalFact]
public async Task BasePathIsNotPrefixedBySlashSlashQuestionMark()
{
Assert.DoesNotContain(@"\\?\", await _fixture.Client.GetStringAsync("/BasePath"));
}
}
}

View File

@ -1,234 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public class StartupTests : LoggedTest
{
public StartupTests(ITestOutputHelper output) : base(output)
{
}
[Fact]
public async Task ExpandEnvironmentVariableInWebConfig()
{
var architecture = RuntimeArchitecture.x64;
var dotnetLocation = $"%USERPROFILE%\\.dotnet\\{architecture.ToString()}\\dotnet.exe";
using (StartLog(out var loggerFactory))
{
var logger = loggerFactory.CreateLogger("HelloWorldTest");
var deploymentParameters = GetBaseDeploymentParameters();
// Point to dotnet installed in user profile.
deploymentParameters.EnvironmentVariables["DotnetPath"] = Environment.ExpandEnvironmentVariables(dotnetLocation); // Path to dotnet.
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
{
var deploymentResult = await deployer.DeployAsync();
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
var response = await RetryHelper.RetryRequest(() =>
{
return deploymentResult.HttpClient.GetAsync("HelloWorld");
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
var responseText = await response.Content.ReadAsStringAsync();
try
{
Assert.Equal("Hello World", responseText);
}
catch (XunitException)
{
logger.LogWarning(response.ToString());
logger.LogWarning(responseText);
throw;
}
}
}
}
[Fact]
public async Task InvalidProcessPath_ExpectServerError()
{
var dotnetLocation = "bogus";
using (StartLog(out var loggerFactory))
{
var logger = loggerFactory.CreateLogger("HelloWorldTest");
var deploymentParameters = GetBaseDeploymentParameters();
// Point to dotnet installed in user profile.
deploymentParameters.EnvironmentVariables["DotnetPath"] = Environment.ExpandEnvironmentVariables(dotnetLocation); // Path to dotnet.
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
{
var deploymentResult = await deployer.DeployAsync();
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%");
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
var response = await RetryHelper.RetryRequest(() =>
{
return deploymentResult.HttpClient.GetAsync("HelloWorld");
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
}
}
}
#if NETCOREAPP2_0 || NETCOREAPP2_1
[Fact] // Consistently fails on CI for net461
public async Task StandaloneApplication_ExpectCorrectPublish()
{
using (StartLog(out var loggerFactory))
{
var logger = loggerFactory.CreateLogger("HelloWorldTest");
var deploymentParameters = GetBaseDeploymentParameters();
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
{
var deploymentResult = await deployer.DeployAsync();
deploymentParameters.ApplicationType = ApplicationType.Standalone;
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
var response = await RetryHelper.RetryRequest(() =>
{
return deploymentResult.HttpClient.GetAsync("HelloWorld");
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
var responseText = await response.Content.ReadAsStringAsync();
try
{
Assert.Equal("Hello World", responseText);
}
catch (XunitException)
{
logger.LogWarning(response.ToString());
logger.LogWarning(responseText);
throw;
}
}
}
}
[Fact] // Consistently fails on CI for net461
public async Task StandaloneApplication_AbsolutePathToExe_ExpectCorrectPublish()
{
using (StartLog(out var loggerFactory))
{
var logger = loggerFactory.CreateLogger("HelloWorldTest");
var deploymentParameters = GetBaseDeploymentParameters();
deploymentParameters.ApplicationType = ApplicationType.Standalone;
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
{
var deploymentResult = await deployer.DeployAsync();
Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", $"{deploymentResult.ContentRoot}\\InProcessWebSite.exe");
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
var response = await RetryHelper.RetryRequest(() =>
{
return deploymentResult.HttpClient.GetAsync("HelloWorld");
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
var responseText = await response.Content.ReadAsStringAsync();
try
{
Assert.Equal("Hello World", responseText);
}
catch (XunitException)
{
logger.LogWarning(response.ToString());
logger.LogWarning(responseText);
throw;
}
}
}
}
#elif NET461
#else
#error Target frameworks need to be updated
#endif
[Fact]
public async Task DetectsOveriddenServer()
{
var testSink = new TestSink();
using (StartLog(out var loggerFactory))
{
var testLoggerFactory = new TestLoggerFactory(testSink, true);
loggerFactory.AddProvider(new TestLoggerProvider(testLoggerFactory));
using (var deployer = ApplicationDeployerFactory.Create(GetBaseDeploymentParameters("OverriddenServerWebSite"), loggerFactory))
{
var deploymentResult = await deployer.DeployAsync();
var response = await deploymentResult.HttpClient.GetAsync("/");
Assert.False(response.IsSuccessStatusCode);
}
}
Assert.Contains(testSink.Writes, context => context.State.ToString().Contains("Application is running inside IIS process but is not configured to use IIS server"));
}
private DeploymentParameters GetBaseDeploymentParameters(string site = null)
{
return new DeploymentParameters(Helpers.GetTestWebSitePath(site ?? "InProcessWebSite"), ServerType.IISExpress, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64)
{
ServerConfigTemplateContent = File.ReadAllText("AppHostConfig/Http.config"),
SiteName = "HttpTestSite", // This is configured in the Http.config
TargetFramework = "netcoreapp2.1",
ApplicationType = ApplicationType.Portable,
Configuration = GetCurrentConfiguration()
};
}
private static string GetCurrentConfiguration()
{
#if DEBUG
return "Debug";
#else
return "Release";
#endif
}
private class TestLoggerProvider : ILoggerProvider
{
private readonly TestLoggerFactory _loggerFactory;
public TestLoggerProvider(TestLoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public void Dispose()
{
}
public ILogger CreateLogger(string categoryName)
{
return _loggerFactory.CreateLogger(categoryName);
}
}
}
}

View File

@ -1,125 +0,0 @@
// 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;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class SynchronousReadAndWriteTests
{
private readonly IISTestSiteFixture _fixture;
public SynchronousReadAndWriteTests(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task ReadAndWriteSynchronously()
{
for (int i = 0; i < 100; i++)
{
var content = new StringContent(new string('a', 100000));
var response = await _fixture.Client.PostAsync("ReadAndWriteSynchronously", content);
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(expected: 110000, actual: responseText.Length);
}
}
[ConditionalFact]
public async Task ReadAndWriteEcho()
{
var body = new string('a', 100000);
var content = new StringContent(body);
var response = await _fixture.Client.PostAsync("ReadAndWriteEcho", content);
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(body, responseText);
}
[ConditionalFact]
public async Task ReadAndWriteCopyToAsync()
{
var body = new string('a', 100000);
var content = new StringContent(body);
var response = await _fixture.Client.PostAsync("ReadAndWriteCopyToAsync", content);
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(body, responseText);
}
[ConditionalFact]
public async Task ReadAndWriteEchoTwice()
{
var requestBody = new string('a', 10000);
var content = new StringContent(requestBody);
var response = await _fixture.Client.PostAsync("ReadAndWriteEchoTwice", content);
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal(requestBody.Length * 2, responseText.Length);
}
[ConditionalFact]
public void ReadAndWriteSlowConnection()
{
var ipHostEntry = Dns.GetHostEntry(_fixture.Client.BaseAddress.Host);
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
foreach (var hostEntry in ipHostEntry.AddressList)
{
try
{
socket.Connect(hostEntry, _fixture.Client.BaseAddress.Port);
break;
}
catch (Exception)
{
// Exceptions can be thrown based on ipv6 support
}
}
Assert.True(socket.Connected);
var testString = "hello world";
var request = $"POST /ReadAndWriteSlowConnection HTTP/1.0\r\n" +
$"Content-Length: {testString.Length}\r\n" +
"Host: " + "localhost\r\n" +
"\r\n";
var bytes = 0;
var requestStringBytes = Encoding.ASCII.GetBytes(request);
var testStringBytes = Encoding.ASCII.GetBytes(testString);
while ((bytes += socket.Send(requestStringBytes, bytes, 1, SocketFlags.None)) < requestStringBytes.Length)
{
}
bytes = 0;
while ((bytes += socket.Send(testStringBytes, bytes, 1, SocketFlags.None)) < testStringBytes.Length)
{
Thread.Sleep(100);
}
var stringBuilder = new StringBuilder();
var buffer = new byte[4096];
int size;
while ((size = socket.Receive(buffer, buffer.Length, SocketFlags.None)) != 0)
{
stringBuilder.Append(Encoding.ASCII.GetString(buffer, 0, size));
}
Assert.Contains(new StringBuilder().Insert(0, "hello world", 100).ToString(), stringBuilder.ToString());
}
}
}
}

View File

@ -22,26 +22,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
}
[Fact]
public Task UpgradeFeatureDetectionEnabled_InProcess_IISExpress_CoreClr_x64_Portable()
{
return UpgradeFeatureDetectionDeployer(RuntimeFlavor.CoreClr,
ApplicationType.Portable,
"AppHostConfig/WebsocketsNotSupported.config",
Helpers.GetInProcessTestSitesPath(),
"Disabled");
}
[Fact]
public Task UpgradeFeatureDetectionDisabled_InProcess_IISExpress_CoreClr_x64_Portable()
{
return UpgradeFeatureDetectionDeployer(RuntimeFlavor.CoreClr,
ApplicationType.Portable,
"AppHostConfig/Http.config",
Helpers.GetInProcessTestSitesPath(),
_isWebsocketsSupported);
}
[Fact]
public Task UpgradeFeatureDetectionEnabled_OutOfProcess_IISExpress_CoreClr_x64_Portable()
{
@ -75,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
var logger = loggerFactory.CreateLogger("HelloWorldTest");
var deploymentParameters = new DeploymentParameters(Helpers.GetInProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
var deploymentParameters = new DeploymentParameters(sitePath, serverType, runtimeFlavor, architecture)
{
EnvironmentName = "UpgradeFeatureDetection", // Will pick the Start class named 'StartupHelloWorld',
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText(configPath) : null,