Fix conflicts with main branch
This commit is contained in:
commit
9aa90c3e59
|
@ -17,8 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Docments can be converted into responses with std::convert::Into [@Alch-Emi]
|
- Docments can be converted into responses with std::convert::Into [@Alch-Emi]
|
||||||
### Improved
|
### Improved
|
||||||
- build time and size by [@Alch-Emi]
|
- build time and size by [@Alch-Emi]
|
||||||
### Changed
|
- Improved error handling in serve_dir [@Alch-Emi]
|
||||||
- `Response::success` now takes a request body [@Alch-Emi]
|
|
||||||
|
|
||||||
## [0.3.0] - 2020-11-14
|
## [0.3.0] - 2020-11-14
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -30,3 +30,7 @@ mime_guess = { version = "2.0.3", optional = true }
|
||||||
env_logger = "0.8.1"
|
env_logger = "0.8.1"
|
||||||
futures-util = "0.3.7"
|
futures-util = "0.3.7"
|
||||||
tokio = { version = "0.3.1", features = ["macros", "rt-multi-thread", "sync"] }
|
tokio = { version = "0.3.1", features = ["macros", "rt-multi-thread", "sync"] }
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "serve_dir"
|
||||||
|
required-features = ["serve_dir"]
|
||||||
|
|
21
src/lib.rs
21
src/lib.rs
|
@ -17,6 +17,7 @@ use tokio::{
|
||||||
};
|
};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use rustls::ClientCertVerifier;
|
use rustls::ClientCertVerifier;
|
||||||
|
use rustls::internal::msgs::handshake::DigitallySignedStruct;
|
||||||
use tokio_rustls::{rustls, TlsAcceptor};
|
use tokio_rustls::{rustls, TlsAcceptor};
|
||||||
use rustls::*;
|
use rustls::*;
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
|
@ -434,6 +435,8 @@ impl ClientCertVerifier for AllowAnonOrSelfsignedClient {
|
||||||
Some(false)
|
Some(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the below methods are a hack until webpki doesn't break with certain certs
|
||||||
|
|
||||||
fn verify_client_cert(
|
fn verify_client_cert(
|
||||||
&self,
|
&self,
|
||||||
_: &[Certificate],
|
_: &[Certificate],
|
||||||
|
@ -441,6 +444,24 @@ impl ClientCertVerifier for AllowAnonOrSelfsignedClient {
|
||||||
) -> Result<ClientCertVerified, TLSError> {
|
) -> Result<ClientCertVerified, TLSError> {
|
||||||
Ok(ClientCertVerified::assertion())
|
Ok(ClientCertVerified::assertion())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_tls12_signature(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_cert: &Certificate,
|
||||||
|
_dss: &DigitallySignedStruct,
|
||||||
|
) -> Result<HandshakeSignatureValid, TLSError> {
|
||||||
|
Ok(HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls13_signature(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_cert: &Certificate,
|
||||||
|
_dss: &DigitallySignedStruct,
|
||||||
|
) -> Result<HandshakeSignatureValid, TLSError> {
|
||||||
|
Ok(HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -147,8 +147,9 @@ impl Document {
|
||||||
///
|
///
|
||||||
/// assert_eq!(document.to_string(), "hello\n * world!\n");
|
/// assert_eq!(document.to_string(), "hello\n * world!\n");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_text(&mut self, text: &str) -> &mut Self {
|
pub fn add_text(&mut self, text: impl AsRef<str>) -> &mut Self {
|
||||||
let text = text
|
let text = text
|
||||||
|
.as_ref()
|
||||||
.lines()
|
.lines()
|
||||||
.map(Text::new_lossy)
|
.map(Text::new_lossy)
|
||||||
.map(Item::Text);
|
.map(Item::Text);
|
||||||
|
@ -236,8 +237,8 @@ impl Document {
|
||||||
///
|
///
|
||||||
/// assert_eq!(document.to_string(), "```\na\n b\n c\n```\n");
|
/// assert_eq!(document.to_string(), "```\na\n b\n c\n```\n");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_preformatted(&mut self, preformatted_text: &str) -> &mut Self {
|
pub fn add_preformatted(&mut self, preformatted_text: impl AsRef<str>) -> &mut Self {
|
||||||
self.add_preformatted_with_alt("", preformatted_text)
|
self.add_preformatted_with_alt("", preformatted_text.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a block of preformatted text with an alt text.
|
/// Adds a block of preformatted text with an alt text.
|
||||||
|
@ -257,9 +258,10 @@ impl Document {
|
||||||
///
|
///
|
||||||
/// assert_eq!(document.to_string(), "```rust\nfn main() {\n}\n```\n");
|
/// assert_eq!(document.to_string(), "```rust\nfn main() {\n}\n```\n");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_preformatted_with_alt(&mut self, alt: &str, preformatted_text: &str) -> &mut Self {
|
pub fn add_preformatted_with_alt(&mut self, alt: impl AsRef<str>, preformatted_text: impl AsRef<str>) -> &mut Self {
|
||||||
let alt = AltText::new_lossy(alt);
|
let alt = AltText::new_lossy(alt.as_ref());
|
||||||
let lines = preformatted_text
|
let lines = preformatted_text
|
||||||
|
.as_ref()
|
||||||
.lines()
|
.lines()
|
||||||
.map(PreformattedText::new_lossy)
|
.map(PreformattedText::new_lossy)
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -318,8 +320,8 @@ impl Document {
|
||||||
///
|
///
|
||||||
/// assert_eq!(document.to_string(), "* milk\n* eggs\n");
|
/// assert_eq!(document.to_string(), "* milk\n* eggs\n");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_unordered_list_item(&mut self, text: &str) -> &mut Self {
|
pub fn add_unordered_list_item(&mut self, text: impl AsRef<str>) -> &mut Self {
|
||||||
let item = UnorderedListItem::new_lossy(text);
|
let item = UnorderedListItem::new_lossy(text.as_ref());
|
||||||
let item = Item::UnorderedListItem(item);
|
let item = Item::UnorderedListItem(item);
|
||||||
|
|
||||||
self.add_item(item);
|
self.add_item(item);
|
||||||
|
@ -340,8 +342,9 @@ impl Document {
|
||||||
///
|
///
|
||||||
/// assert_eq!(document.to_string(), "> I think,\n> therefore I am\n");
|
/// assert_eq!(document.to_string(), "> I think,\n> therefore I am\n");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn add_quote(&mut self, text: &str) -> &mut Self {
|
pub fn add_quote(&mut self, text: impl AsRef<str>) -> &mut Self {
|
||||||
let quote = text
|
let quote = text
|
||||||
|
.as_ref()
|
||||||
.lines()
|
.lines()
|
||||||
.map(Quote::new_lossy)
|
.map(Quote::new_lossy)
|
||||||
.map(Item::Quote);
|
.map(Item::Quote);
|
||||||
|
|
|
@ -60,7 +60,8 @@ impl Request {
|
||||||
self.certificate = cert;
|
self.certificate = cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn certificate(&self) -> Option<&Certificate> {
|
#[allow(clippy::missing_const_for_fn)]
|
||||||
|
pub fn certificate(&self) -> Option<&Certificate> {
|
||||||
self.certificate.as_ref()
|
self.certificate.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@ impl Status {
|
||||||
self.category().is_success()
|
self.category().is_success()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn category(&self) -> StatusCategory {
|
#[allow(clippy::missing_const_for_fn)]
|
||||||
|
pub fn category(&self) -> StatusCategory {
|
||||||
let class = self.0 / 10;
|
let class = self.0 / 10;
|
||||||
|
|
||||||
match class {
|
match class {
|
||||||
|
|
67
src/util.rs
67
src/util.rs
|
@ -23,8 +23,11 @@ pub async fn serve_file<P: AsRef<Path>>(path: P, mime: &Mime) -> Result<Response
|
||||||
let file = match File::open(path).await {
|
let file = match File::open(path).await {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => match err.kind() {
|
Err(err) => match err.kind() {
|
||||||
io::ErrorKind::NotFound => return Ok(Response::not_found()),
|
std::io::ErrorKind::PermissionDenied => {
|
||||||
_ => return Err(err.into()),
|
warn!("Asked to serve {}, but permission denied by OS", path.display());
|
||||||
|
return Ok(Response::not_found());
|
||||||
|
},
|
||||||
|
_ => return warn_unexpected(err, path, line!()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,16 +37,44 @@ pub async fn serve_file<P: AsRef<Path>>(path: P, mime: &Mime) -> Result<Response
|
||||||
#[cfg(feature="serve_dir")]
|
#[cfg(feature="serve_dir")]
|
||||||
pub async fn serve_dir<D: AsRef<Path>, P: AsRef<Path>>(dir: D, virtual_path: &[P]) -> Result<Response> {
|
pub async fn serve_dir<D: AsRef<Path>, P: AsRef<Path>>(dir: D, virtual_path: &[P]) -> Result<Response> {
|
||||||
debug!("Dir: {}", dir.as_ref().display());
|
debug!("Dir: {}", dir.as_ref().display());
|
||||||
let dir = dir.as_ref().canonicalize()
|
let dir = dir.as_ref();
|
||||||
.context("Failed to canonicalize directory")?;
|
let dir = match dir.canonicalize() {
|
||||||
|
Ok(dir) => dir,
|
||||||
|
Err(e) => {
|
||||||
|
match e.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
warn!("Path {} not found. Check your configuration.", dir.display());
|
||||||
|
return Response::server_error("Server incorrectly configured")
|
||||||
|
},
|
||||||
|
std::io::ErrorKind::PermissionDenied => {
|
||||||
|
warn!("Permission denied for {}. Check that the server has access.", dir.display());
|
||||||
|
return Response::server_error("Server incorrectly configured")
|
||||||
|
},
|
||||||
|
_ => return warn_unexpected(e, dir, line!()),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
let mut path = dir.to_path_buf();
|
let mut path = dir.to_path_buf();
|
||||||
|
|
||||||
for segment in virtual_path {
|
for segment in virtual_path {
|
||||||
path.push(segment);
|
path.push(segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = path.canonicalize()
|
let path = match path.canonicalize() {
|
||||||
.context("Failed to canonicalize path")?;
|
Ok(dir) => dir,
|
||||||
|
Err(e) => {
|
||||||
|
match e.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => return Ok(Response::not_found()),
|
||||||
|
std::io::ErrorKind::PermissionDenied => {
|
||||||
|
// Runs when asked to serve a file in a restricted dir
|
||||||
|
// i.e. not /noaccess, but /noaccess/file
|
||||||
|
warn!("Asked to serve {}, but permission denied by OS", path.display());
|
||||||
|
return Ok(Response::not_found());
|
||||||
|
},
|
||||||
|
_ => return warn_unexpected(e, path.as_ref(), line!()),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
if !path.starts_with(&dir) {
|
if !path.starts_with(&dir) {
|
||||||
return Ok(Response::not_found());
|
return Ok(Response::not_found());
|
||||||
|
@ -59,11 +90,15 @@ pub async fn serve_dir<D: AsRef<Path>, P: AsRef<Path>>(dir: D, virtual_path: &[P
|
||||||
|
|
||||||
#[cfg(feature="serve_dir")]
|
#[cfg(feature="serve_dir")]
|
||||||
async fn serve_dir_listing<P: AsRef<Path>, B: AsRef<Path>>(path: P, virtual_path: &[B]) -> Result<Response> {
|
async fn serve_dir_listing<P: AsRef<Path>, B: AsRef<Path>>(path: P, virtual_path: &[B]) -> Result<Response> {
|
||||||
let mut dir = match fs::read_dir(path).await {
|
let mut dir = match fs::read_dir(path.as_ref()).await {
|
||||||
Ok(dir) => dir,
|
Ok(dir) => dir,
|
||||||
Err(err) => match err.kind() {
|
Err(err) => match err.kind() {
|
||||||
io::ErrorKind::NotFound => return Ok(Response::not_found()),
|
io::ErrorKind::NotFound => return Ok(Response::not_found()),
|
||||||
_ => return Err(err.into()),
|
std::io::ErrorKind::PermissionDenied => {
|
||||||
|
warn!("Asked to serve {}, but permission denied by OS", path.as_ref().display());
|
||||||
|
return Ok(Response::not_found());
|
||||||
|
},
|
||||||
|
_ => return warn_unexpected(err, path.as_ref(), line!()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,6 +147,22 @@ pub fn guess_mime_from_path<P: AsRef<Path>>(path: P) -> Mime {
|
||||||
mime_guess::from_ext(extension).first_or_octet_stream()
|
mime_guess::from_ext(extension).first_or_octet_stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="serve_dir")]
|
||||||
|
/// Print a warning to the log asking to file an issue and respond with "Unexpected Error"
|
||||||
|
pub (crate) fn warn_unexpected(err: impl std::fmt::Debug, path: &Path, line: u32) -> Result<Response> {
|
||||||
|
warn!(
|
||||||
|
concat!(
|
||||||
|
"Unexpected error serving path {} at util.rs:{}, please report to ",
|
||||||
|
env!("CARGO_PKG_REPOSITORY"),
|
||||||
|
"/issues: {:?}",
|
||||||
|
),
|
||||||
|
path.display(),
|
||||||
|
line,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
Response::server_error("Unexpected error")
|
||||||
|
}
|
||||||
|
|
||||||
/// A convenience trait alias for `AsRef<T> + Into<T::Owned>`,
|
/// A convenience trait alias for `AsRef<T> + Into<T::Owned>`,
|
||||||
/// most commonly used to accept `&str` or `String`:
|
/// most commonly used to accept `&str` or `String`:
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in New Issue