aspnetcore/test/Microsoft.Net.Http.Server.F.../ResponseHeaderTests.cs

448 lines
20 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.Net.Http.Server
{
public class ResponseHeaderTests
{
[Fact]
public async Task ResponseHeaders_11Request_ServerSendsDefaultHeaders()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
var context = await server.GetContextAsync();
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(2, response.Headers.Count());
Assert.False(response.Headers.TransferEncodingChunked.HasValue);
Assert.True(response.Headers.Date.HasValue);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers.Server.ToString());
Assert.Equal(1, response.Content.Headers.Count());
Assert.Equal(0, response.Content.Headers.ContentLength);
}
}
[Fact]
public async Task ResponseHeaders_10Request_ServerSendsDefaultHeaders()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address, usehttp11: false);
var context = await server.GetContextAsync();
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(3, response.Headers.Count());
Assert.False(response.Headers.TransferEncodingChunked.HasValue);
Assert.True(response.Headers.ConnectionClose.Value);
Assert.True(response.Headers.Date.HasValue);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers.Server.ToString());
Assert.Equal(1, response.Content.Headers.Count());
Assert.Equal(0, response.Content.Headers.ContentLength);
}
}
[Fact]
public async Task ResponseHeaders_11HeadRequest_ServerSendsDefaultHeaders()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendHeadRequestAsync(address);
var context = await server.GetContextAsync();
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(3, response.Headers.Count());
Assert.True(response.Headers.TransferEncodingChunked.Value);
Assert.True(response.Headers.Date.HasValue);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers.Server.ToString());
Assert.False(response.Content.Headers.Contains("Content-Length"));
Assert.Equal(0, response.Content.Headers.Count());
}
}
[Fact]
public async Task ResponseHeaders_10HeadRequest_ServerSendsDefaultHeaders()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendHeadRequestAsync(address, usehttp11: false);
var context = await server.GetContextAsync();
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(3, response.Headers.Count());
Assert.False(response.Headers.TransferEncodingChunked.HasValue);
Assert.True(response.Headers.ConnectionClose.Value);
Assert.True(response.Headers.Date.HasValue);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers.Server.ToString());
Assert.False(response.Content.Headers.Contains("Content-Length"));
Assert.Equal(0, response.Content.Headers.Count());
}
}
[Fact]
public async Task ResponseHeaders_11HeadRequestWithContentLength_Success()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendHeadRequestAsync(address);
var context = await server.GetContextAsync();
context.Response.ContentLength = 20;
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(2, response.Headers.Count());
Assert.False(response.Headers.TransferEncodingChunked.HasValue);
Assert.True(response.Headers.Date.HasValue);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers.Server.ToString());
Assert.Equal(1, response.Content.Headers.Count());
Assert.Equal(20, response.Content.Headers.ContentLength);
}
}
[Fact]
public async Task ResponseHeaders_11RequestStatusCodeWithoutBody_NoContentLengthOrChunkedOrClose()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
var context = await server.GetContextAsync();
context.Response.StatusCode = 204; // No Content
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(2, response.Headers.Count());
Assert.False(response.Headers.TransferEncodingChunked.HasValue);
Assert.True(response.Headers.Date.HasValue);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers.Server.ToString());
Assert.False(response.Content.Headers.Contains("Content-Length"));
Assert.Equal(0, response.Content.Headers.Count());
}
}
[Fact]
public async Task ResponseHeaders_11HeadRequestStatusCodeWithoutBody_NoContentLengthOrChunkedOrClose()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendHeadRequestAsync(address);
var context = await server.GetContextAsync();
context.Response.StatusCode = 204; // No Content
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(2, response.Headers.Count());
Assert.False(response.Headers.TransferEncodingChunked.HasValue);
Assert.True(response.Headers.Date.HasValue);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers.Server.ToString());
Assert.False(response.Content.Headers.Contains("Content-Length"));
Assert.Equal(0, response.Content.Headers.Count());
}
}
[Fact]
public async Task ResponseHeaders_ServerSendsSingleValueKnownHeaders_Success()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
WebRequest request = WebRequest.Create(address);
Task<WebResponse> responseTask = request.GetResponseAsync();
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders["WWW-Authenticate"] = "custom1";
context.Dispose();
// HttpClient would merge the headers no matter what
HttpWebResponse response = (HttpWebResponse)await responseTask;
Assert.Equal(4, response.Headers.Count);
Assert.Null(response.Headers["Transfer-Encoding"]);
Assert.Equal(0, response.ContentLength);
Assert.NotNull(response.Headers["Date"]);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers["Server"]);
Assert.Equal("custom1", response.Headers["WWW-Authenticate"]);
}
}
[Fact]
public async Task ResponseHeaders_ServerSendsMultiValueKnownHeaders_Success()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
WebRequest request = WebRequest.Create(address);
Task<WebResponse> responseTask = request.GetResponseAsync();
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders["WWW-Authenticate"] = new[] { "custom1, and custom2", "custom3" };
context.Dispose();
// HttpClient would merge the headers no matter what
HttpWebResponse response = (HttpWebResponse)await responseTask;
Assert.Equal(4, response.Headers.Count);
Assert.Null(response.Headers["Transfer-Encoding"]);
Assert.Equal(0, response.ContentLength);
Assert.NotNull(response.Headers["Date"]);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers["Server"]);
#if DNXCORE50 // WebHeaderCollection.GetValues() not available in CoreCLR.
Assert.Equal("custom1, and custom2, custom3", response.Headers["WWW-Authenticate"]);
#else
Assert.Equal(new string[] { "custom1, and custom2", "custom3" }, response.Headers.GetValues("WWW-Authenticate"));
#endif
}
}
[Fact]
public async Task ResponseHeaders_ServerSendsCustomHeaders_Success()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
WebRequest request = WebRequest.Create(address);
Task<WebResponse> responseTask = request.GetResponseAsync();
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders["Custom-Header1"] = new[] { "custom1, and custom2", "custom3" };
context.Dispose();
// HttpClient would merge the headers no matter what
HttpWebResponse response = (HttpWebResponse)await responseTask;
Assert.Equal(4, response.Headers.Count);
Assert.Null(response.Headers["Transfer-Encoding"]);
Assert.Equal(0, response.ContentLength);
Assert.NotNull(response.Headers["Date"]);
Assert.Equal("Microsoft-HTTPAPI/2.0", response.Headers["Server"]);
#if DNXCORE50 // WebHeaderCollection.GetValues() not available in CoreCLR.
Assert.Equal("custom1, and custom2, custom3", response.Headers["Custom-Header1"]);
#else
Assert.Equal(new string[] { "custom1, and custom2", "custom3" }, response.Headers.GetValues("Custom-Header1"));
#endif
}
}
[Fact]
public async Task ResponseHeaders_ServerSendsConnectionClose_Closed()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders["Connection"] = "Close";
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.True(response.Headers.ConnectionClose.Value);
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
}
}
[Fact]
public async Task ResponseHeaders_HTTP10Request_Gets11Close()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address, usehttp11: false);
var context = await server.GetContextAsync();
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(new Version(1, 1), response.Version);
Assert.True(response.Headers.ConnectionClose.Value);
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
}
}
[Fact]
public async Task ResponseHeaders_HTTP10Request_AllowsManualChunking()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
using (HttpClient client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, address);
request.Version = new Version(1, 0);
Task<HttpResponseMessage> responseTask = client.SendAsync(request);
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders["Transfer-Encoding"] = "chunked";
var responseBytes = Encoding.ASCII.GetBytes("10\r\nManually Chunked\r\n0\r\n\r\n");
await context.Response.Body.WriteAsync(responseBytes, 0, responseBytes.Length);
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(new Version(1, 1), response.Version);
Assert.True(response.Headers.TransferEncodingChunked.Value);
Assert.False(response.Content.Headers.Contains("Content-Length"));
Assert.True(response.Headers.ConnectionClose.Value);
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
Assert.Equal("Manually Chunked", await response.Content.ReadAsStringAsync());
}
}
}
[Fact]
public async Task ResponseHeaders_HTTP10KeepAliveRequest_Gets11Close()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
// Http.Sys does not support 1.0 keep-alives.
Task<HttpResponseMessage> responseTask = SendRequestAsync(address, usehttp11: false, sendKeepAlive: true);
var context = await server.GetContextAsync();
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(new Version(1, 1), response.Version);
Assert.True(response.Headers.ConnectionClose.Value);
}
}
[Fact]
public async Task Headers_FlushSendsHeaders_Success()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders["Custom1"] = new[] { "value1a", "value1b" };
responseHeaders["Custom2"] = "value2a, value2b";
var body = context.Response.Body;
Assert.False(context.Response.HasStarted);
body.Flush();
Assert.True(context.Response.HasStarted);
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 the response has already started.", ex.Message);
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(5, response.Headers.Count()); // Date, Server, Chunked
Assert.Equal(2, response.Headers.GetValues("Custom1").Count());
Assert.Equal("value1a", response.Headers.GetValues("Custom1").First());
Assert.Equal("value1b", response.Headers.GetValues("Custom1").Skip(1).First());
Assert.Equal(1, response.Headers.GetValues("Custom2").Count());
Assert.Equal("value2a, value2b", response.Headers.GetValues("Custom2").First());
}
}
[Fact]
public async Task Headers_FlushAsyncSendsHeaders_Success()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders["Custom1"] = new[] { "value1a", "value1b" };
responseHeaders["Custom2"] = "value2a, value2b";
var body = context.Response.Body;
Assert.False(context.Response.HasStarted);
await body.FlushAsync();
Assert.True(context.Response.HasStarted);
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 the response has already started.", ex.Message);
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
Assert.Equal(5, response.Headers.Count()); // Date, Server, Chunked
Assert.Equal(2, response.Headers.GetValues("Custom1").Count());
Assert.Equal("value1a", response.Headers.GetValues("Custom1").First());
Assert.Equal("value1b", response.Headers.GetValues("Custom1").Skip(1).First());
Assert.Equal(1, response.Headers.GetValues("Custom2").Count());
Assert.Equal("value2a, value2b", response.Headers.GetValues("Custom2").First());
}
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri, bool usehttp11 = true, bool sendKeepAlive = false)
{
using (HttpClient client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, uri);
if (!usehttp11)
{
request.Version = new Version(1, 0);
}
if (sendKeepAlive)
{
request.Headers.Add("Connection", "Keep-Alive");
}
return await client.SendAsync(request);
}
}
private async Task<HttpResponseMessage> SendHeadRequestAsync(string uri, bool usehttp11 = true)
{
using (HttpClient client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Head, uri);
if (!usehttp11)
{
request.Version = new Version(1, 0);
}
return await client.SendAsync(request);
}
}
}
}