Skip to content

Commit e190be6

Browse files
Merge pull request #4 from hubertshelley/develop
public 0.3.0
2 parents b15c3b1 + 6d20749 commit e190be6

File tree

8 files changed

+196
-5
lines changed

8 files changed

+196
-5
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
/Cargo.lock
33
.idea/
4+
static/

examples/file_server/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "file_server"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
silent = { path = "../../silent" }
10+
async-trait = "0.1.68"
11+
tokio = { version = "1", features = ["full"] }

examples/file_server/src/main.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use silent::prelude::*;
2+
use std::sync::Arc;
3+
4+
fn main() {
5+
logger::fmt().init();
6+
if !std::path::Path::new("static").is_dir() {
7+
std::fs::create_dir("static").unwrap();
8+
std::fs::write(
9+
"./static/index.html",
10+
r#"<!DOCTYPE html>
11+
<html>
12+
<head>
13+
<meta charset="utf-8">
14+
<title>Silent</title>
15+
</head>
16+
<body>
17+
18+
<h1>我的第一个标题</h1>
19+
20+
<p>我的第一个段落。</p>
21+
22+
</body>
23+
</html>"#,
24+
)
25+
.unwrap();
26+
}
27+
let mut route = Route::new("<path:**>");
28+
route
29+
.get_handler_mut()
30+
.insert(Method::GET, Arc::new(static_handler("static")));
31+
Server::new().bind_route(route).run();
32+
}

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Silent 是一个简单的基于Hyper的Web框架,它的目标是提供一个
88

99
- [x] 路由
1010
- [ ] 中间件
11-
- [ ] 静态文件
11+
- [x] 静态文件
1212
- [ ] 模板
1313
- [ ] 数据库
1414
- [x] 日志 (使用了tracing)

silent/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ keywords = ["web", "web-framework"]
1212
license = "Apache-2.0"
1313
readme = "../readme.md"
1414
repository = "https://github.com/hubertshelley/silent"
15-
version = "0.2.1"
15+
version = "0.3.0"
1616
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1717

