DefaultFilesMiddlewareTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. using System.Net;
  4. using Microsoft.AspNetCore.Builder;
  5. using Microsoft.AspNetCore.Http;
  6. using Microsoft.AspNetCore.TestHost;
  7. using Microsoft.AspNetCore.Testing;
  8. using Microsoft.Extensions.DependencyInjection;
  9. using Microsoft.Extensions.FileProviders;
  10. namespace Microsoft.AspNetCore.StaticFiles;
  11. public class DefaultFilesMiddlewareTests
  12. {
  13. [Fact]
  14. public async Task NullArguments()
  15. {
  16. // No exception, default provided
  17. using (await StaticFilesTestServer.Create(app => app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = null })))
  18. { }
  19. // PathString(null) is OK.
  20. using var host = await StaticFilesTestServer.Create(app => app.UseDefaultFiles((string)null));
  21. using var server = host.GetTestServer();
  22. var response = await server.CreateClient().GetAsync("/");
  23. Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
  24. }
  25. [Theory]
  26. [InlineData("", @".", "/missing.dir")]
  27. [InlineData("", @".", "/missing.dir/")]
  28. [InlineData("/subdir", @".", "/subdir/missing.dir")]
  29. [InlineData("/subdir", @".", "/subdir/missing.dir/")]
  30. [InlineData("", @"./", "/missing.dir")]
  31. [InlineData("", @".", "/missing.dir", false)]
  32. [InlineData("", @".", "/missing.dir/", false)]
  33. [InlineData("/subdir", @".", "/subdir/missing.dir", false)]
  34. [InlineData("/subdir", @".", "/subdir/missing.dir/", false)]
  35. [InlineData("", @"./", "/missing.dir", false)]
  36. public async Task NoMatch_PassesThrough_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  37. {
  38. await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  39. }
  40. [ConditionalTheory]
  41. [OSSkipCondition(OperatingSystems.Linux)]
  42. [OSSkipCondition(OperatingSystems.MacOSX)]
  43. [InlineData("", @".\", "/missing.dir")]
  44. [InlineData("", @".\", "/Missing.dir")]
  45. [InlineData("", @".\", "/missing.dir", false)]
  46. [InlineData("", @".\", "/Missing.dir", false)]
  47. public async Task NoMatch_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  48. {
  49. await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  50. }
  51. private async Task NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  52. {
  53. using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
  54. {
  55. using var host = await StaticFilesTestServer.Create(app =>
  56. {
  57. app.UseDefaultFiles(new DefaultFilesOptions
  58. {
  59. RequestPath = new PathString(baseUrl),
  60. FileProvider = fileProvider,
  61. RedirectToAppendTrailingSlash = appendTrailingSlash
  62. });
  63. app.Run(context => context.Response.WriteAsync(context.Request.Path.Value));
  64. });
  65. using var server = host.GetTestServer();
  66. var response = await server.CreateClient().GetAsync(requestUrl);
  67. Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  68. Assert.Equal(requestUrl, await response.Content.ReadAsStringAsync()); // Should not be modified
  69. }
  70. }
  71. [Fact]
  72. public async Task Endpoint_PassesThrough()
  73. {
  74. using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, ".")))
  75. {
  76. using var host = await StaticFilesTestServer.Create(
  77. app =>
  78. {
  79. app.UseRouting();
  80. app.Use(next => context =>
  81. {
  82. // Assign an endpoint, this will make the default files noop.
  83. context.SetEndpoint(new Endpoint((c) =>
  84. {
  85. return context.Response.WriteAsync(context.Request.Path.Value);
  86. },
  87. new EndpointMetadataCollection(),
  88. "test"));
  89. return next(context);
  90. });
  91. app.UseDefaultFiles(new DefaultFilesOptions
  92. {
  93. RequestPath = new PathString(""),
  94. FileProvider = fileProvider
  95. });
  96. app.UseEndpoints(endpoints => { });
  97. },
  98. services => { services.AddDirectoryBrowser(); services.AddRouting(); });
  99. using var server = host.GetTestServer();
  100. var response = await server.CreateRequest("/SubFolder/").GetAsync();
  101. Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  102. Assert.Equal("/SubFolder/", await response.Content.ReadAsStringAsync()); // Should not be modified
  103. }
  104. }
  105. [Theory]
  106. [InlineData("", @".", "/SubFolder/")]
  107. [InlineData("", @"./", "/SubFolder/")]
  108. [InlineData("", @"./SubFolder", "/")]
  109. [InlineData("", @"./SubFolder", "/你好/")]
  110. [InlineData("", @"./SubFolder", "/你好/世界/")]
  111. [InlineData("", @".", "/SubFolder/", false)]
  112. [InlineData("", @"./", "/SubFolder/", false)]
  113. [InlineData("", @"./SubFolder", "/", false)]
  114. [InlineData("", @"./SubFolder", "/你好/", false)]
  115. [InlineData("", @"./SubFolder", "/你好/世界/", false)]
  116. [InlineData("", @".", "/SubFolder", false)]
  117. [InlineData("", @"./", "/SubFolder", false)]
  118. [InlineData("", @"./SubFolder", "", false)]
  119. [InlineData("", @"./SubFolder", "/你好", false)]
  120. [InlineData("", @"./SubFolder", "/你好/世界", false)]
  121. [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/40642")]
  122. public async Task FoundDirectoryWithDefaultFile_PathModified_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  123. {
  124. await FoundDirectoryWithDefaultFile_PathModified(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  125. }
  126. [ConditionalTheory]
  127. [OSSkipCondition(OperatingSystems.Linux)]
  128. [OSSkipCondition(OperatingSystems.MacOSX)]
  129. [InlineData("", @".\", "/SubFolder/")]
  130. [InlineData("", @".\subFolder", "/")]
  131. [InlineData("", @".\SubFolder", "/你好/")]
  132. [InlineData("", @".\SubFolder", "/你好/世界/")]
  133. [InlineData("", @".\", "/SubFolder/", false)]
  134. [InlineData("", @".\subFolder", "/", false)]
  135. [InlineData("", @".\SubFolder", "/你好/", false)]
  136. [InlineData("", @".\SubFolder", "/你好/世界/", false)]
  137. [InlineData("", @".\", "/SubFolder", false)]
  138. [InlineData("", @".\subFolder", "", false)]
  139. [InlineData("", @".\SubFolder", "/你好", false)]
  140. [InlineData("", @".\SubFolder", "/你好/世界", false)]
  141. [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/40642")]
  142. public async Task FoundDirectoryWithDefaultFile_PathModified_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  143. {
  144. await FoundDirectoryWithDefaultFile_PathModified(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  145. }
  146. private async Task FoundDirectoryWithDefaultFile_PathModified(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  147. {
  148. using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
  149. {
  150. using var host = await StaticFilesTestServer.Create(app =>
  151. {
  152. app.UseDefaultFiles(new DefaultFilesOptions
  153. {
  154. RequestPath = new PathString(baseUrl),
  155. FileProvider = fileProvider,
  156. RedirectToAppendTrailingSlash = appendTrailingSlash
  157. });
  158. app.Run(context => context.Response.WriteAsync(context.Request.Path.Value));
  159. });
  160. using var server = host.GetTestServer();
  161. var response = await server.CreateClient().GetAsync(requestUrl);
  162. Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  163. var requestUrlWithSlash = requestUrl.EndsWith('/') ? requestUrl : requestUrl + "/";
  164. Assert.Equal(requestUrlWithSlash + "default.html", await response.Content.ReadAsStringAsync()); // Should be modified and be valid path to file
  165. }
  166. }
  167. [Theory]
  168. [InlineData("", @".", "/SubFolder", "")]
  169. [InlineData("", @"./", "/SubFolder", "")]
  170. [InlineData("", @"./", "/SubFolder", "?a=b")]
  171. [InlineData("", @"./SubFolder", "/你好", "?a=b")]
  172. [InlineData("", @"./SubFolder", "/你好/世界", "?a=b")]
  173. [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/40642")]
  174. public async Task NearMatch_RedirectAddSlash_All(string baseUrl, string baseDir, string requestUrl, string queryString)
  175. {
  176. await NearMatch_RedirectAddSlash(baseUrl, baseDir, requestUrl, queryString);
  177. }
  178. [ConditionalTheory]
  179. [OSSkipCondition(OperatingSystems.Linux)]
  180. [OSSkipCondition(OperatingSystems.MacOSX)]
  181. [InlineData("", @".\", "/SubFolder", "")]
  182. [InlineData("", @".\", "/SubFolder", "?a=b")]
  183. [InlineData("", @".\SubFolder", "/你好", "?a=b")]
  184. [InlineData("", @".\SubFolder", "/你好/世界", "?a=b")]
  185. [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/40642")]
  186. public async Task NearMatch_RedirectAddSlash_Windows(string baseUrl, string baseDir, string requestUrl, string queryString)
  187. {
  188. await NearMatch_RedirectAddSlash(baseUrl, baseDir, requestUrl, queryString);
  189. }
  190. private async Task NearMatch_RedirectAddSlash(string baseUrl, string baseDir, string requestUrl, string queryString)
  191. {
  192. using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
  193. {
  194. using var host = await StaticFilesTestServer.Create(app => app.UseDefaultFiles(new DefaultFilesOptions
  195. {
  196. RequestPath = new PathString(baseUrl),
  197. FileProvider = fileProvider
  198. }));
  199. using var server = host.GetTestServer();
  200. var response = await server.CreateRequest(requestUrl + queryString).GetAsync();
  201. Assert.Equal(HttpStatusCode.Moved, response.StatusCode);
  202. // the url in the header of `Location: /xxx/xxx` should be encoded
  203. var actualURL = response.Headers.GetValues("Location").FirstOrDefault();
  204. Assert.Equal("http://localhost" + baseUrl + new PathString(requestUrl + "/") + queryString, actualURL);
  205. Assert.Empty((await response.Content.ReadAsByteArrayAsync()));
  206. }
  207. }
  208. [Theory]
  209. [InlineData("/SubFolder", @"./", "/SubFolder/")]
  210. [InlineData("/SubFolder", @".", "/somedir/")]
  211. [InlineData("", @"./SubFolder", "/")]
  212. [InlineData("", @"./SubFolder/", "/")]
  213. [InlineData("/SubFolder", @"./", "/SubFolder/", false)]
  214. [InlineData("/SubFolder", @".", "/somedir/", false)]
  215. [InlineData("", @"./SubFolder", "/", false)]
  216. [InlineData("", @"./SubFolder/", "/", false)]
  217. [InlineData("/SubFolder", @"./", "/SubFolder", false)]
  218. [InlineData("/SubFolder", @".", "/somedir", false)]
  219. [InlineData("", @"./SubFolder", "", false)]
  220. [InlineData("", @"./SubFolder/", "", false)]
  221. public async Task PostDirectory_PassesThrough_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  222. {
  223. await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  224. }
  225. [ConditionalTheory]
  226. [OSSkipCondition(OperatingSystems.Linux)]
  227. [OSSkipCondition(OperatingSystems.MacOSX)]
  228. [InlineData("/SubFolder", @".\", "/SubFolder/")]
  229. [InlineData("", @".\SubFolder", "/")]
  230. [InlineData("", @".\SubFolder\", "/")]
  231. [InlineData("/SubFolder", @".\", "/SubFolder/", false)]
  232. [InlineData("", @".\SubFolder", "/", false)]
  233. [InlineData("", @".\SubFolder\", "/", false)]
  234. [InlineData("/SubFolder", @".\", "/SubFolder", false)]
  235. [InlineData("", @".\SubFolder", "", false)]
  236. [InlineData("", @".\SubFolder\", "", false)]
  237. public async Task PostDirectory_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  238. {
  239. await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  240. }
  241. private async Task PostDirectory_PassesThrough(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  242. {
  243. using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
  244. {
  245. using var host = await StaticFilesTestServer.Create(app => app.UseDefaultFiles(new DefaultFilesOptions
  246. {
  247. RequestPath = new PathString(baseUrl),
  248. FileProvider = fileProvider,
  249. RedirectToAppendTrailingSlash = appendTrailingSlash
  250. }));
  251. using var server = host.GetTestServer();
  252. var response = await server.CreateRequest(requestUrl).GetAsync();
  253. Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); // Passed through
  254. }
  255. }
  256. [Fact]
  257. public void Options_AppendTrailingSlashByDefault()
  258. {
  259. Assert.True(new DefaultFilesOptions().RedirectToAppendTrailingSlash);
  260. }
  261. }