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 @@ -3,7 +3,6 @@

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;

namespace Microsoft.AspNetCore.SystemWebAdapters;

Expand All @@ -14,12 +13,6 @@ internal class SetDefaultResponseHeadersMiddleware
{
private readonly RequestDelegate _next;

private static readonly (string Header, string Value)[] _defaultHeaders = new (string, string)[]
{
(HeaderNames.CacheControl, CacheControlHeaderValue.PrivateString),
(HeaderNames.ContentType, "text/html")
};

public SetDefaultResponseHeadersMiddleware(RequestDelegate next) => _next = next;

public Task InvokeAsync(HttpContext context)
Expand All @@ -28,17 +21,20 @@ public Task InvokeAsync(HttpContext context)
{
var context = (HttpContext)state;

foreach (var (header, value) in _defaultHeaders)
{
if (!context.Response.Headers.ContainsKey(header))
{
context.Response.Headers[header] = value;
}
}
WriteDefaultContentType(context);
context.Response.GetAdapter().Cache.UpdateHeaders();

return Task.CompletedTask;
}, context);

return _next(context);
}

private static void WriteDefaultContentType(HttpContext context)
{
if (context.Response.Headers.ContentType.Count == 0)
{
context.Response.Headers.ContentType = "text/html";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,33 @@ public partial class HttpBrowserCapabilitiesWrapper : System.Web.HttpBrowserCapa
public override string Platform { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public override string Version { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
}
public enum HttpCacheability
{
NoCache = 1,
Private = 2,
Public = 4,
Server = 3,
ServerAndNoCache = 3,
ServerAndPrivate = 5,
}
public partial class HttpCachePolicy
{
internal HttpCachePolicy() { }
public System.Web.HttpCacheVaryByHeaders VaryByHeaders { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public System.Web.HttpCacheability GetCacheability() { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void SetCacheability(System.Web.HttpCacheability cacheability) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void SetExpires(System.DateTime date) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void SetLastModified(System.DateTime date) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void SetMaxAge(System.TimeSpan delta) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void SetOmitVaryStar(bool omit) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
}
public sealed partial class HttpCacheVaryByHeaders
{
internal HttpCacheVaryByHeaders() { }
public bool this[string name] { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public string[] GetHeaders() { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void SetHeaders(string[] headers) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
}
public partial class HttpContext : System.IServiceProvider
{
internal HttpContext() { }
Expand Down Expand Up @@ -422,6 +449,7 @@ public partial class HttpRequestWrapper : System.Web.HttpRequestBase
public partial class HttpResponse
{
internal HttpResponse() { }
public System.Web.HttpCachePolicy Cache { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public string Charset { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public System.Text.Encoding ContentEncoding { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public string ContentType { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
Expand Down Expand Up @@ -462,6 +490,7 @@ internal HttpResponse() { }
public partial class HttpResponseBase
{
public HttpResponseBase() { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public virtual System.Web.HttpCachePolicy Cache { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public virtual string Charset { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public virtual System.Text.Encoding ContentEncoding { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public virtual string ContentType { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
Expand Down Expand Up @@ -495,6 +524,7 @@ public partial class HttpResponseBase
public partial class HttpResponseWrapper : System.Web.HttpResponseBase
{
public HttpResponseWrapper(System.Web.HttpResponse response) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public override System.Web.HttpCachePolicy Cache { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public override string Charset { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public override System.Text.Encoding ContentEncoding { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
public override string ContentType { get { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} set { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");} }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpBrowserCapabilities))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpBrowserCapabilitiesBase))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpBrowserCapabilitiesWrapper))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpCacheability))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpCachePolicy))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpCacheVaryByHeaders))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpContext))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpContextBase))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.HttpContextWrapper))]
Expand Down
84 changes: 84 additions & 0 deletions src/Microsoft.AspNetCore.SystemWebAdapters/HttpCachePolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Http.Headers;
using Microsoft.AspNetCore.SystemWebAdapters;
using Microsoft.Net.Http.Headers;

namespace System.Web;

[Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1024:Use properties where appropriate", Justification = Constants.ApiFromAspNet)]
public class HttpCachePolicy
{
private readonly HttpContextCore _context;

private HttpCacheVaryByHeaders? _varyByHeaders;
private TimeSpan? _maxAge;
private HttpCacheability? _cacheability;
private bool _omitVaryStar;

internal HttpCachePolicy(HttpContextCore context)
{
_context = context;
}

internal void UpdateHeaders()
{
var headers = Headers;

if (_varyByHeaders is { IsEmpty: false })
{
headers.Headers.Vary = new(_varyByHeaders.GetHeaders(_omitVaryStar));
}

if (headers.Headers.CacheControl.Count == 0 || ShouldUpdateCache)
{
var cacheControl = headers.CacheControl ?? new();
UpdateCacheControl(cacheControl);
headers.CacheControl = cacheControl;
}
}

private bool ShouldUpdateCache => _maxAge.HasValue || _cacheability.HasValue;

private void UpdateCacheControl(CacheControlHeaderValue cacheControl)
{
cacheControl.MaxAge = _maxAge;
cacheControl.Public = GetCacheability() == HttpCacheability.Public;
cacheControl.Private = GetCacheability() == HttpCacheability.Private;
cacheControl.NoCache = GetCacheability() == HttpCacheability.NoCache;
}

private ResponseHeaders Headers => _context.GetAdapter().Response.TypedHeaders;

public HttpCacheVaryByHeaders VaryByHeaders => _varyByHeaders ??= new();

public HttpCacheability GetCacheability() => _cacheability ?? HttpCacheability.Private;

public void SetCacheability(HttpCacheability cacheability) => _cacheability = cacheability;

public void SetLastModified(DateTime date) => Headers.LastModified = date.ToUniversalTime();

public void SetMaxAge(TimeSpan delta) => _maxAge = delta;

public void SetExpires(DateTime date)
{
var utcDate = date.ToUniversalTime();
var utcNow = DateTime.UtcNow;
var oneYear = TimeSpan.FromDays(365);

if (utcDate - utcNow > oneYear)
{
utcDate = utcNow + oneYear;
}

var current = Headers.Expires;

if (!current.HasValue || utcDate < current)
{
Headers.Expires = utcDate;
}
}

public void SetOmitVaryStar(bool omit) => _omitVaryStar = omit;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;

namespace System.Web;

public sealed class HttpCacheVaryByHeaders
{
private readonly HashSet<string> _headers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

internal HttpCacheVaryByHeaders()
{
}

public bool this[string name]
{
get => _headers.Contains(name);
set
{
// ASP.NET did not allow setting false here - it just ignored it
if (!value)
{
return;
}

_headers.Add(name);
}
}

internal bool IsEmpty => _headers.Count == 0;

public void SetHeaders(string[] headers)
{
_headers.UnionWith(headers);
}

public string[] GetHeaders() => _headers.ToArray();

internal string[] GetHeaders(bool omit)
{
if (omit)
{
return _headers.Where(h => string.Equals(h, "*", StringComparison.Ordinal)).ToArray();
}
else
{
return GetHeaders();
}
}
}
21 changes: 21 additions & 0 deletions src/Microsoft.AspNetCore.SystemWebAdapters/HttpCacheability.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Web;

[Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1027:Mark enums with FlagsAttribute", Justification = Constants.ApiFromAspNet)]
[Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1008:Enums should have zero value", Justification = Constants.ApiFromAspNet)]
public enum HttpCacheability
{
NoCache = 1,

Private,

Server,

ServerAndNoCache = Server,

Public,

ServerAndPrivate,
}
3 changes: 3 additions & 0 deletions src/Microsoft.AspNetCore.SystemWebAdapters/HttpResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class HttpResponse
private ResponseHeaders? _typedHeaders;
private TextWriter? _writer;
private HttpCookieCollection? _cookies;
private HttpCachePolicy? _cache;

internal HttpResponse(HttpResponseCore response)
{
Expand Down Expand Up @@ -209,6 +210,8 @@ private void Redirect(string url, bool endResponse, bool permanent)
}
}

public HttpCachePolicy Cache => _cache ??= new(_response.HttpContext);

private string ResolvePath(string url)
{
if (string.IsNullOrEmpty(url))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public virtual TextWriter Output
set => throw new NotImplementedException();
}

public virtual HttpCachePolicy Cache => throw new NotImplementedException();

public virtual bool IsClientConnected => throw new NotImplementedException();

public virtual void AddHeader(string name, string value) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ public override bool TrySkipIisCustomErrors
set => _response.TrySkipIisCustomErrors = value;
}

public override HttpCachePolicy Cache => _response.Cache;

public override void Write(char ch) => _response.Write(ch);

public override void Write(object obj) => _response.Write(obj);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ public async Task Existing(string name, string value)

[Theory]
[InlineData("Content-Type", "some-content-type")]
[InlineData("Cache-Control", "cache-value")]
public async Task AddedInLaterMiddleware(string name, string value)
{
// Arrange
Expand Down