Implement read-only HeaderDictionary (#958)

This commit is contained in:
Chris Ross 2017-11-06 20:03:49 -08:00 committed by GitHub
parent f287c46bad
commit 3e3772eecd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 16 deletions

View File

@ -58,13 +58,13 @@ namespace Microsoft.AspNetCore.Http
}
return StringValues.Empty;
}
set
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ThrowIfReadOnly();
if (StringValues.IsNullOrEmpty(value))
{
@ -90,7 +90,11 @@ namespace Microsoft.AspNetCore.Http
StringValues IDictionary<string, StringValues>.this[string key]
{
get { return Store[key]; }
set { this[key] = value; }
set
{
ThrowIfReadOnly();
this[key] = value;
}
}
public long? ContentLength
@ -110,6 +114,7 @@ namespace Microsoft.AspNetCore.Http
}
set
{
ThrowIfReadOnly();
if (value.HasValue)
{
this[HeaderNames.ContentLength] = HeaderUtilities.FormatNonNegativeInt64(value.Value);
@ -125,25 +130,13 @@ namespace Microsoft.AspNetCore.Http
/// Gets the number of elements contained in the <see cref="HeaderDictionary" />;.
/// </summary>
/// <returns>The number of elements contained in the <see cref="HeaderDictionary" />.</returns>
public int Count
{
get
{
return Store?.Count ?? 0;
}
}
public int Count => Store?.Count ?? 0;
/// <summary>
/// Gets a value that indicates whether the <see cref="HeaderDictionary" /> is in read-only mode.
/// </summary>
/// <returns>true if the <see cref="HeaderDictionary" /> is in read-only mode; otherwise, false.</returns>
public bool IsReadOnly
{
get
{
return false;
}
}
public bool IsReadOnly { get; set; }
public ICollection<string> Keys
{
@ -179,6 +172,7 @@ namespace Microsoft.AspNetCore.Http
{
throw new ArgumentNullException("The key is null");
}
ThrowIfReadOnly();
if (Store == null)
{
Store = new Dictionary<string, StringValues>(1, StringComparer.OrdinalIgnoreCase);
@ -197,6 +191,7 @@ namespace Microsoft.AspNetCore.Http
{
throw new ArgumentNullException(nameof(key));
}
ThrowIfReadOnly();
if (Store == null)
{
@ -210,6 +205,7 @@ namespace Microsoft.AspNetCore.Http
/// </summary>
public void Clear()
{
ThrowIfReadOnly();
Store?.Clear();
}
@ -270,6 +266,7 @@ namespace Microsoft.AspNetCore.Http
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
public bool Remove(KeyValuePair<string, StringValues> item)
{
ThrowIfReadOnly();
if (Store == null)
{
return false;
@ -291,6 +288,7 @@ namespace Microsoft.AspNetCore.Http
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
public bool Remove(string key)
{
ThrowIfReadOnly();
if (Store == null)
{
return false;
@ -356,6 +354,14 @@ namespace Microsoft.AspNetCore.Http
return Store.GetEnumerator();
}
private void ThrowIfReadOnly()
{
if (IsReadOnly)
{
throw new InvalidOperationException("The response headers cannot be modified because the response has already started.");
}
}
public struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
{
// Do NOT make this readonly, or MoveNext will not work

View File

@ -68,5 +68,40 @@ namespace Microsoft.AspNetCore.Http
var result = headers.GetCommaSeparatedValues("Header1");
Assert.Equal(new[] { "Value1", "Value2" }, result);
}
[Fact]
public void ReadActionsWorkWhenReadOnly()
{
var headers = new HeaderDictionary(
new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase)
{
{ "Header1", "Value1" }
});
headers.IsReadOnly = true;
Assert.Single(headers);
Assert.Equal<string>(new[] { "Header1" }, headers.Keys);
Assert.True(headers.ContainsKey("header1"));
Assert.False(headers.ContainsKey("header2"));
Assert.Equal("Value1", headers["header1"]);
Assert.Equal(new[] { "Value1" }, headers["header1"].ToArray());
}
[Fact]
public void WriteActionsThrowWhenReadOnly()
{
var headers = new HeaderDictionary();
headers.IsReadOnly = true;
Assert.Throws<InvalidOperationException>(() => headers["header1"] = "value1");
Assert.Throws<InvalidOperationException>(() => ((IDictionary<string, StringValues>)headers)["header1"] = "value1");
Assert.Throws<InvalidOperationException>(() => headers.ContentLength = 12);
Assert.Throws<InvalidOperationException>(() => headers.Add(new KeyValuePair<string, StringValues>("header1", "value1")));
Assert.Throws<InvalidOperationException>(() => headers.Add("header1", "value1"));
Assert.Throws<InvalidOperationException>(() => headers.Clear());
Assert.Throws<InvalidOperationException>(() => headers.Remove(new KeyValuePair<string, StringValues>("header1", "value1")));
Assert.Throws<InvalidOperationException>(() => headers.Remove("header1"));
}
}
}