DefaultFilesMiddlewareTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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. public async Task FoundDirectoryWithDefaultFile_PathModified_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  122. {
  123. await FoundDirectoryWithDefaultFile_PathModified(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  124. }
  125. [ConditionalTheory]
  126. [OSSkipCondition(OperatingSystems.Linux)]
  127. [OSSkipCondition(OperatingSystems.MacOSX)]
  128. [InlineData("", @".\", "/SubFolder/")]
  129. [InlineData("", @".\subFolder", "/")]
  130. [InlineData("", @".\SubFolder", "/你好/")]
  131. [InlineData("", @".\SubFolder", "/你好/世界/")]
  132. [InlineData("", @".\", "/SubFolder/", false)]
  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. public async Task FoundDirectoryWithDefaultFile_PathModified_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  141. {
  142. await FoundDirectoryWithDefaultFile_PathModified(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  143. }
  144. private async Task FoundDirectoryWithDefaultFile_PathModified(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  145. {
  146. using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
  147. {
  148. using var host = await StaticFilesTestServer.Create(app =>
  149. {
  150. app.UseDefaultFiles(new DefaultFilesOptions
  151. {
  152. RequestPath = new PathString(baseUrl),
  153. FileProvider = fileProvider,
  154. RedirectToAppendTrailingSlash = appendTrailingSlash
  155. });
  156. app.Run(context => context.Response.WriteAsync(context.Request.Path.Value));
  157. });
  158. using var server = host.GetTestServer();
  159. var response = await server.CreateClient().GetAsync(requestUrl);
  160. Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  161. var requestUrlWithSlash = requestUrl.EndsWith('/') ? requestUrl : requestUrl + "/";
  162. Assert.Equal(requestUrlWithSlash + "default.html", await response.Content.ReadAsStringAsync()); // Should be modified and be valid path to file
  163. }
  164. }
  165. [Theory]
  166. [InlineData("", @".", "/SubFolder", "")]
  167. [InlineData("", @"./", "/SubFolder", "")]
  168. [InlineData("", @"./", "/SubFolder", "?a=b")]
  169. [InlineData("", @"./SubFolder", "/你好", "?a=b")]
  170. [InlineData("", @"./SubFolder", "/你好/世界", "?a=b")]
  171. public async Task NearMatch_RedirectAddSlash_All(string baseUrl, string baseDir, string requestUrl, string queryString)
  172. {
  173. await NearMatch_RedirectAddSlash(baseUrl, baseDir, requestUrl, queryString);
  174. }
  175. [ConditionalTheory]
  176. [OSSkipCondition(OperatingSystems.Linux)]
  177. [OSSkipCondition(OperatingSystems.MacOSX)]
  178. [InlineData("", @".\", "/SubFolder", "")]
  179. [InlineData("", @".\", "/SubFolder", "?a=b")]
  180. [InlineData("", @".\SubFolder", "/你好", "?a=b")]
  181. [InlineData("", @".\SubFolder", "/你好/世界", "?a=b")]
  182. public async Task NearMatch_RedirectAddSlash_Windows(string baseUrl, string baseDir, string requestUrl, string queryString)
  183. {
  184. await NearMatch_RedirectAddSlash(baseUrl, baseDir, requestUrl, queryString);
  185. }
  186. private async Task NearMatch_RedirectAddSlash(string baseUrl, string baseDir, string requestUrl, string queryString)
  187. {
  188. using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
  189. {
  190. using var host = await StaticFilesTestServer.Create(app => app.UseDefaultFiles(new DefaultFilesOptions
  191. {
  192. RequestPath = new PathString(baseUrl),
  193. FileProvider = fileProvider
  194. }));
  195. using var server = host.GetTestServer();
  196. var response = await server.CreateRequest(requestUrl + queryString).GetAsync();
  197. Assert.Equal(HttpStatusCode.Moved, response.StatusCode);
  198. // the url in the header of `Location: /xxx/xxx` should be encoded
  199. var actualURL = response.Headers.GetValues("Location").FirstOrDefault();
  200. Assert.Equal("http://localhost" + baseUrl + new PathString(requestUrl + "/") + queryString, actualURL);
  201. Assert.Empty((await response.Content.ReadAsByteArrayAsync()));
  202. }
  203. }
  204. [Theory]
  205. [InlineData("/SubFolder", @"./", "/SubFolder/")]
  206. [InlineData("/SubFolder", @".", "/somedir/")]
  207. [InlineData("", @"./SubFolder", "/")]
  208. [InlineData("", @"./SubFolder/", "/")]
  209. [InlineData("/SubFolder", @"./", "/SubFolder/", false)]
  210. [InlineData("/SubFolder", @".", "/somedir/", false)]
  211. [InlineData("", @"./SubFolder", "/", false)]
  212. [InlineData("", @"./SubFolder/", "/", false)]
  213. [InlineData("/SubFolder", @"./", "/SubFolder", false)]
  214. [InlineData("/SubFolder", @".", "/somedir", false)]
  215. [InlineData("", @"./SubFolder", "", false)]
  216. [InlineData("", @"./SubFolder/", "", false)]
  217. public async Task PostDirectory_PassesThrough_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  218. {
  219. await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  220. }
  221. [ConditionalTheory]
  222. [OSSkipCondition(OperatingSystems.Linux)]
  223. [OSSkipCondition(OperatingSystems.MacOSX)]
  224. [InlineData("/SubFolder", @".\", "/SubFolder/")]
  225. [InlineData("", @".\SubFolder", "/")]
  226. [InlineData("", @".\SubFolder\", "/")]
  227. [InlineData("/SubFolder", @".\", "/SubFolder/", false)]
  228. [InlineData("", @".\SubFolder", "/", false)]
  229. [InlineData("", @".\SubFolder\", "/", false)]
  230. [InlineData("/SubFolder", @".\", "/SubFolder", false)]
  231. [InlineData("", @".\SubFolder", "", false)]
  232. [InlineData("", @".\SubFolder\", "", false)]
  233. public async Task PostDirectory_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  234. {
  235. await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
  236. }
  237. private async Task PostDirectory_PassesThrough(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
  238. {
  239. using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
  240. {
  241. using var host = await StaticFilesTestServer.Create(app => app.UseDefaultFiles(new DefaultFilesOptions
  242. {
  243. RequestPath = new PathString(baseUrl),
  244. FileProvider = fileProvider,
  245. RedirectToAppendTrailingSlash = appendTrailingSlash
  246. }));
  247. using var server = host.GetTestServer();
  248. var response = await server.CreateRequest(requestUrl).GetAsync();
  249. Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); // Passed through
  250. }
  251. }
  252. [Fact]
  253. public void Options_AppendTrailingSlashByDefault()
  254. {
  255. Assert.True(new DefaultFilesOptions().RedirectToAppendTrailingSlash);
  256. }
  257. }