Skip to content
This repository was archived by the owner on Nov 22, 2018. It is now read-only.

Commit 2c4811f

Browse files
committed
Import static file tests from Katana.
1 parent b49b46c commit 2c4811f

16 files changed

+1232
-24
lines changed

StaticFiles.sln

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 14
4-
VisualStudioVersion = 14.0.21628.1
4+
VisualStudioVersion = 14.0.21916.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{40EE0889-960E-41B4-A3D3-9CE963EB0797}"
77
EndProject
@@ -11,6 +11,15 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.StaticFile
1111
EndProject
1212
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "StaticFileSample", "samples\StaticFileSample\StaticFileSample.kproj", "{092141D9-305A-4FC5-AE74-CB23982CA8D4}"
1313
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EF02AFE8-7C15-4DDB-8B2C-58A676112A98}"
15+
EndProject
16+
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.StaticFiles.Tests", "test\Microsoft.AspNet.StaticFiles.Tests\Microsoft.AspNet.StaticFiles.Tests.kproj", "{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}"
17+
EndProject
18+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5EE39BF7-6457-432B-B26B-53B77A1C03D9}"
19+
ProjectSection(SolutionItems) = preProject
20+
global.json = global.json
21+
EndProjectSection
22+
EndProject
1423
Global
1524
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1625
Debug|Any CPU = Debug|Any CPU
@@ -21,33 +30,43 @@ Global
2130
Release|x86 = Release|x86
2231
EndGlobalSection
2332
GlobalSection(ProjectConfigurationPlatforms) = postSolution
24-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Any CPU.ActiveCfg = Debug|x86
25-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Any CPU.Build.0 = Debug|x86
26-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
27-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Mixed Platforms.Build.0 = Debug|x86
28-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|x86.ActiveCfg = Debug|x86
29-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|x86.Build.0 = Debug|x86
30-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Any CPU.ActiveCfg = Release|x86
31-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Mixed Platforms.ActiveCfg = Release|x86
32-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Mixed Platforms.Build.0 = Release|x86
33-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|x86.ActiveCfg = Release|x86
34-
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|x86.Build.0 = Release|x86
35-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Any CPU.ActiveCfg = Debug|x86
36-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Any CPU.Build.0 = Debug|x86
37-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
38-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|x86.ActiveCfg = Debug|x86
39-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|x86.Build.0 = Debug|x86
40-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Any CPU.ActiveCfg = Release|x86
41-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Mixed Platforms.ActiveCfg = Release|x86
42-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Mixed Platforms.Build.0 = Release|x86
43-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|x86.ActiveCfg = Release|x86
44-
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|x86.Build.0 = Release|x86
33+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Any CPU.Build.0 = Debug|Any CPU
35+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
36+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
37+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Debug|x86.ActiveCfg = Debug|Any CPU
38+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Any CPU.ActiveCfg = Release|Any CPU
39+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Any CPU.Build.0 = Release|Any CPU
40+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
41+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
42+
{8D7BC5A4-F19C-4184-8338-A6B42997218C}.Release|x86.ActiveCfg = Release|Any CPU
43+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
45+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
46+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
47+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Debug|x86.ActiveCfg = Debug|Any CPU
48+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
49+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Any CPU.Build.0 = Release|Any CPU
50+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
51+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|Mixed Platforms.Build.0 = Release|Any CPU
52+
{092141D9-305A-4FC5-AE74-CB23982CA8D4}.Release|x86.ActiveCfg = Release|Any CPU
53+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
55+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
56+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
57+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Debug|x86.ActiveCfg = Debug|Any CPU
58+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
59+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|Any CPU.Build.0 = Release|Any CPU
60+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
61+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
62+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5}.Release|x86.ActiveCfg = Release|Any CPU
4563
EndGlobalSection
4664
GlobalSection(SolutionProperties) = preSolution
4765
HideSolutionNode = FALSE
4866
EndGlobalSection
4967
GlobalSection(NestedProjects) = preSolution
5068
{8D7BC5A4-F19C-4184-8338-A6B42997218C} = {40EE0889-960E-41B4-A3D3-9CE963EB0797}
5169
{092141D9-305A-4FC5-AE74-CB23982CA8D4} = {8B21A3A9-9CA6-4857-A6E0-1A3203404B60}
70+
{CC87FE7D-8F42-4BE9-A152-9625E837C1E5} = {EF02AFE8-7C15-4DDB-8B2C-58A676112A98}
5271
EndGlobalSection
5372
EndGlobal