1818
[dependencies]
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use crate::prelude::PathParam;
2+
use crate::{Handler, Request, Response, SilentError, StatusCode};
3+
use async_trait::async_trait;
4+
5+
struct HandlerWrapperStatic {
6+
path: String,
7+
}
8+
9+
impl Default for HandlerWrapperStatic {
10+
fn default() -> Self {
11+
Self::new(".")
12+
}
13+
}
14+
15+
impl HandlerWrapperStatic {
16+
fn new(path: &str) -> Self {
17+
let mut path = path;
18+
if path.ends_with('/') {
19+
path = &path[..path.len() - 1];
20+
}
21+
if !std::path::Path::new(path).is_dir() {
22+
panic!("Path not exists: {}", path);
23+
}
24+
Self {
25+
path: path.to_string(),
26+
}
27+
}
28+
}
29+
30+
#[async_trait]
31+
impl Handler for HandlerWrapperStatic {
32+
async fn call(&self, req: Request) -> Result<Response, SilentError> {
33+
if let PathParam::Path(file_path) = req.get_path_params("path").unwrap() {
34+
let mut path = format!("{}/{}", self.path, file_path);
35+
if path.ends_with('/') {
36+
path.push_str("index.html");
37+
}
38+
if let Ok(contents) = tokio::fs::read(path).await {
39+
return Ok(contents.into());
40+
}
41+
};
42+
Err(SilentError::BusinessError {
43+
code: StatusCode::NOT_FOUND,
44+
msg: "Not Found".to_string(),
45+
})
46+
}
47+
}
48+
49+
pub fn static_handler(path: &str) -> impl Handler {
50+
HandlerWrapperStatic::new(path)
51+
}
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use super::HandlerWrapperStatic;
56+
use crate::prelude::*;
57+
use crate::Handler;
58+
use crate::Request;
59+
use crate::SilentError;
60+
use crate::StatusCode;
61+
use bytes::Bytes;
62+
use http_body_util::BodyExt;
63+
64+
static CONTENT: &str = r#"<!DOCTYPE html>
65+
<html>
66+
<head>
67+
<meta charset="utf-8">
68+
<title>Silent</title>
69+
</head>
70+
<body>
71+
72+
<h1>我的第一个标题</h1>
73+
74+
<p>我的第一个段落。</p>
75+
76+
</body>
77+
</html>"#;
78+
79+
fn create_static(path: &str) {
80+
if !std::path::Path::new(path).is_dir() {
81+
std::fs::create_dir(path).unwrap();
82+
std::fs::write(format!("./{}/index.html", path), CONTENT).unwrap();
83+
}
84+
}
85+
86+
fn clean_static(path: &str) {
87+
if std::path::Path::new(path).is_dir() {
88+
std::fs::remove_file(format!("./{}/index.html", path)).unwrap();
89+
std::fs::remove_dir(path).unwrap();
90+
}
91+
}
92+
93+
#[tokio::test]
94+
async fn test_static() {
95+
let path = "test_static";
96+
create_static(path);
97+
let handler = HandlerWrapperStatic::new(path);
98+
let mut req = Request::default();
99+
req.set_path_params("path".to_owned(), PathParam::Path("index.html".to_string()));
100+
let mut res = handler.call(req).await.unwrap();
101+
clean_static(path);
102+
assert_eq!(res.status(), StatusCode::OK);
103+
assert_eq!(
104+
res.frame().await.unwrap().unwrap().data_ref().unwrap(),
105+
&Bytes::from(CONTENT)
106+
);
107+
}
108+
109+
#[tokio::test]
110+
async fn test_static_default() {
111+
let path = "test_static_default";
112+
create_static(path);
113+
let handler = HandlerWrapperStatic::new(path);
114+
let mut req = Request::default();
115+
req.set_path_params("path".to_owned(), PathParam::Path("".to_string()));
116+
let mut res = handler.call(req).await.unwrap();
117+
clean_static(path);
118+
assert_eq!(res.status(), StatusCode::OK);
119+
assert_eq!(
120+
res.frame().await.unwrap().unwrap().data_ref().unwrap(),
121+
&Bytes::from(CONTENT)
122+
);
123+
}
124+
125+
#[tokio::test]
126+
async fn test_static_not_found() {
127+
let path = "test_static_not_found";
128+
create_static(path);
129+
let handler = HandlerWrapperStatic::new(path);
130+
let mut req = Request::default();
131+
req.set_path_params(
132+
"path".to_owned(),
133+
PathParam::Path("not_found.html".to_string()),
134+
);
135+
let res = handler.call(req).await.unwrap_err();
136+
clean_static(path);
137+
if let SilentError::BusinessError { code, .. } = res {
138+
assert_eq!(code, StatusCode::NOT_FOUND);
139+
} else {
140+
panic!();
141+
}
142+
}
143+
}

silent/src/handler/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
mod handler_trait;
33
mod handler_wrapper;
44
mod handler_wrapper_html;
5+
mod handler_wrapper_static;
56

67
pub use handler_trait::Handler;
78
pub(crate) use handler_wrapper::HandlerWrapper;
89
pub(crate) use handler_wrapper_html::HandlerWrapperHtml;
10+
pub use handler_wrapper_static::static_handler;

silent/src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ pub(crate) use handler::HandlerWrapper;
1616
pub use hyper::{header, Method, StatusCode};
1717

1818
pub mod prelude {
19-
pub use crate::core::{path_param::PathParam, request::Request, response::Response};
19+
pub use crate::core::{
20+
path_param::PathParam, request::Request, res_body::full, response::Response,
21+
};
2022
pub use crate::error::SilentError;
21-
pub use crate::handler::Handler;
23+
pub use crate::handler::{static_handler, Handler};
2224
pub use crate::log::{logger, Level};
23-
pub use crate::route::handler_append::{HandlerAppend, HtmlHandlerAppend};
25+
pub use crate::route::handler_append::{HandlerAppend, HandlerGetter, HtmlHandlerAppend};
2426
pub use crate::route::Route;
2527
pub use crate::service::Server;
2628
pub use hyper::{header, Method, StatusCode};

0 commit comments

Comments
 (0)