Skip to content

Commit 8956307

Browse files
committed
feat: add support for file serving in router
1 parent d55a9be commit 8956307

File tree

4 files changed

+74
-6
lines changed

4 files changed

+74
-6
lines changed

Cargo.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ edition = "2021"
77
anyhow = "1.0.97"
88
chrono = "0.4.40"
99
log = "0.4.26"
10+
mime_guess = "2.0.5"
1011
serde = { version = "1.0.218", features = ["derive"] }
1112
serde_json = "1.0.140"

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ Should never be used in production for obvious reasons 💀
1717
- [ ] MIME support 🎭
1818
- [x] support for file download (`HttpResponse.body` is now `Vec<u8>`)
1919
- [x] support for file upload (`HttpRequest.body` is now `Vec<u8>`)
20-
- [-] Body support
20+
- [ ] Body support
2121
- [x] Bytes body
2222
- [x] String body
23-
- [-] Multipart body
23+
- [ ] Multipart body
2424
- [x] Single part (useful for single file uploads)
2525
- [ ] Multi parts
2626
- [ ] HTTPS 🛡️
2727
- [ ] Improved routing 🚄 (W.I.P)
28+
- [x] static file serving (using `mime_guess` for setting proper mime type)
2829
- [ ] support for dynamic paths: `/foo/{:id}/bar`
2930

3031
## Usage example

src/router.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
use anyhow::{anyhow, Context, Result};
2-
use log::trace;
3-
use std::{collections::HashMap, str::FromStr};
4-
5-
use crate::http::{HttpMethod, HttpRequest, HttpResponse};
2+
use log::{trace, warn};
3+
use std::{
4+
collections::HashMap,
5+
fs,
6+
path::{Path, PathBuf},
7+
str::FromStr,
8+
};
9+
10+
use crate::http::{
11+
response_status_codes::HttpStatusCode, HttpMethod, HttpRequest, HttpResponse,
12+
HttpResponseBuilder,
13+
};
614

715
type RoutingCallback = fn(&HttpRequest) -> Result<HttpResponse>;
16+
type StaticFilesMount = (String, String);
817

918
#[derive(Debug)]
1019
pub struct Router {
1120
pub routes: HashMap<Route, RoutingCallback>,
21+
pub file_server: Option<StaticFilesMount>,
1222
}
1323

1424
impl Default for Router {
@@ -21,6 +31,7 @@ impl Router {
2131
pub fn new() -> Self {
2232
Router {
2333
routes: HashMap::new(),
34+
file_server: None,
2435
}
2536
}
2637

@@ -29,6 +40,33 @@ impl Router {
2940
let route = Route::from_str(&route_def)?;
3041
trace!("trying to match route: {route_def}");
3142

43+
if let Some(file_server) = &self.file_server {
44+
let (route_prefix, dir_path) = file_server;
45+
if let Some(filename) = route.path.strip_prefix(route_prefix) {
46+
let safe_filename = match Path::new(filename).file_name() {
47+
Some(filename) => Ok(filename.to_owned()),
48+
None => Err(anyhow!("not a file: {filename}")),
49+
};
50+
51+
if let Err(e) = safe_filename {
52+
warn!("failed to serve file '{filename}': {e}");
53+
return HttpResponseBuilder::new()
54+
.set_status(HttpStatusCode::BadRequest)
55+
.build();
56+
}
57+
58+
let safe_filename = safe_filename.unwrap();
59+
let filepath = PathBuf::from(dir_path).join(safe_filename);
60+
let mime_type = mime_guess::from_path(&filepath).first_or_octet_stream();
61+
let content = fs::read(filepath)?;
62+
63+
return HttpResponseBuilder::new()
64+
.set_raw_body(content)
65+
.set_content_type(mime_type.as_ref())
66+
.build();
67+
}
68+
}
69+
3270
let response = if let Some(route_callback) = self.routes.get(&route) {
3371
route_callback(request)
3472
} else {
@@ -71,6 +109,11 @@ impl Router {
71109
Ok(())
72110
}
73111

112+
pub fn setup_file_serving(mut self, route: &str, directory_path: &str) -> Result<Self> {
113+
self.file_server = Some((route.to_owned(), directory_path.to_owned()));
114+
Ok(self)
115+
}
116+
74117
pub fn get(mut self, path: &str, callback: RoutingCallback) -> Result<Self> {
75118
self.add_route(HttpMethod::GET, path, callback)?;
76119
Ok(self)

0 commit comments

Comments
 (0)