Add more unit tests

This commit is contained in:
Pavel Krymets 2016-05-18 16:10:06 -07:00
parent e3ed603b7e
commit 3a0d8c43ca
7 changed files with 349 additions and 102 deletions

View File

@ -16,6 +16,10 @@ namespace Microsoft.AspNetCore.StaticFiles.Infrastructure
internal static IList<RangeItemHeaderValue> NormalizeRanges(ICollection<RangeItemHeaderValue> ranges, long length)
{
IList<RangeItemHeaderValue> normalizedRanges = new List<RangeItemHeaderValue>(ranges.Count);
if (length == 0)
{
return normalizedRanges;
}
foreach (var range in ranges)
{
long? start = range.From;

View File

@ -197,9 +197,11 @@ namespace Microsoft.AspNetCore.StaticFiles
private void ComputeIfModifiedSince()
{
var now = DateTimeOffset.UtcNow;
// 14.25 If-Modified-Since
var ifModifiedSince = _requestHeaders.IfModifiedSince;
if (ifModifiedSince.HasValue)
if (ifModifiedSince.HasValue && ifModifiedSince <= now)
{
bool modified = ifModifiedSince < _lastModified;
_ifModifiedSinceState = modified ? PreconditionState.ShouldProcess : PreconditionState.NotModified;
@ -207,7 +209,7 @@ namespace Microsoft.AspNetCore.StaticFiles
// 14.28 If-Unmodified-Since
var ifUnmodifiedSince = _requestHeaders.IfUnmodifiedSince;
if (ifUnmodifiedSince.HasValue)
if (ifUnmodifiedSince.HasValue && ifModifiedSince <= now)
{
bool unmodified = ifUnmodifiedSince >= _lastModified;
_ifUnmodifiedSinceState = unmodified ? PreconditionState.ShouldProcess : PreconditionState.PreconditionFailed;

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
@ -41,38 +43,52 @@ namespace Microsoft.AspNetCore.StaticFiles
// as PUT, from modifying a resource that has changed since the client
// last retrieved it.
[Fact]
public async Task IfMatchShouldReturn412WhenNotListed()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task IfMatchShouldReturn412WhenNotListed(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/extra.xml");
var req = new HttpRequestMessage(method, "http://localhost/SubFolder/extra.xml");
req.Headers.Add("If-Match", "\"fake\"");
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
Assert.Equal(HttpStatusCode.PreconditionFailed, resp.StatusCode);
}
[Fact]
public async Task IfMatchShouldBeServedWhenListed()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task IfMatchShouldBeServedWhenListed(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/extra.xml");
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/extra.xml");
var req = new HttpRequestMessage(method, "http://localhost/SubFolder/extra.xml");
req.Headers.Add("If-Match", original.Headers.ETag.ToString());
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
}
[Fact]
public async Task IfMatchShouldBeServedForAstrisk()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task IfMatchShouldBeServedForAstrisk(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/extra.xml");
var req = new HttpRequestMessage(method, "http://localhost/SubFolder/extra.xml");
req.Headers.Add("If-Match", "*");
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
}
[Theory]
[MemberData(nameof(UnsupportedMethods))]
public async Task IfMatchShouldBeIgnoredForUnsupportedMethods(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
var req = new HttpRequestMessage(method, "http://localhost/SubFolder/extra.xml");
req.Headers.Add("If-Match", "*");
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
Assert.Equal(HttpStatusCode.NotFound, resp.StatusCode);
}
// 14.26 If-None-Match
// If any of the entity tags match the entity tag of the entity that
// would have been returned in the response to a similar GET request
@ -87,38 +103,43 @@ namespace Microsoft.AspNetCore.StaticFiles
// matched. For all other request methods, the server MUST respond with
// a status of 412 (Precondition Failed).
[Fact]
public async Task IfNoneMatchShouldReturn304ForMatchingOnGetAndHeadMethod()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task IfNoneMatchShouldReturn304ForMatching(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage resp1 = await server.CreateClient().GetAsync("http://localhost/SubFolder/extra.xml");
var req2 = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/extra.xml");
var req2 = new HttpRequestMessage(method, "http://localhost/SubFolder/extra.xml");
req2.Headers.Add("If-None-Match", resp1.Headers.ETag.ToString());
HttpResponseMessage resp2 = await server.CreateClient().SendAsync(req2);
Assert.Equal(HttpStatusCode.NotModified, resp2.StatusCode);
var req3 = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/extra.xml");
req3.Headers.Add("If-None-Match", resp1.Headers.ETag.ToString());
HttpResponseMessage resp3 = await server.CreateClient().SendAsync(req3);
Assert.Equal(HttpStatusCode.NotModified, resp3.StatusCode);
}
[Fact]
public async Task IfNoneMatchShouldBeIgnoredForNonTwoHundredAnd304Responses()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task IfNoneMatchAllShouldReturn304ForMatching(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage resp1 = await server.CreateClient().GetAsync("http://localhost/SubFolder/extra.xml");
var req2 = new HttpRequestMessage(HttpMethod.Post, "http://localhost/SubFolder/extra.xml");
var req2 = new HttpRequestMessage(method, "http://localhost/SubFolder/extra.xml");
req2.Headers.Add("If-None-Match", "*");
HttpResponseMessage resp2 = await server.CreateClient().SendAsync(req2);
Assert.Equal(HttpStatusCode.NotModified, resp2.StatusCode);
}
[Theory]
[MemberData(nameof(UnsupportedMethods))]
public async Task IfNoneMatchShouldBeIgnoredForNonTwoHundredAnd304Responses(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage resp1 = await server.CreateClient().GetAsync("http://localhost/SubFolder/extra.xml");
var req2 = new HttpRequestMessage(method, "http://localhost/SubFolder/extra.xml");
req2.Headers.Add("If-None-Match", resp1.Headers.ETag.ToString());
HttpResponseMessage resp2 = await server.CreateClient().SendAsync(req2);
Assert.Equal(HttpStatusCode.NotFound, resp2.StatusCode);
var req3 = new HttpRequestMessage(HttpMethod.Put, "http://localhost/SubFolder/extra.xml");
req3.Headers.Add("If-None-Match", resp1.Headers.ETag.ToString());
HttpResponseMessage resp3 = await server.CreateClient().SendAsync(req3);
Assert.Equal(HttpStatusCode.NotFound, resp3.StatusCode);
}
// 14.26 If-None-Match
@ -131,12 +152,15 @@ namespace Microsoft.AspNetCore.StaticFiles
// A server MUST use the strong comparison function (see section 13.3.3)
// to compare the entity tags in If-Match.
[Fact]
public async Task ServerShouldReturnLastModified()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task ServerShouldReturnLastModified(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/SubFolder/extra.xml");
HttpResponseMessage response = await server.CreateClient().SendAsync(
new HttpRequestMessage(method, "http://localhost/SubFolder/extra.xml"));
Assert.NotNull(response.Content.Headers.LastModified);
// Verify that DateTimeOffset is UTC
Assert.Equal(response.Content.Headers.LastModified.Value.Offset, TimeSpan.Zero);
@ -151,30 +175,58 @@ namespace Microsoft.AspNetCore.StaticFiles
// unless doing so is consistent with all of the conditional header
// fields in the request.
[Fact]
public async Task MatchingBothConditionsReturnsNotModified()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task MatchingBothConditionsReturnsNotModified(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage resp1 = await server
.CreateRequest("/SubFolder/extra.xml")
.GetAsync();
.SendAsync(method.Method);
HttpResponseMessage resp2 = await server
.CreateRequest("/SubFolder/extra.xml")
.AddHeader("If-None-Match", resp1.Headers.ETag.ToString())
.And(req => req.Headers.IfModifiedSince = resp1.Content.Headers.LastModified)
.GetAsync();
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.NotModified, resp2.StatusCode);
}
[Fact]
public async Task MissingEitherOrBothConditionsReturnsNormally()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task MatchingAtLeastOneETagReturnsNotModified(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage resp1 = await server
.CreateRequest("/SubFolder/extra.xml")
.GetAsync();
.SendAsync(method.Method);
var etag = resp1.Headers.ETag.ToString();
HttpResponseMessage resp2 = await server
.CreateRequest("/SubFolder/extra.xml")
.AddHeader("If-Match", etag + ", " + etag)
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.OK, resp2.StatusCode);
HttpResponseMessage resp3 = await server
.CreateRequest("/SubFolder/extra.xml")
.AddHeader("If-Match", etag+ ", \"badetag\"")
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.OK, resp3.StatusCode);
}
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task MissingEitherOrBothConditionsReturnsNormally(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage resp1 = await server
.CreateRequest("/SubFolder/extra.xml")
.SendAsync(method.Method);
DateTimeOffset lastModified = resp1.Content.Headers.LastModified.Value;
DateTimeOffset pastDate = lastModified.AddHours(-1);
@ -184,19 +236,19 @@ namespace Microsoft.AspNetCore.StaticFiles
.CreateRequest("/SubFolder/extra.xml")
.AddHeader("If-None-Match", "\"fake\"")
.And(req => req.Headers.IfModifiedSince = lastModified)
.GetAsync();
.SendAsync(method.Method);
HttpResponseMessage resp3 = await server
.CreateRequest("/SubFolder/extra.xml")
.AddHeader("If-None-Match", resp1.Headers.ETag.ToString())
.And(req => req.Headers.IfModifiedSince = pastDate)
.GetAsync();
.SendAsync(method.Method);
HttpResponseMessage resp4 = await server
.CreateRequest("/SubFolder/extra.xml")
.AddHeader("If-None-Match", "\"fake\"")
.And(req => req.Headers.IfModifiedSince = furtureDate)
.GetAsync();
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.OK, resp2.StatusCode);
Assert.Equal(HttpStatusCode.OK, resp3.StatusCode);
@ -215,15 +267,30 @@ namespace Microsoft.AspNetCore.StaticFiles
// invalid, the response is exactly the same as for a normal GET.
// A date which is later than the server's current time is
// invalid.
[Fact]
public async Task InvalidIfModifiedSinceDateFormatGivesNormalGet()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task InvalidIfModifiedSinceDateFormatGivesNormalGet(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage res = await server
.CreateRequest("/SubFolder/extra.xml")
.AddHeader("If-Modified-Since", "bad-date")
.GetAsync();
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.OK, res.StatusCode);
}
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task FutureIfModifiedSinceDateFormatGivesNormalGet(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage res = await server
.CreateRequest("/SubFolder/extra.xml")
.And(req => req.Headers.IfModifiedSince = DateTimeOffset.Now.AddYears(1))
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.OK, res.StatusCode);
}
@ -235,38 +302,82 @@ namespace Microsoft.AspNetCore.StaticFiles
// Modified-Since date, the server SHOULD return a 304 (Not
// Modified) response.
[Fact]
public async Task IfModifiedSinceDateGreaterThanLastModifiedShouldReturn304()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task IfModifiedSinceDateGreaterThanLastModifiedShouldReturn304(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage res1 = await server
.CreateRequest("/SubFolder/extra.xml")
.GetAsync();
.SendAsync(method.Method);
HttpResponseMessage res2 = await server
.CreateRequest("/SubFolder/extra.xml")
.And(req => req.Headers.IfModifiedSince = DateTimeOffset.Now)
.GetAsync();
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.NotModified, res2.StatusCode);
}
[Fact]
public async Task IfModifiedSinceDateLessThanLastModifiedShouldReturn200()
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task SuppportsIfModifiedDateFormats(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage res1 = await server
.CreateRequest("/SubFolder/extra.xml")
.SendAsync(method.Method);
var formats = new[]
{
"ddd, dd MMM yyyy HH:mm:ss 'GMT'",
"dddd, dd-MMM-yy HH:mm:ss 'GMT'",
"ddd MMM d HH:mm:ss yyyy"
};
foreach (var format in formats)
{
HttpResponseMessage res2 = await server
.CreateRequest("/SubFolder/extra.xml")
.AddHeader("If-Modified-Since", DateTimeOffset.Now.ToString(format))
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.NotModified, res2.StatusCode);
}
}
[Theory]
[MemberData(nameof(SupportedMethods))]
public async Task IfModifiedSinceDateLessThanLastModifiedShouldReturn200(HttpMethod method)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage res1 = await server
.CreateRequest("/SubFolder/extra.xml")
.GetAsync();
.SendAsync(method.Method);
HttpResponseMessage res2 = await server
.CreateRequest("/SubFolder/extra.xml")
.And(req => req.Headers.IfModifiedSince = DateTimeOffset.MinValue)
.GetAsync();
.SendAsync(method.Method);
Assert.Equal(HttpStatusCode.OK, res2.StatusCode);
}
public static IEnumerable<object> SupportedMethods => new[]
{
new [] { HttpMethod.Get },
new [] { HttpMethod.Head }
};
public static IEnumerable<object> UnsupportedMethods => new[]
{
new [] { HttpMethod.Post },
new [] { HttpMethod.Put },
new [] { HttpMethod.Options },
new [] { HttpMethod.Trace },
new [] { new HttpMethod("VERB") }
};
}
}

