1#![warn(missing_docs, missing_debug_implementations)]
15#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
16#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
17#![cfg_attr(docsrs, feature(doc_cfg))]
18
19use std::path::Path;
20
21use actix_service::boxed::{BoxService, BoxServiceFactory};
22use actix_web::{
23 dev::{RequestHead, ServiceRequest, ServiceResponse},
24 error::Error,
25 http::header::DispositionType,
26};
27use mime_guess::from_ext;
28
29mod chunked;
30mod directory;
31mod encoding;
32mod error;
33mod files;
34mod named;
35mod path_buf;
36mod range;
37mod service;
38
39pub use self::{
40 chunked::ChunkedReadFile,
41 directory::Directory,
42 error::UriSegmentError,
43 files::{Files, FilesDirs},
44 named::NamedFile,
45 path_buf::PathBufWrap,
46 range::HttpRange,
47 service::FilesService,
48};
49use self::{
50 directory::{directory_listing, DirectoryRenderer},
51 error::FilesError,
52};
53
54type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;
55type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;
56
57#[inline]
61pub fn file_extension_to_mime(ext: &str) -> mime::Mime {
62 from_ext(ext).first_or_octet_stream()
63}
64
65type MimeOverride = dyn Fn(&mime::Name<'_>) -> DispositionType;
66
67type PathFilter = dyn Fn(&Path, &RequestHead) -> bool;
68
69#[cfg(test)]
70mod tests {
71 use std::{
72 ffi::OsString,
73 fmt::Write as _,
74 fs::{self},
75 ops::Add,
76 path::PathBuf,
77 time::{Duration, SystemTime},
78 };
79
80 use actix_http::header::HttpDate;
81 use actix_web::{
82 body,
83 dev::ServiceFactory,
84 guard,
85 http::{
86 header::{self, ContentDisposition, DispositionParam},
87 Method, StatusCode,
88 },
89 middleware::Compress,
90 test::{self, TestRequest},
91 web::{self, Bytes},
92 App, HttpResponse, Responder,
93 };
94
95 use super::*;
96 use crate::named::File;
97
98 #[actix_web::test]
99 async fn test_file_extension_to_mime() {
100 let m = file_extension_to_mime("");
101 assert_eq!(m, mime::APPLICATION_OCTET_STREAM);
102
103 let m = file_extension_to_mime("jpg");
104 assert_eq!(m, mime::IMAGE_JPEG);
105
106 let m = file_extension_to_mime("invalid extension!!");
107 assert_eq!(m, mime::APPLICATION_OCTET_STREAM);
108
109 let m = file_extension_to_mime("");
110 assert_eq!(m, mime::APPLICATION_OCTET_STREAM);
111 }
112
113 #[actix_rt::test]
114 async fn test_if_modified_since_without_if_none_match() {
115 let file = NamedFile::open("Cargo.toml").unwrap();
116 let since = header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));
117
118 let req = TestRequest::default()
119 .insert_header((header::IF_MODIFIED_SINCE, since))
120 .to_http_request();
121 let resp = file.respond_to(&req);
122 assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);
123 }
124
125 #[actix_rt::test]
126 async fn test_if_modified_since_without_if_none_match_same() {
127 let file = NamedFile::open("Cargo.toml").unwrap();
128 let since = file.last_modified().unwrap();
129
130 let req = TestRequest::default()
131 .insert_header((header::IF_MODIFIED_SINCE, since))
132 .to_http_request();
133 let resp = file.respond_to(&req);
134 assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);
135 }
136
137 #[actix_rt::test]
138 async fn test_if_modified_since_with_if_none_match() {
139 let file = NamedFile::open("Cargo.toml").unwrap();
140 let since = header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));
141
142 let req = TestRequest::default()
143 .insert_header((header::IF_NONE_MATCH, "miss_etag"))
144 .insert_header((header::IF_MODIFIED_SINCE, since))
145 .to_http_request();
146 let resp = file.respond_to(&req);
147 assert_ne!(resp.status(), StatusCode::NOT_MODIFIED);
148 }
149
150 #[actix_rt::test]
151 async fn test_if_unmodified_since() {
152 let file = NamedFile::open("Cargo.toml").unwrap();
153 let since = file.last_modified().unwrap();
154
155 let req = TestRequest::default()
156 .insert_header((header::IF_UNMODIFIED_SINCE, since))
157 .to_http_request();
158 let resp = file.respond_to(&req);
159 assert_eq!(resp.status(), StatusCode::OK);
160 }
161
162 #[actix_rt::test]
163 async fn test_if_unmodified_since_failed() {
164 let file = NamedFile::open("Cargo.toml").unwrap();
165 let since = header::HttpDate::from(SystemTime::UNIX_EPOCH);
166
167 let req = TestRequest::default()
168 .insert_header((header::IF_UNMODIFIED_SINCE, since))
169 .to_http_request();
170 let resp = file.respond_to(&req);
171 assert_eq!(resp.status(), StatusCode::PRECONDITION_FAILED);
172 }
173
174 #[actix_rt::test]
175 async fn test_named_file_text() {
176 assert!(NamedFile::open("test--").is_err());
177 let mut file = NamedFile::open("Cargo.toml").unwrap();
178 {
179 file.file();
180 let _f: &File = &file;
181 }
182 {
183 let _f: &mut File = &mut file;
184 }
185
186 let req = TestRequest::default().to_http_request();
187 let resp = file.respond_to(&req);
188 assert_eq!(
189 resp.headers().get(header::CONTENT_TYPE).unwrap(),
190 "text/x-toml"
191 );
192 assert_eq!(
193 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
194 "inline; filename=\"Cargo.toml\""
195 );
196 }
197
198 #[actix_rt::test]
199 async fn test_named_file_content_disposition() {
200 assert!(NamedFile::open("test--").is_err());
201 let mut file = NamedFile::open("Cargo.toml").unwrap();
202 {
203 file.file();
204 let _f: &File = &file;
205 }
206 {
207 let _f: &mut File = &mut file;
208 }
209
210 let req = TestRequest::default().to_http_request();
211 let resp = file.respond_to(&req);
212 assert_eq!(
213 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
214 "inline; filename=\"Cargo.toml\""
215 );
216
217 let file = NamedFile::open("Cargo.toml")
218 .unwrap()
219 .disable_content_disposition();
220 let req = TestRequest::default().to_http_request();
221 let resp = file.respond_to(&req);
222 assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none());
223 }
224
225 #[actix_rt::test]
226 async fn test_named_file_non_ascii_file_name() {
227 let file = crate::named::File::open("Cargo.toml").unwrap();
228
229 let mut file = NamedFile::from_file(file, "貨物.toml").unwrap();
230 {
231 file.file();
232 let _f: &File = &file;
233 }
234 {
235 let _f: &mut File = &mut file;
236 }
237
238 let req = TestRequest::default().to_http_request();
239 let resp = file.respond_to(&req);
240 assert_eq!(
241 resp.headers().get(header::CONTENT_TYPE).unwrap(),
242 "text/x-toml"
243 );
244 assert_eq!(
245 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
246 "inline; filename=\"貨物.toml\"; filename*=UTF-8''%E8%B2%A8%E7%89%A9.toml"
247 );
248 }
249
250 #[actix_rt::test]
251 async fn test_named_file_set_content_type() {
252 let mut file = NamedFile::open("Cargo.toml")
253 .unwrap()
254 .set_content_type(mime::TEXT_XML);
255 {
256 file.file();
257 let _f: &File = &file;
258 }
259 {
260 let _f: &mut File = &mut file;
261 }
262
263 let req = TestRequest::default().to_http_request();
264 let resp = file.respond_to(&req);
265 assert_eq!(
266 resp.headers().get(header::CONTENT_TYPE).unwrap(),
267 "text/xml"
268 );
269 assert_eq!(
270 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
271 "inline; filename=\"Cargo.toml\""
272 );
273 }
274
275 #[actix_rt::test]
276 async fn test_named_file_image() {
277 let mut file = NamedFile::open("tests/test.png").unwrap();
278 {
279 file.file();
280 let _f: &File = &file;
281 }
282 {
283 let _f: &mut File = &mut file;
284 }
285
286 let req = TestRequest::default().to_http_request();
287 let resp = file.respond_to(&req);
288 assert_eq!(
289 resp.headers().get(header::CONTENT_TYPE).unwrap(),
290 "image/png"
291 );
292 assert_eq!(
293 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
294 "inline; filename=\"test.png\""
295 );
296 }
297
298 #[actix_rt::test]
299 async fn test_named_file_javascript() {
300 let file = NamedFile::open("tests/test.js").unwrap();
301
302 let req = TestRequest::default().to_http_request();
303 let resp = file.respond_to(&req);
304 assert_eq!(
305 resp.headers().get(header::CONTENT_TYPE).unwrap(),
306 "text/javascript",
307 );
308 assert_eq!(
309 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
310 "inline; filename=\"test.js\"",
311 );
312 }
313
314 #[actix_rt::test]
315 async fn test_named_file_image_attachment() {
316 let cd = ContentDisposition {
317 disposition: DispositionType::Attachment,
318 parameters: vec![DispositionParam::Filename(String::from("test.png"))],
319 };
320 let mut file = NamedFile::open("tests/test.png")
321 .unwrap()
322 .set_content_disposition(cd);
323 {
324 file.file();
325 let _f: &File = &file;
326 }
327 {
328 let _f: &mut File = &mut file;
329 }
330
331 let req = TestRequest::default().to_http_request();
332 let resp = file.respond_to(&req);
333 assert_eq!(
334 resp.headers().get(header::CONTENT_TYPE).unwrap(),
335 "image/png"
336 );
337 assert_eq!(
338 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
339 "attachment; filename=\"test.png\""
340 );
341 }
342
343 #[actix_rt::test]
344 async fn test_named_file_binary() {
345 let mut file = NamedFile::open("tests/test.binary").unwrap();
346 {
347 file.file();
348 let _f: &File = &file;
349 }
350 {
351 let _f: &mut File = &mut file;
352 }
353
354 let req = TestRequest::default().to_http_request();
355 let resp = file.respond_to(&req);
356 assert_eq!(
357 resp.headers().get(header::CONTENT_TYPE).unwrap(),
358 "application/octet-stream"
359 );
360 assert_eq!(
361 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
362 "attachment; filename=\"test.binary\""
363 );
364 }
365
366 #[allow(deprecated)]
367 #[actix_rt::test]
368 async fn status_code_customize_same_output() {
369 let file1 = NamedFile::open("Cargo.toml")
370 .unwrap()
371 .set_status_code(StatusCode::NOT_FOUND);
372
373 let file2 = NamedFile::open("Cargo.toml")
374 .unwrap()
375 .customize()
376 .with_status(StatusCode::NOT_FOUND);
377
378 let req = TestRequest::default().to_http_request();
379 let res1 = file1.respond_to(&req);
380 let res2 = file2.respond_to(&req);
381
382 assert_eq!(res1.status(), StatusCode::NOT_FOUND);
383 assert_eq!(res2.status(), StatusCode::NOT_FOUND);
384 }
385
386 #[actix_rt::test]
387 async fn test_named_file_status_code_text() {
388 let mut file = NamedFile::open("Cargo.toml").unwrap();
389
390 {
391 file.file();
392 let _f: &File = &file;
393 }
394
395 {
396 let _f: &mut File = &mut file;
397 }
398
399 let file = file.customize().with_status(StatusCode::NOT_FOUND);
400
401 let req = TestRequest::default().to_http_request();
402 let resp = file.respond_to(&req);
403 assert_eq!(
404 resp.headers().get(header::CONTENT_TYPE).unwrap(),
405 "text/x-toml"
406 );
407 assert_eq!(
408 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
409 "inline; filename=\"Cargo.toml\""
410 );
411 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
412 }
413
414 #[actix_rt::test]
415 async fn test_mime_override() {
416 fn all_attachment(_: &mime::Name<'_>) -> DispositionType {
417 DispositionType::Attachment
418 }
419
420 let srv = test::init_service(
421 App::new().service(
422 Files::new("/", ".")
423 .mime_override(all_attachment)
424 .index_file("Cargo.toml"),
425 ),
426 )
427 .await;
428
429 let request = TestRequest::get().uri("/").to_request();
430 let response = test::call_service(&srv, request).await;
431 assert_eq!(response.status(), StatusCode::OK);
432
433 let content_disposition = response
434 .headers()
435 .get(header::CONTENT_DISPOSITION)
436 .expect("To have CONTENT_DISPOSITION");
437 let content_disposition = content_disposition
438 .to_str()
439 .expect("Convert CONTENT_DISPOSITION to str");
440 assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\"");
441 }
442
443 #[actix_rt::test]
444 async fn test_named_file_ranges_status_code() {
445 let srv = test::init_service(
446 App::new().service(Files::new("/test", ".").index_file("Cargo.toml")),
447 )
448 .await;
449
450 let request = TestRequest::get()
452 .uri("/t%65st/Cargo.toml")
453 .insert_header((header::RANGE, "bytes=10-20"))
454 .to_request();
455 let response = test::call_service(&srv, request).await;
456 assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
457
458 let request = TestRequest::get()
460 .uri("/t%65st/Cargo.toml")
461 .insert_header((header::RANGE, "bytes=1-0"))
462 .to_request();
463 let response = test::call_service(&srv, request).await;
464
465 assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
466 }
467
468 #[actix_rt::test]
469 async fn test_named_file_if_range_etag() {
470 let file = NamedFile::open("Cargo.toml").unwrap();
471 let etag = file.etag().unwrap();
472
473 let req = TestRequest::default()
474 .insert_header((header::RANGE, "bytes=0-9"))
475 .insert_header((header::IF_RANGE, etag.to_string()))
476 .to_http_request();
477
478 let resp = file.respond_to(&req);
479 assert_eq!(resp.status(), StatusCode::PARTIAL_CONTENT);
480
481 for stale in ["\"stale\"", "W/\"stale\"", "not a validator"] {
483 let file = NamedFile::open("Cargo.toml").unwrap();
484 let content = fs::read("Cargo.toml").unwrap();
485
486 let req = TestRequest::default()
487 .insert_header((header::RANGE, "bytes=0-9"))
488 .insert_header((header::IF_RANGE, stale))
489 .to_http_request();
490
491 let resp = file.respond_to(&req);
492 assert_eq!(resp.status(), StatusCode::OK, "If-Range: {stale}");
493 assert!(!resp.headers().contains_key(header::CONTENT_RANGE));
494
495 let body = body::to_bytes(resp.into_body()).await.unwrap();
496
497 assert_eq!(
498 body, content,
499 "Response body and full file content should match",
500 );
501 }
502 }
503
504 #[actix_rt::test]
505 async fn test_named_file_if_range_date() {
506 let file = NamedFile::open("Cargo.toml").unwrap();
507 let content = fs::read("Cargo.toml").unwrap();
508 let modified = file.last_modified().unwrap();
509
510 let req = TestRequest::default()
511 .insert_header((header::RANGE, "bytes=0-9"))
512 .insert_header((header::IF_RANGE, modified))
513 .to_http_request();
514
515 let resp = file.respond_to(&req);
516 assert_eq!(resp.status(), StatusCode::PARTIAL_CONTENT);
517
518 let file = NamedFile::open("Cargo.toml").unwrap();
519 let later = HttpDate::from(SystemTime::from(modified).add(Duration::from_secs(60)));
520 let req = TestRequest::default()
521 .insert_header((header::RANGE, "bytes=0-9"))
522 .insert_header((header::IF_RANGE, later))
523 .to_http_request();
524
525 let resp = file.respond_to(&req);
526 assert_eq!(resp.status(), StatusCode::OK);
527 assert!(!resp.headers().contains_key(header::CONTENT_RANGE));
528
529 let body = body::to_bytes(resp.into_body()).await.unwrap();
530
531 assert_eq!(
532 body, content,
533 "Response body and full file content should match",
534 );
535 }
536
537 #[actix_rt::test]
538 async fn test_named_file_empty_range_headers() {
539 let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
540
541 for range in ["", "bytes="] {
542 let response = srv
543 .get("/tests/test.binary")
544 .insert_header((header::RANGE, range))
545 .send()
546 .await
547 .unwrap();
548
549 assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
550 let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();
551 assert_eq!(content_range.to_str().unwrap(), "bytes */100");
552 }
553 }
554
555 #[actix_rt::test]
556 async fn test_named_file_content_range_headers() {
557 let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
558
559 let response = srv
561 .get("/tests/test.binary")
562 .insert_header((header::RANGE, "bytes=10-20"))
563 .send()
564 .await
565 .unwrap();
566 let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();
567 assert_eq!(content_range.to_str().unwrap(), "bytes 10-20/100");
568
569 let response = srv
571 .get("/tests/test.binary")
572 .insert_header((header::RANGE, "bytes=10-5"))
573 .send()
574 .await
575 .unwrap();
576 let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();
577 assert_eq!(content_range.to_str().unwrap(), "bytes */100");
578 }
579
580 #[actix_rt::test]
581 async fn test_named_file_range_header_from_zero_to_end_returns_partial_content() {
582 let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
583
584 let response = srv
585 .get("/tests/test.binary")
586 .insert_header((header::RANGE, "bytes=0-"))
587 .send()
588 .await
589 .unwrap();
590
591 assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
592
593 let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();
594 assert_eq!(content_range.to_str().unwrap(), "bytes 0-99/100");
595
596 let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();
597 assert_eq!(content_length.to_str().unwrap(), "100");
598
599 let transfer_encoding = response.headers().get(header::TRANSFER_ENCODING);
601 assert!(transfer_encoding.is_none());
602 }
603
604 #[actix_rt::test]
605 async fn test_named_file_content_length_headers() {
606 let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
607
608 let response = srv
610 .get("/tests/test.binary")
611 .insert_header((header::RANGE, "bytes=10-20"))
612 .send()
613 .await
614 .unwrap();
615 let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();
616 assert_eq!(content_length.to_str().unwrap(), "11");
617
618 let response = srv
620 .get("/tests/test.binary")
621 .insert_header((header::RANGE, "bytes=0-20"))
622 .send()
623 .await
624 .unwrap();
625 let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();
626 assert_eq!(content_length.to_str().unwrap(), "21");
627
628 let mut response = srv.get("/tests/test.binary").send().await.unwrap();
630 let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();
631 assert_eq!(content_length.to_str().unwrap(), "100");
632
633 let transfer_encoding = response.headers().get(header::TRANSFER_ENCODING);
635 assert!(transfer_encoding.is_none());
636
637 let bytes = response.body().await.unwrap();
639 let data = web::Bytes::from(fs::read("tests/test.binary").unwrap());
640 assert_eq!(bytes, data);
641 }
642
643 #[actix_rt::test]
644 async fn test_head_content_length_headers() {
645 let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
646
647 let response = srv.head("/tests/test.binary").send().await.unwrap();
648
649 let content_length = response
650 .headers()
651 .get(header::CONTENT_LENGTH)
652 .unwrap()
653 .to_str()
654 .unwrap();
655
656 assert_eq!(content_length, "100");
657 }
658
659 #[actix_rt::test]
660 async fn test_static_files_with_spaces() {
661 let srv =
662 test::init_service(App::new().service(Files::new("/", ".").index_file("Cargo.toml")))
663 .await;
664 let request = TestRequest::get()
665 .uri("/tests/test%20space.binary")
666 .to_request();
667 let response = test::call_service(&srv, request).await;
668 assert_eq!(response.status(), StatusCode::OK);
669
670 let bytes = test::read_body(response).await;
671 let data = web::Bytes::from(fs::read("tests/test space.binary").unwrap());
672 assert_eq!(bytes, data);
673 }
674
675 #[cfg(not(target_os = "windows"))]
676 #[actix_rt::test]
677 async fn test_static_files_with_special_characters() {
678 let temp_dir = tempfile::tempdir().unwrap();
681 let file_with_newlines = temp_dir.path().join("test\n\x0B\x0C\rnewline.text");
682 fs::write(&file_with_newlines, "Look at my newlines").unwrap();
683
684 let srv = test::init_service(
685 App::new().service(Files::new("/", temp_dir.path()).index_file("Cargo.toml")),
686 )
687 .await;
688 let request = TestRequest::get()
689 .uri("/test%0A%0B%0C%0Dnewline.text")
690 .to_request();
691 let response = test::call_service(&srv, request).await;
692 assert_eq!(response.status(), StatusCode::OK);
693
694 let bytes = test::read_body(response).await;
695 let data = web::Bytes::from(fs::read(file_with_newlines).unwrap());
696 assert_eq!(bytes, data);
697 }
698
699 #[actix_rt::test]
700 async fn test_files_not_allowed() {
701 let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
702
703 let req = TestRequest::default()
704 .uri("/Cargo.toml")
705 .method(Method::POST)
706 .to_request();
707
708 let resp = test::call_service(&srv, req).await;
709 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
710
711 let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
712 let req = TestRequest::default()
713 .method(Method::PUT)
714 .uri("/Cargo.toml")
715 .to_request();
716 let resp = test::call_service(&srv, req).await;
717 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
718 }
719
720 #[actix_rt::test]
721 async fn test_files_guards() {
722 let srv = test::init_service(
723 App::new().service(Files::new("/", ".").method_guard(guard::Post())),
724 )
725 .await;
726
727 let req = TestRequest::default()
728 .uri("/Cargo.toml")
729 .method(Method::POST)
730 .to_request();
731
732 let resp = test::call_service(&srv, req).await;
733 assert_eq!(resp.status(), StatusCode::OK);
734 }
735
736 #[actix_rt::test]
737 async fn test_named_file_content_encoding() {
738 let srv = test::init_service(App::new().wrap(Compress::default()).service(
739 web::resource("/").to(|| async {
740 NamedFile::open("Cargo.toml")
741 .unwrap()
742 .set_content_encoding(header::ContentEncoding::Identity)
743 }),
744 ))
745 .await;
746
747 let request = TestRequest::get()
748 .uri("/")
749 .insert_header((header::ACCEPT_ENCODING, "gzip"))
750 .to_request();
751 let res = test::call_service(&srv, request).await;
752 assert_eq!(res.status(), StatusCode::OK);
753 assert!(res.headers().contains_key(header::CONTENT_ENCODING));
754 assert!(!test::read_body(res).await.is_empty());
755 }
756
757 #[actix_rt::test]
758 async fn test_named_file_content_encoding_gzip() {
759 let srv = test::init_service(App::new().wrap(Compress::default()).service(
760 web::resource("/").to(|| async {
761 NamedFile::open("Cargo.toml")
762 .unwrap()
763 .set_content_encoding(header::ContentEncoding::Gzip)
764 }),
765 ))
766 .await;
767
768 let request = TestRequest::get()
769 .uri("/")
770 .insert_header((header::ACCEPT_ENCODING, "gzip"))
771 .to_request();
772 let res = test::call_service(&srv, request).await;
773 assert_eq!(res.status(), StatusCode::OK);
774 assert_eq!(
775 res.headers()
776 .get(header::CONTENT_ENCODING)
777 .unwrap()
778 .to_str()
779 .unwrap(),
780 "gzip"
781 );
782 }
783
784 #[actix_rt::test]
785 async fn test_named_file_allowed_method() {
786 let req = TestRequest::default().method(Method::GET).to_http_request();
787 let file = NamedFile::open("Cargo.toml").unwrap();
788 let resp = file.respond_to(&req);
789 assert_eq!(resp.status(), StatusCode::OK);
790 }
791
792 #[actix_rt::test]
793 async fn test_static_files() {
794 let srv =
795 test::init_service(App::new().service(Files::new("/", ".").show_files_listing())).await;
796 let req = TestRequest::with_uri("/missing").to_request();
797
798 let resp = test::call_service(&srv, req).await;
799 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
800
801 let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
802
803 let req = TestRequest::default().to_request();
804 let resp = test::call_service(&srv, req).await;
805 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
806
807 let srv =
808 test::init_service(App::new().service(Files::new("/", ".").show_files_listing())).await;
809 let req = TestRequest::with_uri("/tests").to_request();
810 let resp = test::call_service(&srv, req).await;
811 assert_eq!(
812 resp.headers().get(header::CONTENT_TYPE).unwrap(),
813 "text/html; charset=utf-8"
814 );
815
816 let bytes = test::read_body(resp).await;
817 assert!(format!("{:?}", bytes).contains("/tests/test.png"));
818 }
819
820 #[actix_rt::test]
821 async fn test_redirect_to_slash_directory() {
822 let srv = test::init_service(
824 App::new().service(Files::new("/", ".").redirect_to_slash_directory()),
825 )
826 .await;
827 let req = TestRequest::with_uri("/tests").to_request();
828 let resp = test::call_service(&srv, req).await;
829 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
830
831 let srv = test::init_service(
833 App::new().service(
834 Files::new("/", ".")
835 .index_file("test.png")
836 .redirect_to_slash_directory(),
837 ),
838 )
839 .await;
840 let req = TestRequest::with_uri("/tests").to_request();
841 let resp = test::call_service(&srv, req).await;
842 assert_eq!(resp.status(), StatusCode::TEMPORARY_REDIRECT);
843
844 let srv = test::init_service(
846 App::new().service(
847 Files::new("/", ".")
848 .index_file("test.png")
849 .redirect_to_slash_directory()
850 .with_permanent_redirect(),
851 ),
852 )
853 .await;
854 let req = TestRequest::with_uri("/tests").to_request();
855 let resp = test::call_service(&srv, req).await;
856 assert_eq!(resp.status(), StatusCode::PERMANENT_REDIRECT);
857
858 let srv = test::init_service(
860 App::new().service(
861 Files::new("/", ".")
862 .show_files_listing()
863 .redirect_to_slash_directory(),
864 ),
865 )
866 .await;
867 let req = TestRequest::with_uri("/tests").to_request();
868 let resp = test::call_service(&srv, req).await;
869 assert_eq!(resp.status(), StatusCode::TEMPORARY_REDIRECT);
870
871 let req = TestRequest::with_uri("/not_existing").to_request();
873 let resp = test::call_service(&srv, req).await;
874 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
875 }
876
877 #[actix_rt::test]
878 async fn test_static_files_bad_directory() {
879 let service = Files::new("/", "./missing").new_service(()).await.unwrap();
880
881 let req = TestRequest::with_uri("/").to_srv_request();
882 let resp = test::call_service(&service, req).await;
883
884 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
885 }
886
887 #[actix_rt::test]
888 async fn test_static_files_bad_directory_does_not_serve_cwd_files() {
889 let service = Files::new("/", "./missing").new_service(()).await.unwrap();
890
891 let req = TestRequest::with_uri("/Cargo.toml").to_srv_request();
892 let resp = test::call_service(&service, req).await;
893
894 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
895 }
896
897 #[actix_rt::test]
898 async fn test_static_files_accepts_borrowed_os_string_directory() {
899 let dir = OsString::from(".");
900 let service = Files::new("/", &dir).new_service(()).await.unwrap();
901
902 let req = TestRequest::with_uri("/Cargo.toml").to_srv_request();
903 let resp = test::call_service(&service, req).await;
904
905 assert_eq!(resp.status(), StatusCode::OK);
906 }
907
908 #[actix_rt::test]
909 async fn test_static_files_empty_directories() {
910 let service = Files::new("/", Vec::<PathBuf>::new())
911 .new_service(())
912 .await
913 .unwrap();
914
915 let req = TestRequest::with_uri("/Cargo.toml").to_srv_request();
916 let resp = test::call_service(&service, req).await;
917 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
918
919 let service = Files::new("/", Vec::<PathBuf>::new())
920 .default_handler(|req: ServiceRequest| async {
921 Ok(req.into_response(HttpResponse::Ok().body("default content")))
922 })
923 .new_service(())
924 .await
925 .unwrap();
926
927 let req = TestRequest::with_uri("/Cargo.toml").to_srv_request();
928 let resp = test::call_service(&service, req).await;
929 assert_eq!(resp.status(), StatusCode::OK);
930 assert_eq!(
931 test::read_body(resp).await,
932 Bytes::from_static(b"default content")
933 );
934 }
935
936 #[actix_rt::test]
937 async fn test_static_files_multiple_directories() {
938 let first_dir = tempfile::tempdir().unwrap();
939 let second_dir = tempfile::tempdir().unwrap();
940
941 fs::write(first_dir.path().join("shared.txt"), "first").unwrap();
942 fs::write(second_dir.path().join("shared.txt"), "second").unwrap();
943 fs::write(second_dir.path().join("fallback.txt"), "fallback").unwrap();
944
945 let service = Files::new("/", [first_dir.path(), second_dir.path()])
946 .new_service(())
947 .await
948 .unwrap();
949
950 let req = TestRequest::with_uri("/shared.txt").to_srv_request();
951 let resp = test::call_service(&service, req).await;
952 assert_eq!(resp.status(), StatusCode::OK);
953 assert_eq!(test::read_body(resp).await, Bytes::from_static(b"first"));
954
955 let req = TestRequest::with_uri("/fallback.txt").to_srv_request();
956 let resp = test::call_service(&service, req).await;
957 assert_eq!(resp.status(), StatusCode::OK);
958 assert_eq!(test::read_body(resp).await, Bytes::from_static(b"fallback"));
959 }
960
961 #[actix_rt::test]
962 async fn test_static_files_multiple_directories_file_as_parent_falls_back() {
963 let first_dir = tempfile::tempdir().unwrap();
964 let second_dir = tempfile::tempdir().unwrap();
965
966 fs::write(first_dir.path().join("assets"), "file").unwrap();
967 fs::create_dir(second_dir.path().join("assets")).unwrap();
968 fs::write(
969 second_dir.path().join("assets").join("fallback.txt"),
970 "fallback",
971 )
972 .unwrap();
973
974 let service = Files::new("/", [first_dir.path(), second_dir.path()])
975 .new_service(())
976 .await
977 .unwrap();
978
979 let req = TestRequest::with_uri("/assets/fallback.txt").to_srv_request();
980 let resp = test::call_service(&service, req).await;
981 assert_eq!(resp.status(), StatusCode::OK);
982 assert_eq!(test::read_body(resp).await, Bytes::from_static(b"fallback"));
983 }
984
985 #[actix_rt::test]
986 async fn test_static_files_multiple_directories_default_handler() {
987 let first_dir = tempfile::tempdir().unwrap();
988 let second_dir = tempfile::tempdir().unwrap();
989
990 fs::write(second_dir.path().join("fallback.txt"), "fallback").unwrap();
991
992 let service = Files::new("/", vec![first_dir.path(), second_dir.path()])
993 .default_handler(|req: ServiceRequest| async {
994 Ok(req.into_response(HttpResponse::Ok().body("default content")))
995 })
996 .new_service(())
997 .await
998 .unwrap();
999
1000 let req = TestRequest::with_uri("/fallback.txt").to_srv_request();
1001 let resp = test::call_service(&service, req).await;
1002 assert_eq!(resp.status(), StatusCode::OK);
1003 assert_eq!(test::read_body(resp).await, Bytes::from_static(b"fallback"));
1004
1005 let req = TestRequest::with_uri("/missing.txt").to_srv_request();
1006 let resp = test::call_service(&service, req).await;
1007 assert_eq!(resp.status(), StatusCode::OK);
1008 assert_eq!(
1009 test::read_body(resp).await,
1010 Bytes::from_static(b"default content")
1011 );
1012 }
1013
1014 #[actix_rt::test]
1015 async fn test_static_files_multiple_directories_index_file() {
1016 let first_dir = tempfile::tempdir().unwrap();
1017 let second_dir = tempfile::tempdir().unwrap();
1018
1019 fs::create_dir(first_dir.path().join("nested")).unwrap();
1020 fs::create_dir(second_dir.path().join("nested")).unwrap();
1021 fs::write(
1022 second_dir.path().join("nested").join("index.html"),
1023 "second index",
1024 )
1025 .unwrap();
1026
1027 let service = Files::new("/", [first_dir.path(), second_dir.path()])
1028 .index_file("index.html")
1029 .new_service(())
1030 .await
1031 .unwrap();
1032
1033 let req = TestRequest::with_uri("/nested/").to_srv_request();
1034 let resp = test::call_service(&service, req).await;
1035 assert_eq!(resp.status(), StatusCode::OK);
1036 assert_eq!(
1037 test::read_body(resp).await,
1038 Bytes::from_static(b"second index")
1039 );
1040 }
1041
1042 #[actix_rt::test]
1043 async fn test_static_files_multiple_directories_index_file_as_parent_falls_back() {
1044 let first_dir = tempfile::tempdir().unwrap();
1045 let second_dir = tempfile::tempdir().unwrap();
1046
1047 fs::create_dir(first_dir.path().join("nested")).unwrap();
1048 fs::write(first_dir.path().join("nested").join("index.html"), "file").unwrap();
1049 fs::create_dir(second_dir.path().join("nested")).unwrap();
1050 fs::create_dir(second_dir.path().join("nested").join("index.html")).unwrap();
1051 fs::write(
1052 second_dir
1053 .path()
1054 .join("nested")
1055 .join("index.html")
1056 .join("fallback.txt"),
1057 "fallback",
1058 )
1059 .unwrap();
1060
1061 let service = Files::new("/", [first_dir.path(), second_dir.path()])
1062 .index_file("index.html/fallback.txt")
1063 .new_service(())
1064 .await
1065 .unwrap();
1066
1067 let req = TestRequest::with_uri("/nested/").to_srv_request();
1068 let resp = test::call_service(&service, req).await;
1069 assert_eq!(resp.status(), StatusCode::OK);
1070 assert_eq!(test::read_body(resp).await, Bytes::from_static(b"fallback"));
1071 }
1072
1073 #[actix_rt::test]
1074 async fn test_static_files_index_file_error_falls_back_to_listing() {
1075 let dir = tempfile::tempdir().unwrap();
1076
1077 fs::write(dir.path().join("listed.txt"), "listed").unwrap();
1078
1079 let service = Files::new("/", dir.path())
1080 .index_file("index.html\0")
1081 .show_files_listing()
1082 .new_service(())
1083 .await
1084 .unwrap();
1085
1086 let req = TestRequest::with_uri("/").to_srv_request();
1087 let resp = test::call_service(&service, req).await;
1088 assert_eq!(resp.status(), StatusCode::OK);
1089 let bytes = test::read_body(resp).await;
1090 assert!(format!("{bytes:?}").contains("listed.txt"));
1091 }
1092
1093 #[actix_rt::test]
1094 async fn test_static_files_multiple_directories_show_files_listing() {
1095 let first_dir = tempfile::tempdir().unwrap();
1096 let second_dir = tempfile::tempdir().unwrap();
1097
1098 fs::write(first_dir.path().join("listed.txt"), "listed").unwrap();
1099
1100 let service = Files::new("/", [first_dir.path(), second_dir.path()])
1101 .show_files_listing()
1102 .new_service(())
1103 .await
1104 .unwrap();
1105
1106 let req = TestRequest::with_uri("/").to_srv_request();
1107 let resp = test::call_service(&service, req).await;
1108 assert_eq!(resp.status(), StatusCode::OK);
1109 let bytes = test::read_body(resp).await;
1110 assert!(format!("{bytes:?}").contains("listed.txt"));
1111 }
1112
1113 #[actix_rt::test]
1114 async fn test_static_files_multiple_directories_redirect_precedence() {
1115 let first_dir = tempfile::tempdir().unwrap();
1116 let second_dir = tempfile::tempdir().unwrap();
1117
1118 fs::create_dir(first_dir.path().join("item")).unwrap();
1119 fs::write(second_dir.path().join("item"), "file").unwrap();
1120
1121 let service = Files::new("/", [first_dir.path(), second_dir.path()])
1122 .show_files_listing()
1123 .redirect_to_slash_directory()
1124 .new_service(())
1125 .await
1126 .unwrap();
1127
1128 let req = TestRequest::with_uri("/item").to_srv_request();
1129 let resp = test::call_service(&service, req).await;
1130 assert_eq!(resp.status(), StatusCode::TEMPORARY_REDIRECT);
1131 assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/item/");
1132 }
1133
1134 #[actix_rt::test]
1135 async fn test_default_handler_file_missing() {
1136 let st = Files::new("/", ".")
1137 .default_handler(|req: ServiceRequest| async {
1138 Ok(req.into_response(HttpResponse::Ok().body("default content")))
1139 })
1140 .new_service(())
1141 .await
1142 .unwrap();
1143 let req = TestRequest::with_uri("/missing").to_srv_request();
1144 let resp = test::call_service(&st, req).await;
1145
1146 assert_eq!(resp.status(), StatusCode::OK);
1147 let bytes = test::read_body(resp).await;
1148 assert_eq!(bytes, web::Bytes::from_static(b"default content"));
1149 }
1150
1151 #[actix_rt::test]
1152 async fn test_hidden_files_reject_cur_dir_segment() {
1153 let service = Files::new("/", Vec::<PathBuf>::new())
1154 .use_hidden_files()
1155 .default_handler(Files::new("/", ".").use_hidden_files())
1156 .new_service(())
1157 .await
1158 .unwrap();
1159
1160 let req = TestRequest::with_uri("/./Cargo.toml").to_srv_request();
1161 let resp = test::call_service(&service, req).await;
1162
1163 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
1164 }
1165
1166 #[actix_rt::test]
1167 async fn test_serve_index_nested() {
1168 let service = Files::new(".", ".")
1169 .index_file("lib.rs")
1170 .new_service(())
1171 .await
1172 .unwrap();
1173
1174 let req = TestRequest::default().uri("/src").to_srv_request();
1175 let resp = test::call_service(&service, req).await;
1176
1177 assert_eq!(resp.status(), StatusCode::OK);
1178 assert_eq!(
1179 resp.headers().get(header::CONTENT_TYPE).unwrap(),
1180 "text/x-rust"
1181 );
1182 assert_eq!(
1183 resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
1184 "inline; filename=\"lib.rs\""
1185 );
1186 }
1187
1188 #[actix_rt::test]
1189 async fn integration_serve_index() {
1190 let srv = test::init_service(
1191 App::new().service(Files::new("test", ".").index_file("Cargo.toml")),
1192 )
1193 .await;
1194
1195 let req = TestRequest::get().uri("/test").to_request();
1196 let res = test::call_service(&srv, req).await;
1197 assert_eq!(res.status(), StatusCode::OK);
1198
1199 let bytes = test::read_body(res).await;
1200
1201 let data = Bytes::from(fs::read("Cargo.toml").unwrap());
1202 assert_eq!(bytes, data);
1203
1204 let req = TestRequest::get().uri("/test/").to_request();
1205 let res = test::call_service(&srv, req).await;
1206 assert_eq!(res.status(), StatusCode::OK);
1207
1208 let bytes = test::read_body(res).await;
1209 let data = Bytes::from(fs::read("Cargo.toml").unwrap());
1210 assert_eq!(bytes, data);
1211
1212 let req = TestRequest::get().uri("/test/unknown").to_request();
1214 let res = test::call_service(&srv, req).await;
1215 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1216
1217 let req = TestRequest::get().uri("/test/unknown/").to_request();
1218 let res = test::call_service(&srv, req).await;
1219 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1220 }
1221
1222 #[actix_rt::test]
1223 async fn integration_percent_encoded() {
1224 let srv = test::init_service(
1225 App::new().service(Files::new("test", ".").index_file("Cargo.toml")),
1226 )
1227 .await;
1228
1229 let req = TestRequest::get().uri("/test/%43argo.toml").to_request();
1230 let res = test::call_service(&srv, req).await;
1231 assert_eq!(res.status(), StatusCode::OK);
1232
1233 let req = TestRequest::get().uri("/test%2Ftest.binary").to_request();
1235 let res = test::call_service(&srv, req).await;
1236 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1237
1238 let req = TestRequest::get().uri("/test/Cargo.toml%00").to_request();
1239 let res = test::call_service(&srv, req).await;
1240 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1241 }
1242
1243 #[actix_rt::test]
1244 async fn test_percent_encoding_2() {
1245 let temp_dir = tempfile::tempdir().unwrap();
1246 let filename = match cfg!(unix) {
1247 true => "ض:?#[]{}<>()@!$&'`|*+,;= %20\n.test",
1248 false => "ض#[]{}()@!$&'`+,;= %20.test",
1249 };
1250 let filename_encoded = filename
1251 .as_bytes()
1252 .iter()
1253 .fold(String::new(), |mut buf, c| {
1254 write!(&mut buf, "%{:02X}", c).unwrap();
1255 buf
1256 });
1257 std::fs::File::create(temp_dir.path().join(filename)).unwrap();
1258
1259 let srv = test::init_service(App::new().service(Files::new("/", temp_dir.path()))).await;
1260
1261 let req = TestRequest::get()
1262 .uri(&format!("/{}", filename_encoded))
1263 .to_request();
1264 let res = test::call_service(&srv, req).await;
1265 assert_eq!(res.status(), StatusCode::OK);
1266 }
1267
1268 #[actix_rt::test]
1269 async fn test_serve_named_file() {
1270 let factory = NamedFile::open("Cargo.toml").unwrap();
1271 let srv = test::init_service(App::new().service(factory)).await;
1272
1273 let req = TestRequest::get().uri("/Cargo.toml").to_request();
1274 let res = test::call_service(&srv, req).await;
1275 assert_eq!(res.status(), StatusCode::OK);
1276
1277 let bytes = test::read_body(res).await;
1278 let data = Bytes::from(fs::read("Cargo.toml").unwrap());
1279 assert_eq!(bytes, data);
1280
1281 let req = TestRequest::get().uri("/test/unknown").to_request();
1282 let res = test::call_service(&srv, req).await;
1283 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1284 }
1285
1286 #[actix_rt::test]
1287 async fn test_serve_named_file_prefix() {
1288 let factory = NamedFile::open("Cargo.toml").unwrap();
1289 let srv =
1290 test::init_service(App::new().service(web::scope("/test").service(factory))).await;
1291
1292 let req = TestRequest::get().uri("/test/Cargo.toml").to_request();
1293 let res = test::call_service(&srv, req).await;
1294 assert_eq!(res.status(), StatusCode::OK);
1295
1296 let bytes = test::read_body(res).await;
1297 let data = Bytes::from(fs::read("Cargo.toml").unwrap());
1298 assert_eq!(bytes, data);
1299
1300 let req = TestRequest::get().uri("/Cargo.toml").to_request();
1301 let res = test::call_service(&srv, req).await;
1302 assert_eq!(res.status(), StatusCode::NOT_FOUND);
1303 }
1304
1305 #[actix_rt::test]
1306 async fn test_named_file_default_service() {
1307 let factory = NamedFile::open("Cargo.toml").unwrap();
1308 let srv = test::init_service(App::new().default_service(factory)).await;
1309
1310 for route in ["/foobar", "/baz", "/"].iter() {
1311 let req = TestRequest::get().uri(route).to_request();
1312 let res = test::call_service(&srv, req).await;
1313 assert_eq!(res.status(), StatusCode::OK);
1314
1315 let bytes = test::read_body(res).await;
1316 let data = Bytes::from(fs::read("Cargo.toml").unwrap());
1317 assert_eq!(bytes, data);
1318 }
1319 }
1320
1321 #[actix_rt::test]
1322 async fn test_default_handler_named_file() {
1323 let factory = NamedFile::open("Cargo.toml").unwrap();
1324 let st = Files::new("/", ".")
1325 .default_handler(factory)
1326 .new_service(())
1327 .await
1328 .unwrap();
1329 let req = TestRequest::with_uri("/missing").to_srv_request();
1330 let resp = test::call_service(&st, req).await;
1331
1332 assert_eq!(resp.status(), StatusCode::OK);
1333 let bytes = test::read_body(resp).await;
1334 let data = Bytes::from(fs::read("Cargo.toml").unwrap());
1335 assert_eq!(bytes, data);
1336 }
1337
1338 #[actix_rt::test]
1339 async fn test_symlinks() {
1340 let srv = test::init_service(App::new().service(Files::new("test", "."))).await;
1341
1342 let req = TestRequest::get()
1343 .uri("/test/tests/symlink-test.png")
1344 .to_request();
1345 let res = test::call_service(&srv, req).await;
1346 assert_eq!(res.status(), StatusCode::OK);
1347 assert_eq!(
1348 res.headers().get(header::CONTENT_DISPOSITION).unwrap(),
1349 "inline; filename=\"symlink-test.png\""
1350 );
1351 }
1352
1353 #[actix_rt::test]
1354 async fn test_index_with_show_files_listing() {
1355 let service = Files::new(".", ".")
1356 .index_file("lib.rs")
1357 .show_files_listing()
1358 .new_service(())
1359 .await
1360 .unwrap();
1361
1362 let req = TestRequest::default().uri("/src").to_srv_request();
1364 let resp = test::call_service(&service, req).await;
1365 assert_eq!(resp.status(), StatusCode::OK);
1366 assert_eq!(
1367 resp.headers().get(header::CONTENT_TYPE).unwrap(),
1368 "text/x-rust"
1369 );
1370
1371 let req = TestRequest::default().uri("/tests").to_srv_request();
1373 let resp = test::call_service(&service, req).await;
1374 assert_eq!(
1375 resp.headers().get(header::CONTENT_TYPE).unwrap(),
1376 "text/html; charset=utf-8"
1377 );
1378 let bytes = test::read_body(resp).await;
1379 assert!(format!("{:?}", bytes).contains("/tests/test.png"));
1380 }
1381
1382 #[actix_rt::test]
1383 async fn test_path_filter() {
1384 let st = Files::new("/", ".")
1386 .path_filter(|path, _| path.components().count() == 1)
1387 .new_service(())
1388 .await
1389 .unwrap();
1390
1391 let req = TestRequest::with_uri("/Cargo.toml").to_srv_request();
1392 let resp = test::call_service(&st, req).await;
1393 assert_eq!(resp.status(), StatusCode::OK);
1394
1395 let req = TestRequest::with_uri("/src/lib.rs").to_srv_request();
1396 let resp = test::call_service(&st, req).await;
1397 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
1398 }
1399
1400 #[actix_rt::test]
1401 async fn test_default_handler_filter() {
1402 let st = Files::new("/", ".")
1403 .default_handler(|req: ServiceRequest| async {
1404 Ok(req.into_response(HttpResponse::Ok().body("default content")))
1405 })
1406 .path_filter(|path, _| path.extension() == Some("png".as_ref()))
1407 .new_service(())
1408 .await
1409 .unwrap();
1410 let req = TestRequest::with_uri("/Cargo.toml").to_srv_request();
1411 let resp = test::call_service(&st, req).await;
1412
1413 assert_eq!(resp.status(), StatusCode::OK);
1414 let bytes = test::read_body(resp).await;
1415 assert_eq!(bytes, web::Bytes::from_static(b"default content"));
1416 }
1417}