Skip to content

Commit 1866116

Browse files
committed
Import 'conduit-static' into workspace
2 parents ad5a509 + 83e6152 commit 1866116

File tree

5 files changed

+197
-0
lines changed

5 files changed

+197
-0
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ members = [
33
"conduit",
44
"conduit-middleware",
55
"conduit-router",
6+
"conduit-static",
67
"conduit-test",
78
"examples/*",
89
]

conduit-static/Cargo.toml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "conduit-static"
3+
version = "0.9.0-alpha.3"
4+
authors = ["wycats@gmail.com",
5+
"Alex Crichton <alex@alexcrichton.com>"]
6+
description = "Middleware for serving static files for conduit"
7+
repository = "https://github.com/conduit-rust/conduit-static"
8+
license = "MIT"
9+
10+
[dependencies]
11+
conduit = { version = "0.9.0-alpha.5", path = "../conduit" }
12+
conduit-mime-types = "0.7"
13+
time = { version = "0.2", default-features = false, features = ["std"] }
14+
filetime = "0.2"
15+
16+
[dev-dependencies]
17+
conduit-test = { version = "0.9.0-alpha.5", path = "../conduit-test" }
18+
tempdir = "0.3"

conduit-static/src/lib.rs

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#![allow(trivial_casts)]
2+
#![warn(rust_2018_idioms)]
3+
#![cfg_attr(test, deny(warnings))]
4+
5+
extern crate conduit;
6+
extern crate conduit_mime_types as mime;
7+
extern crate filetime;
8+
#[cfg(test)]
9+
extern crate tempdir;
10+
extern crate time;
11+
12+
use conduit::{box_error, header, Body, Handler, HandlerResult, RequestExt, Response, StatusCode};
13+
use filetime::FileTime;
14+
use std::fs::File;
15+
use std::path::{Path, PathBuf};
16+
use time::OffsetDateTime;
17+
18+
pub struct Static {
19+
path: PathBuf,
20+
types: mime::Types,
21+
}
22+
23+
impl Static {
24+
pub fn new<P: AsRef<Path>>(path: P) -> Static {
25+
Static {
26+
path: path.as_ref().to_path_buf(),
27+
types: mime::Types::new().expect("Couldn't load mime-types"),
28+
}
29+
}
30+
}
31+
32+
impl Handler for Static {
33+
#[allow(deprecated)]
34+
fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
35+
let request_path = &request.path()[1..];
36+
if request_path.contains("..") {
37+
return Ok(not_found());
38+
}
39+
40+
let path = self.path.join(request_path);
41+
let mime = self.types.mime_for_path(&path);
42+
let file = match File::open(&path) {
43+
Ok(f) => f,
44+
Err(..) => return Ok(not_found()),
45+
};
46+
let data = file.metadata().map_err(box_error)?;
47+
if data.is_dir() {
48+
return Ok(not_found());
49+
}
50+
let mtime = FileTime::from_last_modification_time(&data);
51+
let mtime = OffsetDateTime::from_unix_timestamp(mtime.unix_seconds() as i64);
52+
53+
Response::builder()
54+
.header(header::CONTENT_TYPE, mime)
55+
.header(header::CONTENT_LENGTH, data.len())
56+
.header(header::LAST_MODIFIED, mtime.format("%a, %d %b %Y %T GMT"))
57+
.body(Body::File(file))
58+
.map_err(box_error)
59+
}
60+
}
61+
62+
fn not_found() -> Response<Body> {
63+
Response::builder()
64+
.status(StatusCode::NOT_FOUND)
65+
.header(header::CONTENT_LENGTH, 0)
66+
.header(header::CONTENT_TYPE, "text/plain")
67+
.body(Body::empty())
68+
.unwrap()
69+
}
70+
71+
#[cfg(test)]
72+
mod tests {
73+
extern crate conduit_test;
74+
75+
use std::fs::{self, File};
76+
use std::io::prelude::*;
77+
use tempdir::TempDir;
78+
79+
use self::conduit_test::{MockRequest, ResponseExt};
80+
use conduit::{header, Handler, Method, StatusCode};
81+
use Static;
82+
83+
#[test]
84+
fn test_static() {
85+
let td = TempDir::new("conduit-static").unwrap();
86+
let root = td.path();
87+
let handler = Static::new(root);
88+
File::create(&root.join("Cargo.toml"))
89+
.unwrap()
90+
.write_all(b"[package]")
91+
.unwrap();
92+
let mut req = MockRequest::new(Method::GET, "/Cargo.toml");
93+
let res = handler.call(&mut req).expect("No response");
94+
assert_eq!(
95+
res.headers().get(header::CONTENT_TYPE).unwrap(),
96+
"text/plain"
97+
);
98+
assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "9");
99+
assert_eq!(*res.into_cow(), b"[package]"[..]);
100+
}
101+
102+
#[test]
103+
fn test_mime_types() {
104+
let td = TempDir::new("conduit-static").unwrap();
105+
let root = td.path();
106+
fs::create_dir(&root.join("src")).unwrap();
107+
File::create(&root.join("src/fixture.css")).unwrap();
108+
109+
let handler = Static::new(root);
110+
let mut req = MockRequest::new(Method::GET, "/src/fixture.css");
111+
let res = handler.call(&mut req).expect("No response");
112+
assert_eq!(res.headers().get(header::CONTENT_TYPE).unwrap(), "text/css");
113+
assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "0");
114+
}
115+
116+
#[test]
117+
fn test_missing() {
118+
let td = TempDir::new("conduit-static").unwrap();
119+
let root = td.path();
120+
121+
let handler = Static::new(root);
122+
let mut req = MockRequest::new(Method::GET, "/nope");
123+
let res = handler.call(&mut req).expect("No response");
124+
assert_eq!(res.status(), StatusCode::NOT_FOUND);
125+
}
126+
127+
#[test]
128+
fn test_dir() {
129+
let td = TempDir::new("conduit-static").unwrap();
130+
let root = td.path();
131+
132+
fs::create_dir(&root.join("foo")).unwrap();
133+
134+
let handler = Static::new(root);
135+
let mut req = MockRequest::new(Method::GET, "/foo");
136+
let res = handler.call(&mut req).expect("No response");
137+
assert_eq!(res.status(), StatusCode::NOT_FOUND);
138+
}
139+
140+
#[test]
141+
fn last_modified() {
142+
let td = TempDir::new("conduit-static").unwrap();
143+
let root = td.path();
144+
File::create(&root.join("test")).unwrap();
145+
let handler = Static::new(root);
146+
let mut req = MockRequest::new(Method::GET, "/test");
147+
let res = handler.call(&mut req).expect("No response");
148+
assert_eq!(res.status(), StatusCode::OK);
149+
assert!(res.headers().get(header::LAST_MODIFIED).is_some());
150+
}
151+
}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "static-file-server"
3+
version = "0.0.0"
4+
edition = "2018"
5+
6+
[dependencies]
7+
civet = "0.12.0-alpha.5"
8+
conduit = { version = "0.9.0-alpha.5", path = "../../conduit" }
9+
conduit-static = { version = "0.9.0-alpha.3", path = "../../conduit-static" }
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
extern crate civet;
2+
extern crate conduit;
3+
extern crate conduit_static;
4+
5+
use std::env;
6+
use std::sync::mpsc::channel;
7+
8+
use civet::{Config, Server};
9+
use conduit_static::Static;
10+
11+
fn main() {
12+
let handler = Static::new(&env::current_dir().unwrap());
13+
let mut cfg = Config::new();
14+
cfg.port(8888).threads(50);
15+
let _a = Server::start(cfg, handler);
16+
let (_tx, rx) = channel::<()>();
17+
rx.recv().unwrap();
18+
}

0 commit comments

Comments
 (0)