src/Microsoft.AspNet.StaticFiles/DefaultFilesMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public Task Invoke(HttpContext context)
7474
if (!Helpers.PathEndsInSlash(context.Request.Path))
7575
{
7676
context.Response.StatusCode = 301;
77-
context.Response.Headers[Constants.Location] = context.Request.PathBase + context.Request.Path + "/";
77+
context.Response.Headers[Constants.Location] = context.Request.PathBase + context.Request.Path + "/" + context.Request.QueryString;
7878
return Constants.CompletedTask;
7979
}
8080

src/Microsoft.AspNet.StaticFiles/DirectoryBrowserMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public Task Invoke(HttpContext context)
6767
if (!Helpers.PathEndsInSlash(context.Request.Path))
6868
{
6969
context.Response.StatusCode = 301;
70-
context.Response.Headers[Constants.Location] = context.Request.PathBase + context.Request.Path + "/";
70+
context.Response.Headers[Constants.Location] = context.Request.PathBase + context.Request.Path + "/" + context.Request.QueryString;
7171
return Constants.CompletedTask;
7272
}
7373

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2+
3+
using System;
4+
using System.Net;
5+
using System.Net.Http;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNet.Builder;
8+
using Microsoft.AspNet.TestHost;
9+
using Shouldly;
10+
using Xunit;
11+
12+
namespace Microsoft.AspNet.StaticFiles
13+
{
14+
public class CacheHeaderTests
15+
{
16+
[Fact]
17+
public async Task ServerShouldReturnETag()
18+
{
19+
TestServer server = TestServer.Create(app => app.UseFileServer());
20+
21+
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/SubFolder/Extra.xml");
22+
response.Headers.ETag.ShouldNotBe(null);
23+
response.Headers.ETag.Tag.ShouldNotBe(null);
24+
}
25+
26+
[Fact]
27+
public async Task SameETagShouldBeReturnedAgain()
28+
{
29+
TestServer server = TestServer.Create(app => app.UseFileServer());
30+
31+
HttpResponseMessage response1 = await server.CreateClient().GetAsync("http://localhost/SubFolder/Extra.xml");
32+
HttpResponseMessage response2 = await server.CreateClient().GetAsync("http://localhost/SubFolder/Extra.xml");
33+
response1.Headers.ETag.ShouldBe(response2.Headers.ETag);
34+
}
35+
36+
// 14.24 If-Match
37+
// If none of the entity tags match, or if "*" is given and no current
38+
// entity exists, the server MUST NOT perform the requested method, and
39+
// MUST return a 412 (Precondition Failed) response. This behavior is
40+
// most useful when the client wants to prevent an updating method, such
41+
// as PUT, from modifying a resource that has changed since the client
42+
// last retrieved it.
43+
44+
[Fact]
45+
public async Task IfMatchShouldReturn412WhenNotListed()
46+
{
47+
TestServer server = TestServer.Create(app => app.UseFileServer());
48+
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/Extra.xml");
49+
req.Headers.Add("If-Match", "\"fake\"");
50+
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
51+
resp.StatusCode.ShouldBe(HttpStatusCode.PreconditionFailed);
52+
}
53+
54+
[Fact]
55+
public async Task IfMatchShouldBeServedWhenListed()
56+
{
57+
TestServer server = TestServer.Create(app => app.UseFileServer());
58+
HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/Extra.xml");
59+
60+
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/Extra.xml");
61+
req.Headers.Add("If-Match", original.Headers.ETag.ToString());
62+
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
63+
resp.StatusCode.ShouldBe(HttpStatusCode.OK);
64+
}
65+
66+
[Fact]
67+
public async Task IfMatchShouldBeServedForAstrisk()
68+
{
69+
TestServer server = TestServer.Create(app => app.UseFileServer());
70+
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/Extra.xml");
71+
req.Headers.Add("If-Match", "*");
72+
HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
73+
resp.StatusCode.ShouldBe(HttpStatusCode.OK);
74+
}
75+
76+
// 14.26 If-None-Match
77+
// If any of the entity tags match the entity tag of the entity that
78+
// would have been returned in the response to a similar GET request
79+
// (without the If-None-Match header) on that resource, or if "*" is
80+
// given and any current entity exists for that resource, then the
81+
// server MUST NOT perform the requested method, unless required to do
82+
// so because the resource's modification date fails to match that
83+
// supplied in an If-Modified-Since header field in the request.
84+
// Instead, if the request method was GET or HEAD, the server SHOULD
85+
// respond with a 304 (Not Modified) response, including the cache-
86+
// related header fields (particularly ETag) of one of the entities that
87+
// matched. For all other request methods, the server MUST respond with
88+
// a status of 412 (Precondition Failed).
89+
90+
[Fact]
91+
public async Task IfNoneMatchShouldReturn304ForMatchingOnGetAndHeadMethod()
92+
{
93+
TestServer server = TestServer.Create(app => app.UseFileServer());
94+
HttpResponseMessage resp1 = await server.CreateClient().GetAsync("http://localhost/SubFolder/Extra.xml");
95+
96+
var req2 = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/Extra.xml");
97+
req2.Headers.Add("If-None-Match", resp1.Headers.ETag.ToString());
98+
HttpResponseMessage resp2 = await server.CreateClient().SendAsync(req2);
99+
resp2.StatusCode.ShouldBe(HttpStatusCode.NotModified);
100+
101+
var req3 = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/Extra.xml");
102+
req3.Headers.Add("If-None-Match", resp1.Headers.ETag.ToString());
103+
HttpResponseMessage resp3 = await server.CreateClient().SendAsync(req3);
104+
resp3.StatusCode.ShouldBe(HttpStatusCode.NotModified);
105+
}
106+
107+
[Fact]
108+
public async Task IfNoneMatchShouldBeIgnoredForNonTwoHundredAnd304Responses()
109+
{
110+
TestServer server = TestServer.Create(app => app.UseFileServer());
111+
HttpResponseMessage resp1 = await server.CreateClient().GetAsync("http://localhost/SubFolder/Extra.xml");
112+
113+
var req2 = new HttpRequestMessage(HttpMethod.Post, "http://localhost/SubFolder/Extra.xml");
114+
req2.Headers.Add("If-None-Match", resp1.Headers.ETag.ToString());
115+
HttpResponseMessage resp2 = await server.CreateClient().SendAsync(req2);
116+
resp2.StatusCode.ShouldBe(HttpStatusCode.NotFound);
117+
118+
var req3 = new HttpRequestMessage(HttpMethod.Put, "http://localhost/SubFolder/Extra.xml");
119+
req3.Headers.Add("If-None-Match", resp1.Headers.ETag.ToString());
120+
HttpResponseMessage resp3 = await server.CreateClient().SendAsync(req3);
121+
resp3.StatusCode.ShouldBe(HttpStatusCode.NotFound);
122+
}
123+
124+
// 14.26 If-None-Match
125+
// If none of the entity tags match, then the server MAY perform the
126+
// requested method as if the If-None-Match header field did not exist,
127+
// but MUST also ignore any If-Modified-Since header field(s) in the
128+
// request. That is, if no entity tags match, then the server MUST NOT
129+
// return a 304 (Not Modified) response.
130+
131+
// A server MUST use the strong comparison function (see section 13.3.3)
132+
// to compare the entity tags in If-Match.
133+
134+
[Fact]
135+
public async Task ServerShouldReturnLastModified()
136+
{
137+
TestServer server = TestServer.Create(app => app.UseFileServer());
138+
139+
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/SubFolder/Extra.xml");
140+
response.Content.Headers.LastModified.ShouldNotBe(null);
141+
}
142+
143+
// 13.3.4
144+
// An HTTP/1.1 origin server, upon receiving a conditional request that
145+
// includes both a Last-Modified date (e.g., in an If-Modified-Since or
146+
// If-Unmodified-Since header field) and one or more entity tags (e.g.,
147+
// in an If-Match, If-None-Match, or If-Range header field) as cache
148+
// validators, MUST NOT return a response status of 304 (Not Modified)
149+
// unless doing so is consistent with all of the conditional header
150+
// fields in the request.
151+
152+
[Fact]
153+
public async Task MatchingBothConditionsReturnsNotModified()
154+
{
155+
TestServer server = TestServer.Create(app => app.UseFileServer());
156+
HttpResponseMessage resp1 = await server
157+
.CreateRequest("/SubFolder/Extra.xml")
158+
.GetAsync();
159+
160+
HttpResponseMessage resp2 = await server
161+
.CreateRequest("/SubFolder/Extra.xml")
162+
.AddHeader("If-None-Match", resp1.Headers.ETag.ToString())
163+
.And(req => req.Headers.IfModifiedSince = resp1.Content.Headers.LastModified)
164+
.GetAsync();
165+
166+
resp2.StatusCode.ShouldBe(HttpStatusCode.NotModified);
167+
}
168+
169+
[Fact]
170+
public async Task MissingEitherOrBothConditionsReturnsNormally()
171+
{
172+
TestServer server = TestServer.Create(app => app.UseFileServer());
173+
HttpResponseMessage resp1 = await server
174+
.CreateRequest("/SubFolder/Extra.xml")
175+
.GetAsync();
176+
177+
DateTimeOffset lastModified = resp1.Content.Headers.LastModified.Value;
178+
DateTimeOffset pastDate = lastModified.AddHours(-1);
179+
DateTimeOffset furtureDate = lastModified.AddHours(1);
180+
181+
HttpResponseMessage resp2 = await server
182+
.CreateRequest("/SubFolder/Extra.xml")
183+
.AddHeader("If-None-Match", "\"fake\"")
184+
.And(req => req.Headers.IfModifiedSince = lastModified)
185+
.GetAsync();
186+
187+
HttpResponseMessage resp3 = await server
188+
.CreateRequest("/SubFolder/Extra.xml")
189+
.AddHeader("If-None-Match", resp1.Headers.ETag.ToString())
190+
.And(req => req.Headers.IfModifiedSince = pastDate)
191+
.GetAsync();
192+
193+
HttpResponseMessage resp4 = await server
194+
.CreateRequest("/SubFolder/Extra.xml")
195+
.AddHeader("If-None-Match", "\"fake\"")
196+
.And(req => req.Headers.IfModifiedSince = furtureDate)
197+
.GetAsync();
198+
199+
resp2.StatusCode.ShouldBe(HttpStatusCode.OK);
200+
resp3.StatusCode.ShouldBe(HttpStatusCode.OK);
201+
resp4.StatusCode.ShouldBe(HttpStatusCode.OK);
202+
}
203+
204+
// 14.25 If-Modified-Since
205+
// The If-Modified-Since request-header field is used with a method to
206+
// make it conditional: if the requested variant has not been modified
207+
// since the time specified in this field, an entity will not be
208+
// returned from the server; instead, a 304 (not modified) response will
209+
// be returned without any message-body.
210+
211+
// a) If the request would normally result in anything other than a
212+
// 200 (OK) status, or if the passed If-Modified-Since date is
213+
// invalid, the response is exactly the same as for a normal GET.
214+
// A date which is later than the server's current time is
215+
// invalid.
216+
[Fact]
217+
public async Task InvalidIfModifiedSinceDateFormatGivesNormalGet()
218+
{
219+
TestServer server = TestServer.Create(app => app.UseFileServer());
220+
221+
HttpResponseMessage res = await server
222+
.CreateRequest("/SubFolder/Extra.xml")
223+
.AddHeader("If-Modified-Since", "bad-date")
224+
.GetAsync();
225+
226+
res.StatusCode.ShouldBe(HttpStatusCode.OK);
227+
}
228+
229+
// b) If the variant has been modified since the If-Modified-Since
230+
// date, the response is exactly the same as for a normal GET.
231+
232+
// c) If the variant has not been modified since a valid If-
233+
// Modified-Since date, the server SHOULD return a 304 (Not
234+
// Modified) response.
235+
236+
[Fact]
237+
public async Task IfModifiedSinceDateEqualsLastModifiedShouldReturn304()
238+
{
239+
TestServer server = TestServer.Create(app => app.UseFileServer());
240+
241+
HttpResponseMessage res1 = await server
242+
.CreateRequest("/SubFolder/Extra.xml")
243+
.GetAsync();
244+
245+
HttpResponseMessage res2 = await server
246+
.CreateRequest("/SubFolder/Extra.xml")
247+
.And(req => req.Headers.IfModifiedSince = res1.Content.Headers.LastModified)
248+
.GetAsync();
249+
250+
res2.StatusCode.ShouldBe(HttpStatusCode.NotModified);
251+
}
252+
}
253+
}

0 commit comments

Comments
 (0)