3 use actix_web::{get, route, web, App, HttpRequest, HttpServer, Responder, Result};
4 use actix_web::http::header;
5 use actix_web::middleware::Logger;
6 use actix_files::{Files, NamedFile};
8 #[route("/hello/{name}", method="GET", method="HEAD")]
9 async fn hello(params: web::Path<String>, req: HttpRequest) -> Result<String> {
10 let name = params.into_inner();
12 match req.headers().get(header::USER_AGENT) {
13 Some(agent) => Ok(format!("Hello {} from {}!", name, agent.to_str().unwrap())),
14 None => Ok(format!("Hello {}!", name))
18 #[get("/file/{path:.*}")]
19 async fn static_file(params: web::Path<String>, req: HttpRequest) -> Result<impl Responder> {
20 let request_path = params.into_inner();
21 let disk_path = "../static/".to_string() + &request_path;
23 // if the client accepts gzip encoding, try that first
24 if let Some(accept_encoding) = req.headers().get(header::ACCEPT_ENCODING) {
25 if accept_encoding.to_str().unwrap().contains("gzip") {
26 let path_gz = disk_path.clone() + ".gz";
27 if Path::new(&path_gz).is_file() {
28 log::debug!("client accepts gzip encoding, sending pre-compressed file {}", &path_gz);
29 return Ok(NamedFile::open_async(path_gz).await?
31 .insert_header(header::ContentEncoding::Gzip));
37 Ok(NamedFile::open_async(disk_path).await?.customize())
41 async fn main() -> std::io::Result<()> {
42 env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
48 .service(Files::new("/dir", "../static"))
49 .wrap(Logger::default())
51 .bind(("127.0.0.1", 3030))?
58 use actix_web::{App, body, test, web};
59 use actix_web::http::{header, StatusCode};
61 use super::{hello, static_file};
64 async fn test_hello() {
65 // FIXME: duplicating the .service() call from main() here is super ugly, but it's hard to move that into a fn
66 let app = test::init_service(App::new().service(hello)).await;
69 let req = test::TestRequest::get().uri("/hello/rust").to_request();
70 let res = test::call_service(&app, req).await;
71 assert!(res.status().is_success());
72 assert_eq!(body::to_bytes(res.into_body()).await.unwrap(),
73 web::Bytes::from_static(b"Hello rust!"));
76 let req = test::TestRequest::get()
78 .insert_header((header::USER_AGENT, "TestBrowser 0.1"))
80 let res = test::call_service(&app, req).await;
81 assert!(res.status().is_success());
82 assert_eq!(body::to_bytes(res.into_body()).await.unwrap(),
83 web::Bytes::from_static(b"Hello rust from TestBrowser 0.1!"));
87 async fn test_static_dir() {
88 // FIXME: duplicating the .service() call from main() here is super ugly, but it's hard to move that into a fn
89 let app = test::init_service(App::new().service(actix_files::Files::new("/dir", "../static"))).await;
91 let req = test::TestRequest::get().uri("/dir/plain.txt").to_request();
92 let res = test::call_service(&app, req).await;
93 assert!(res.status().is_success());
94 assert_eq!(body::to_bytes(res.into_body()).await.unwrap(),
95 web::Bytes::from_static(b"Hello world! This is uncompressed text.\n"));
98 let req = test::TestRequest::get().uri("/dir/dir1/optzip.txt").to_request();
99 let res = test::call_service(&app, req).await;
100 assert!(res.status().is_success());
101 assert_eq!(body::to_bytes(res.into_body()).await.unwrap(),
102 web::Bytes::from_static(b"This file is available uncompressed or compressed\n\
103 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"));
105 // does not support transparent decompression
106 let req = test::TestRequest::get().uri("/dir/onlycompressed.txt").to_request();
107 let res = test::call_service(&app, req).await;
108 assert_eq!(res.status(), StatusCode::NOT_FOUND);
112 async fn test_static_file() {
113 // FIXME: duplicating the .service() call from main() here is super ugly, but it's hard to move that into a fn
114 let app = test::init_service(App::new().service(static_file)).await;
117 let req = test::TestRequest::get().uri("/file/dir1/optzip.txt").to_request();
118 let res = test::call_service(&app, req).await;
119 assert!(res.status().is_success());
120 assert_eq!(body::to_bytes(res.into_body()).await.unwrap(),
121 web::Bytes::from_static(b"This file is available uncompressed or compressed\n\
122 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"));
125 let req = test::TestRequest::get()
126 .uri("/file/dir1/optzip.txt")
127 .insert_header((header::ACCEPT_ENCODING, "deflate, gzip"))
129 let res = test::call_service(&app, req).await;
130 assert!(res.status().is_success());
131 let res_bytes = body::to_bytes(res.into_body()).await.unwrap();
132 assert_eq!(res_bytes.len(), 63); // file size of ../static/dir1/optzip.txt.gz
133 assert_eq!(res_bytes[0], 31);