#54 - Throw if anyone tries to modify the response headers after they are sent.

This commit is contained in:
Chris Ross 2014-09-17 11:06:12 -07:00
parent 281ef61e8c
commit 56483cb0ed
6 changed files with 40 additions and 10 deletions

View File

@ -47,7 +47,7 @@ namespace SelfHostServer
else
{
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello world");
await context.Response.WriteAsync("Hello world from " + context.Request.Host + " at " + DateTime.Now);
}
});
}

View File

@ -21,11 +21,15 @@ namespace Microsoft.Net.Http.Server
private IDictionary<string, string[]> Store { get; set; }
// Readonly after the response has been sent.
internal bool Sent { get; set; }
public string this[string key]
{
get { return Get(key); }
set
{
ThrowIfSent();
if (string.IsNullOrEmpty(value))
{
Remove(key);
@ -40,7 +44,11 @@ namespace Microsoft.Net.Http.Server
string[] IDictionary<string, string[]>.this[string key]
{
get { return Store[key]; }
set { Store[key] = value; }
set
{
ThrowIfSent();
Store[key] = value;
}
}
public int Count
@ -50,7 +58,7 @@ namespace Microsoft.Net.Http.Server
public bool IsReadOnly
{
get { return false; }
get { return Sent; }
}
public ICollection<string> Keys
@ -65,16 +73,19 @@ namespace Microsoft.Net.Http.Server
public void Add(KeyValuePair<string, string[]> item)
{
ThrowIfSent();
Store.Add(item);
}
public void Add(string key, string[] value)
{
ThrowIfSent();
Store.Add(key, value);
}
public void Append(string key, string value)
{
ThrowIfSent();
string[] values;
if (Store.TryGetValue(key, out values))
{
@ -91,6 +102,7 @@ namespace Microsoft.Net.Http.Server
public void AppendValues(string key, params string[] values)
{
ThrowIfSent();
string[] oldValues;
if (Store.TryGetValue(key, out oldValues))
{
@ -107,6 +119,7 @@ namespace Microsoft.Net.Http.Server
public void Clear()
{
ThrowIfSent();
Store.Clear();
}
@ -152,21 +165,25 @@ namespace Microsoft.Net.Http.Server
public bool Remove(KeyValuePair<string, string[]> item)
{
ThrowIfSent();
return Store.Remove(item);
}
public bool Remove(string key)
{
ThrowIfSent();
return Store.Remove(key);
}
public void Set(string key, string value)
{
ThrowIfSent();
Store[key] = new[] { value };
}
public void SetValues(string key, params string[] values)
{
ThrowIfSent();
Store[key] = values;
}
@ -179,5 +196,13 @@ namespace Microsoft.Net.Http.Server
{
return GetEnumerator();
}
private void ThrowIfSent()
{
if (Sent)
{
throw new InvalidOperationException("The response headers cannot be modified because they have already been sent.");
}
}
}
}

View File

@ -583,6 +583,7 @@ namespace Microsoft.Net.Http.Server
private List<GCHandle> SerializeHeaders(bool isOpaqueUpgrade)
{
Headers.Sent = true; // Prohibit further modifications.
UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[] unknownHeaders = null;
UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE_INFO[] knownHeaderInfo = null;
List<GCHandle> pinnedHeaders;

View File

@ -247,7 +247,7 @@ namespace Microsoft.AspNet.Server.WebListener
var body = responseInfo.Body;
body.Flush();
Assert.Throws<InvalidOperationException>(() => responseInfo.StatusCode = 404);
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
Assert.Throws<InvalidOperationException>(() => responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }));
return Task.FromResult(0);
}))
{
@ -277,7 +277,7 @@ namespace Microsoft.AspNet.Server.WebListener
var body = responseInfo.Body;
await body.FlushAsync();
Assert.Throws<InvalidOperationException>(() => responseInfo.StatusCode = 404);
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
Assert.Throws<InvalidOperationException>(() => responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }));
}))
{
HttpResponseMessage response = await SendRequestAsync(address);

View File

@ -24,7 +24,7 @@ namespace Microsoft.Net.Http.Server
byte[] body = Encoding.UTF8.GetBytes("Hello World");
context.Response.Body.Write(body, 0, body.Length);
context.Response.Headers["Upgrade"] = "WebSocket"; // Win8.1 blocks anything but WebSocket
Assert.Throws<InvalidOperationException>(() => context.Response.Headers["Upgrade"] = "WebSocket"); // Win8.1 blocks anything but WebSocket
await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.UpgradeAsync());
context.Dispose();
HttpResponseMessage response = await clientTask;

View File

@ -233,8 +233,10 @@ namespace Microsoft.Net.Http.Server
responseHeaders.SetValues("Custom2", "value2a, value2b");
var body = context.Response.Body;
body.Flush();
Assert.Throws<InvalidOperationException>(() => context.Response.StatusCode = 404);
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
var ex = Assert.Throws<InvalidOperationException>(() => context.Response.StatusCode = 404);
Assert.Equal("Headers already sent.", ex.Message);
ex = Assert.Throws<InvalidOperationException>(() => responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }));
Assert.Equal("The response headers cannot be modified because they have already been sent.", ex.Message);
context.Dispose();
@ -265,8 +267,10 @@ namespace Microsoft.Net.Http.Server
responseHeaders.SetValues("Custom2", "value2a, value2b");
var body = context.Response.Body;
await body.FlushAsync();
Assert.Throws<InvalidOperationException>(() => context.Response.StatusCode = 404);
responseHeaders.SetValues("Custom3", "value3a, value3b", "value3c"); // Ignored
var ex = Assert.Throws<InvalidOperationException>(() => context.Response.StatusCode = 404);
Assert.Equal("Headers already sent.", ex.Message);
ex = Assert.Throws<InvalidOperationException>(() => responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }));
Assert.Equal("The response headers cannot be modified because they have already been sent.", ex.Message);
context.Dispose();