View File

@ -72,6 +72,35 @@ namespace Microsoft.AspNetCore.StaticFiles
Assert.Equal("0123456789a", await resp.Content.ReadAsStringAsync());
}
[Fact]
public async Task IfModifiedSinceWithPastDateShouldServePartialContent()
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
req.Headers.Add("If-Modified-Since", original.Content.Headers.LastModified.Value.AddHours(-1).ToString("r"));
req.Headers.Add("Range", "bytes=0-10");
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
Assert.Equal(HttpStatusCode.PartialContent, resp.StatusCode);
Assert.Equal("bytes 0-10/62", resp.Content.Headers.ContentRange.ToString());
Assert.Equal(11, resp.Content.Headers.ContentLength);
Assert.Equal("0123456789a", await resp.Content.ReadAsStringAsync());
}
[Fact]
public async Task IfModifiedSinceWithCurrentDateShouldReturn304()
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
req.Headers.Add("If-Modified-Since", original.Content.Headers.LastModified.Value.ToString("r"));
req.Headers.Add("Range", "bytes=0-10");
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
Assert.Equal(HttpStatusCode.NotModified, resp.StatusCode);
}
// 14.27 If-Range
// If the client has no entity tag for an entity, but does have a Last- Modified date, it MAY use that date in an If-Range header.
// HEAD requests should ignore the Range header
@ -216,7 +245,9 @@ namespace Microsoft.AspNetCore.StaticFiles
// 14.35 Range
[Theory]
[InlineData("0-0", "0-0", 1, "0")]
[InlineData("0-9", "0-9", 10, "0123456789")]
[InlineData("0- 9", "0-9", 10, "0123456789")]
[InlineData("0 -9", "0-9", 10, "0123456789")]
[InlineData("0 - 9", "0-9", 10, "0123456789")]
[InlineData("10-35", "10-35", 26, "abcdefghijklmnopqrstuvwxyz")]
[InlineData("36-61", "36-61", 26, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
[InlineData("36-", "36-61", 26, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] // Last 26
@ -236,6 +267,42 @@ namespace Microsoft.AspNetCore.StaticFiles
Assert.Equal(expectedData, await resp.Content.ReadAsStringAsync());
}
[Theory]
[InlineData("0-0", "0-0", 1, "A")]
[InlineData("0-", "0-0", 1, "A")]
[InlineData("-1", "0-0", 1, "A")]
[InlineData("-2", "0-0", 1, "A")]
[InlineData("0-1", "0-0", 1, "A")]
[InlineData("0-2", "0-0", 1, "A")]
public async Task SingleValidRangeShouldServePartialContentSingleByteFile(string range, string expectedRange, int length, string expectedData)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/SingleByte.txt");
req.Headers.Add("Range", "bytes=" + range);
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
Assert.Equal(HttpStatusCode.PartialContent, resp.StatusCode);
Assert.NotNull(resp.Content.Headers.ContentRange);
Assert.Equal("bytes " + expectedRange + "/1", resp.Content.Headers.ContentRange.ToString());
Assert.Equal(length, resp.Content.Headers.ContentLength);
Assert.Equal(expectedData, await resp.Content.ReadAsStringAsync());
}
[Theory]
[InlineData("0-0")]
[InlineData("0-")]
[InlineData("-1")]
[InlineData("-2")]
[InlineData("0-1")]
[InlineData("0-2")]
public async Task SingleValidRangeShouldServeRequestedRangeNotSatisfiableEmptyFile(string range)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/Empty.txt");
req.Headers.Add("Range", "bytes=" + range);
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, resp.StatusCode);
}
// 14.35 Range
// HEAD ignores range headers
[Theory]
@ -287,6 +354,9 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("0")]
[InlineData("1-0")]
[InlineData("-")]
[InlineData("a-")]
[InlineData("-b")]
[InlineData("a-b")]
public async Task SingleInvalidRangeIgnored(string range)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());
@ -305,6 +375,9 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("0")]
[InlineData("1-0")]
[InlineData("-")]
[InlineData("a-")]
[InlineData("-b")]
[InlineData("a-b")]
public async Task HEADSingleInvalidRangeIgnored(string range)
{
TestServer server = StaticFilesTestServer.Create(app => app.UseFileServer());

View File

@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
@ -29,6 +31,24 @@ namespace Microsoft.AspNetCore.StaticFiles
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
public async Task FoundFile_LastModifiedTrimsSeconds()
{
using (var fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory()))
{
var server = StaticFilesTestServer.Create(app => app.UseStaticFiles(new StaticFileOptions
{
FileProvider = fileProvider
}));
var fileInfo = fileProvider.GetFileInfo("TestDocument.txt");
var response = await server.CreateRequest("TestDocument.txt").GetAsync();
var last = fileInfo.LastModified;
var trimed = new DateTimeOffset(last.Year, last.Month, last.Day, last.Hour, last.Minute, last.Second, last.Offset).ToUniversalTime();
Assert.Equal(response.Content.Headers.LastModified.Value, trimed);
}
}
[Fact]
public async Task NullArguments()
{
@ -45,30 +65,7 @@ namespace Microsoft.AspNetCore.StaticFiles
}
[Theory]
[InlineData("", @".", "/missing.file")]
[InlineData("/subdir", @".", "/subdir/missing.file")]
[InlineData("/missing.file", @"./", "/missing.file")]
[InlineData("", @"./", "/xunit.xml")]
public async Task NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), baseDir)))
{
var server = StaticFilesTestServer.Create(app => app.UseStaticFiles(new StaticFileOptions
{
RequestPath = new PathString(baseUrl),
FileProvider = fileProvider
}));
var response = await server.CreateRequest(requestUrl).GetAsync();
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}
[Theory]
[InlineData("", @".", "/TestDocument.txt")]
[InlineData("/somedir", @".", "/somedir/TestDocument.txt")]
[InlineData("/SomeDir", @".", "/soMediR/TestDocument.txt")]
[InlineData("", @"SubFolder", "/ranges.txt")]
[InlineData("/somedir", @"SubFolder", "/somedir/ranges.txt")]
[MemberData(nameof(ExistingFiles))]
public async Task FoundFile_Served_All(string baseUrl, string baseDir, string requestUrl)
{
await FoundFile_Served(baseUrl, baseDir, requestUrl);
@ -95,41 +92,26 @@ namespace Microsoft.AspNetCore.StaticFiles
RequestPath = new PathString(baseUrl),
FileProvider = fileProvider
}));
var fileInfo = fileProvider.GetFileInfo(Path.GetFileName(requestUrl));
var response = await server.CreateRequest(requestUrl).GetAsync();
var responseContent = await response.Content.ReadAsByteArrayAsync();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.True(response.Content.Headers.ContentLength > 0);
Assert.Equal(response.Content.Headers.ContentLength, (await response.Content.ReadAsByteArrayAsync()).Length);
}
}
Assert.True(response.Content.Headers.ContentLength == fileInfo.Length);
Assert.Equal(response.Content.Headers.ContentLength, responseContent.Length);
[Theory]
[InlineData("", @".", "/TestDocument.txt")]
[InlineData("/somedir", @".", "/somedir/TestDocument.txt")]
[InlineData("/SomeDir", @".", "/soMediR/TestDocument.txt")]
[InlineData("", @"SubFolder", "/ranges.txt")]
[InlineData("/somedir", @"SubFolder", "/somedir/ranges.txt")]
public async Task PostFile_PassesThrough(string baseUrl, string baseDir, string requestUrl)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), baseDir)))
{
var server = StaticFilesTestServer.Create(app => app.UseStaticFiles(new StaticFileOptions
using (var stream = fileInfo.CreateReadStream())
{
RequestPath = new PathString(baseUrl),
FileProvider = fileProvider
}));
var response = await server.CreateRequest(requestUrl).PostAsync();
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
var fileContents = new byte[stream.Length];
stream.Read(fileContents, 0, (int)stream.Length);
Assert.True(responseContent.SequenceEqual(fileContents));
}
}
}
[Theory]
[InlineData("", @".", "/TestDocument.txt")]
[InlineData("/somedir", @".", "/somedir/TestDocument.txt")]
[InlineData("/SomeDir", @".", "/soMediR/TestDocument.txt")]
[InlineData("", @"SubFolder", "/ranges.txt")]
[InlineData("/somedir", @"SubFolder", "/somedir/ranges.txt")]
[MemberData(nameof(ExistingFiles))]
public async Task HeadFile_HeadersButNotBodyServed(string baseUrl, string baseDir, string requestUrl)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), baseDir)))
@ -139,13 +121,87 @@ namespace Microsoft.AspNetCore.StaticFiles
RequestPath = new PathString(baseUrl),
FileProvider = fileProvider
}));
var fileInfo = fileProvider.GetFileInfo(Path.GetFileName(requestUrl));
var response = await server.CreateRequest(requestUrl).SendAsync("HEAD");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.True(response.Content.Headers.ContentLength > 0);
Assert.True(response.Content.Headers.ContentLength == fileInfo.Length);
Assert.Equal(0, (await response.Content.ReadAsByteArrayAsync()).Length);
}
}
[Theory]
[MemberData(nameof(MissingFiles))]
public async Task Get_NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl) =>
await PassesThrough("GET", baseUrl, baseDir, requestUrl);
[Theory]
[MemberData(nameof(MissingFiles))]
public async Task Head_NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl) =>
await PassesThrough("HEAD", baseUrl, baseDir, requestUrl);
[Theory]
[MemberData(nameof(MissingFiles))]
public async Task Unknown_NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl) =>
await PassesThrough("VERB", baseUrl, baseDir, requestUrl);
[Theory]
[MemberData(nameof(ExistingFiles))]
public async Task Options_Match_PassesThrough(string baseUrl, string baseDir, string requestUrl) =>
await PassesThrough("OPTIONS", baseUrl, baseDir, requestUrl);
[Theory]
[MemberData(nameof(ExistingFiles))]
public async Task Trace_Match_PassesThrough(string baseUrl, string baseDir, string requestUrl) =>
await PassesThrough("TRACE", baseUrl, baseDir, requestUrl);
[Theory]
[MemberData(nameof(ExistingFiles))]
public async Task Post_Match_PassesThrough(string baseUrl, string baseDir, string requestUrl) =>
await PassesThrough("POST", baseUrl, baseDir, requestUrl);
[Theory]
[MemberData(nameof(ExistingFiles))]
public async Task Put_Match_PassesThrough(string baseUrl, string baseDir, string requestUrl) =>
await PassesThrough("PUT", baseUrl, baseDir, requestUrl);
[Theory]
[MemberData(nameof(ExistingFiles))]
public async Task Unknown_Match_PassesThrough(string baseUrl, string baseDir, string requestUrl) =>
await PassesThrough("VERB", baseUrl, baseDir, requestUrl);
public async Task PassesThrough(string method, string baseUrl, string baseDir, string requestUrl)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), baseDir)))
{
var server = StaticFilesTestServer.Create(app => app.UseStaticFiles(new StaticFileOptions
{
RequestPath = new PathString(baseUrl),
FileProvider = fileProvider
}));
var response = await server.CreateRequest(requestUrl).SendAsync(method);
Assert.Null(response.Content.Headers.LastModified);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}
public static IEnumerable<string[]> MissingFiles => new[]
{
new[] {"", @".", "/missing.file"},
new[] {"/subdir", @".", "/subdir/missing.file"},
new[] {"/missing.file", @"./", "/missing.file"},
new[] {"", @"./", "/xunit.xml"}
};
public static IEnumerable<string[]> ExistingFiles => new[]
{
new[] {"", @".", "/TestDocument.txt"},
new[] {"/somedir", @".", "/somedir/TestDocument.txt"},
new[] {"/SomeDir", @".", "/soMediR/TestDocument.txt"},
new[] {"", @"SubFolder", "/ranges.txt"},
new[] {"/somedir", @"SubFolder", "/somedir/ranges.txt"},
new[] {"", @"SubFolder", "/Empty.txt"}
};
}
}