Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Extensions.ObjectPool;

namespace Microsoft.AspNetCore.SystemWebAdapters.Features;

internal sealed class RequestHttpApplicationFeature : IHttpApplicationFeature, IHttpResponseEndFeature, IRequestExceptionFeature
internal sealed class HttpApplicationFeature : IHttpApplicationFeature, IHttpResponseEndFeature, IRequestExceptionFeature, IDisposable
{
private readonly IHttpResponseEndFeature _previous;
private readonly ObjectPool<HttpApplication> _pool;

private object? _contextOrApplication;
private List<Exception>? _exceptions;

public RequestHttpApplicationFeature(HttpApplication app, IHttpResponseEndFeature previousEnd)
public HttpApplicationFeature(HttpContextCore context, IHttpResponseEndFeature previousEnd, ObjectPool<HttpApplication> pool)
{
Application = app;
_contextOrApplication = context;
_pool = pool;
_previous = previousEnd;
}

Expand All @@ -25,7 +30,21 @@ public RequestHttpApplicationFeature(HttpApplication app, IHttpResponseEndFeatur

public bool IsEnded { get; private set; }

public HttpApplication Application { get; }
public HttpApplication Application => _contextOrApplication switch
{
HttpContextCore context => InitializeApplication(context),
HttpApplication app => app,
null => throw new InvalidOperationException("HttpApplication is no longer available in the request"),
_ => throw new InvalidOperationException("Unknown application instance")
};

private HttpApplication InitializeApplication(HttpContextCore context)
{
var app = _pool.Get();
app.Context = context;
_contextOrApplication = app;
return app;
}

ValueTask IHttpApplicationFeature.RaiseEventAsync(ApplicationEvent @event)
{
Expand Down Expand Up @@ -118,4 +137,13 @@ IReadOnlyList<Exception> IRequestExceptionFeature.Exceptions
=> ((IReadOnlyList<Exception>?)_exceptions) ?? Array.Empty<Exception>();

void IRequestExceptionFeature.Clear() => _exceptions = null;

public void Dispose()
{
if (_contextOrApplication is HttpApplication app)
{
_pool.Return(app);
_contextOrApplication = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,28 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SystemWebAdapters.Features;
using Microsoft.Extensions.ObjectPool;

namespace Microsoft.AspNetCore.SystemWebAdapters;

internal class HttpApplicationMiddleware
{
private readonly RequestDelegate _next;
private readonly ObjectPool<HttpApplication> _pool;

public HttpApplicationMiddleware(RequestDelegate next, ObjectPool<HttpApplication> pool)
{
_next = next;
_pool = pool;
}
public HttpApplicationMiddleware(RequestDelegate next) => _next = next;

public async Task InvokeAsync(HttpContextCore context)
{
var app = _pool.Get();

var endFeature = context.Features.GetRequired<IHttpResponseEndFeature>();
var httpApplicationFeature = new RequestHttpApplicationFeature(app, endFeature);

context.Features.Set<IHttpApplicationFeature>(httpApplicationFeature);
context.Features.Set<IHttpResponseEndFeature>(httpApplicationFeature);
context.Features.Set<IRequestExceptionFeature>(httpApplicationFeature);
context.Features.GetRequired<IHttpResponseBufferingFeature>().EnableBuffering(BufferResponseStreamAttribute.DefaultMemoryThreshold, default);

try
{
app.Context = context;

context.Features.GetRequired<IHttpResponseBufferingFeature>().EnableBuffering(BufferResponseStreamAttribute.DefaultMemoryThreshold, default);

try
{
await _next(context);
}
finally
{
await context.Features.GetRequired<IHttpResponseEndFeature>().EndAsync();
}
await _next(context);
}
finally
{
context.Features.Set<IHttpResponseEndFeature>(endFeature);
context.Features.Set<IHttpApplicationFeature>(null);
context.Features.Set<IRequestExceptionFeature>(null);

_pool.Return(app);
await context.Features.GetRequired<IHttpResponseEndFeature>().EndAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public Type ApplicationType
internal void MakeReadOnly() => _modules.MakeReadOnly();

/// <summary>
/// Gets or sets the number of <see cref="HttpApplication"/> retained for reuse. In order to support modules and appplications that may contain state,
/// Gets or sets the number of <see cref="HttpApplication"/> retained for reuse. In order to support modules and applications that may contain state,
/// a unique instance is required for each request. This type should be set to the average number of concurrent requests expected to be seen.
/// </summary>
public int PoolSize { get; set; } = 100;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SystemWebAdapters.Features;
using Microsoft.Extensions.ObjectPool;

namespace Microsoft.AspNetCore.SystemWebAdapters;

internal sealed class RegisterHttpApplicationFeatureMiddleware
{
private readonly RequestDelegate _next;
private readonly ObjectPool<HttpApplication> _pool;

public RegisterHttpApplicationFeatureMiddleware(RequestDelegate next, ObjectPool<HttpApplication> pool)
{
_next = next;
_pool = pool;
}

public async Task InvokeAsync(HttpContextCore context)
{
var endFeature = context.Features.GetRequired<IHttpResponseEndFeature>();
using var httpApplicationFeature = new HttpApplicationFeature(context, endFeature, _pool);

context.Features.Set<IHttpApplicationFeature>(httpApplicationFeature);
context.Features.Set<IHttpResponseEndFeature>(httpApplicationFeature);
context.Features.Set<IRequestExceptionFeature>(httpApplicationFeature);

try
{
await _next(context);
}
finally
{
context.Features.Set<IHttpResponseEndFeature>(endFeature);
context.Features.Set<IHttpApplicationFeature>(null);
context.Features.Set<IRequestExceptionFeature>(null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
builder.UseMiddleware<SetHttpContextTimestampMiddleware>();
builder.UseMiddleware<RegisterAdapterFeaturesMiddleware>();

if (builder.AreHttpApplicationEventsRequired())
{
builder.UseMiddleware<RegisterHttpApplicationFeatureMiddleware>();
}

next(builder);
};
}
Expand Down