๐พ Archived View for lists.flounder.online โบ patches โบ threads โบ 20210328161130.1161-4-johann@qwertqwโฆ captured on 2022-07-16 at 16:24:36. Gemini links have been rewritten to link to archived content
โฌ ๏ธ Previous capture (2022-04-28)
-=-=-=-=-=-=-
From: johann@qwertqwefsday.eu
Date: Sun, 28 Mar 2021 18:11:30 +0200
Message-Id: 20210328161130.1161-4-johann@qwertqwefsday.eu
To: <~aw/patches@lists.sr.ht>
In-Reply-To: 20210328161130.1161-1-johann@qwertqwefsday.eu
Cc: "Johann150" <johann@qwertqwefsday.eu>
--------------------------------------
From: Johann150 <johann@qwertqwefsday.eu>
---
src/errorpage.rs | 29 +++++++++----
src/main.rs | 106 ++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 112 insertions(+), 23 deletions(-)
diff --git a/src/errorpage.rs b/src/errorpage.rs
index a1436d3..f6995f6 100644
--- a/src/errorpage.rs
+++ b/src/errorpage.rs
@@ -15,24 +15,35 @@ pub struct ErrorToErrorpage;
impl<State: Clone + Send + Sync + 'static> Middleware<State> for ErrorToErrorpage {
async fn handle(&self, req: Request<State>, next: Next<'_, State>) -> tide::Result {
let resource = req.url().path().to_string();
+ let method = req.method();
let mut response = next.run(req).await;
if let Some(err) = response.take_error() {
let status = err.status();
- response = ErrorTemplate {
- resource,
- status,
- message: err.into_inner().to_string(),
+
+ if method == tide::http::Method::Head {
+ // the server MUST NOT send a message body in the response
+ // - RFC 7231 ยง 4.3.2
+ response.take_body();
+ } else {
+ response = ErrorTemplate {
+ resource,
+ status,
+ message: err.into_inner().to_string(),
+ }
+ .into();
+ response.set_status(status);
}
- .into();
+
if status == 405 {
// The origin server MUST generate an Allow header field in
// a 405 response containing a list of the target
- // resource's currently supported methods. - RFC 7231ยง6.5.5
+ // resource's currently supported methods.
+ // - RFC 7231 ยง 6.5.5
//
- // We only ever support GET requests.
- response.insert_header("Allow", "GET");
+ // We only ever support GET or HEAD requests.
+ // tide adds support for HEAD automatically if we implement GET
+ response.insert_header("Allow", "GET, HEAD");
}
- response.set_status(status);
}
Ok(response)
diff --git a/src/main.rs b/src/main.rs
index 2ba9f68..5bee9fb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,8 @@ use askama::Template;
use git2::{Commit, Diff, DiffDelta, Reference, Repository, Tree, TreeEntry};
use once_cell::sync::Lazy;
use serde::Deserialize;
-use std::fs;
+use std::fs::{self, File};
+use std::io::Read;
use std::path::Path;
use std::str;
use syntect::parsing::SyntaxSet;
@@ -468,10 +469,7 @@ async fn git_data(req: Request<()>) -> tide::Result {
} else if !path.is_file() {
// Either the requested resource does not exist or it is not
// a file, i.e. a directory.
- Err(tide::Error::from_str(
- 404,
- "The file you tried to access does not exist.",
- ))
+ Err(tide::Error::from_str(404, "This page does not exist."))
} else {
// ok - inside the repo directory
let mut resp = tide::Response::new(200);
@@ -488,6 +486,89 @@ async fn git_data(req: Request<()>) -> tide::Result {
}
}
+/// Serve a file from ./templates/static/
+async fn static_resource(req: Request<()>) -> tide::Result {
+ use tide::http::conditional::{IfModifiedSince, LastModified};
+
+ // only use a File handle here because we might not need to load the file
+ let file_mime_option = match req.url().path() {
+ "/style.css" => Some((
+ File::open("templates/static/style.css").unwrap(),
+ tide::http::mime::CSS,
+ )),
+ "/robots.txt" => Some((
+ File::open("templates/static/robots.txt").unwrap(),
+ tide::http::mime::PLAIN,
+ )),
+ _ => None,
+ };
+
+ match file_mime_option {
+ Some((mut file, mime)) => {
+ let metadata = file.metadata().unwrap();
+ let last_modified = metadata.modified().unwrap();
+
+ let header = IfModifiedSince::from_headers(&req).unwrap();
+
+ // check cache validating headers
+ if matches!(header, Some(date) if IfModifiedSince::new(last_modified) <= date) {
+ // the file has not changed
+ let mut response = Response::new(304);
+ response.set_content_type(mime);
+ LastModified::new(last_modified).apply(&mut response);
+
+ /*
+ A server MAY send a Content-Length header field in a 304
+ response to a conditional GET request; a server MUST NOT send
+ Content-Length in such a response unless its field-value equals
+ the decimal number of octets that would have been sent in the
+ payload body of a 200 response to the same request.
+ - RFC 7230 ยง 3.3.2
+ */
+ response.insert_header("Content-Length", metadata.len().to_string());
+
+ return Ok(response);
+ }
+
+ let mut response = Response::new(200);
+
+ match req.method() {
+ tide::http::Method::Head => {
+ /*
+ A server MAY send a Content-Length header field in a
+ response to a HEAD request; a server MUST NOT send
+ Content-Length in such a response unless its field-value
+ equals the decimal number of octets that would have been
+ sent in the payload body of a response if the same request
+ had used the GET method.
+ - RFC 7230 ยง 3.3.2
+ */
+ response.insert_header(
+ "Content-Length",
+ file.metadata().unwrap().len().to_string(),
+ );
+ }
+ tide::http::Method::Get => {
+ // load the file from disk
+ let mut content = String::new();
+ file.read_to_string(&mut content).unwrap();
+ response.set_body(content);
+ }
+ _ => return Err(tide::Error::from_str(405, "")),
+ }
+
+ response.set_content_type(mime);
+ LastModified::new(last_modified).apply(&mut response);
+ Ok(response)
+ }
+ None if req.method() == tide::http::Method::Get => {
+ Err(tide::Error::from_str(404, "This page does not exist."))
+ }
+ // issue a 405 error since this is used as the catchall
+ None => Err(tide::Error::from_str(405, "")),
+ }
+}
+
mod filters {
use super::*;
@@ -554,10 +635,7 @@ async fn main() -> Result<(), std::io::Error> {
let mut app = tide::new();
app.with(errorpage::ErrorToErrorpage);
app.at("/").get(index);
- app.at("/robots.txt")
- .serve_file("templates/static/robots.txt")?; // TODO configurable
- app.at("/style.css")
- .serve_file("templates/static/style.css")?; // TODO configurable
+
app.at("/:repo_name").get(repo_home);
app.at("/:repo_name/").get(repo_home);
@@ -566,17 +644,17 @@ async fn main() -> Result<(), std::io::Error> {
app.at("/:repo_name/HEAD").get(git_data);
app.at("/:repo_name/objects/*obj").get(git_data);
+ // web pages
app.at("/:repo_name/commit/:commit").get(repo_commit);
app.at("/:repo_name/refs").get(repo_refs);
app.at("/:repo_name/log").get(repo_log);
- app.at("/:repo_name/log/:ref").get(repo_log); // ref optional
+ app.at("/:repo_name/log/:ref").get(repo_log); // ref is optional
app.at("/:repo_name/tree").get(repo_tree);
- app.at("/:repo_name/tree/:ref").get(repo_tree);
+ app.at("/:repo_name/tree/:ref").get(repo_tree); // ref is optional
app.at("/:repo_name/tree/:ref/item/*object_name")
.get(repo_file);
- app.at("*")
- .get(|_| async { Result::<Response, tide::Error>::Err(tide::Error::from_str(404, "This page does not exist.")) })
- .all(|_| async { Result::<Response, tide::Error>::Err(tide::Error::from_str(405, "This method is not allowed.")) });
+
+ app.at("*").all(static_resource);
// Raw files, patch files
app.listen(format!("[::]:{}", CONFIG.port)).await?;
Ok(())
--
2.20.1
From: alex@alexwennerberg.com
Date: Sun, 28 Mar 2021 09:25:07 -0700
Message-Id: CA94H9C49HUO.30OIMANX8NJ0W@debian-alex
To: "Johann Galle" <johann@qwertqwefsday.eu>, <~aw/patches@lists.sr.ht>
In-Reply-To: 20210328161130.1161-4-johann@qwertqwefsday.eu
--------------------------------------
Interesting, is there no Tide library to serve a file that includes the 405
handling already?
On Sun Mar 28, 2021 at 9:11 AM PDT, Johann Galle wrote:
From: Johann150 <johann@qwertqwefsday.eu>
---
src/errorpage.rs | 29 +++++++++----
src/main.rs | 106 ++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 112 insertions(+), 23 deletions(-)
diff --git a/src/errorpage.rs b/src/errorpage.rs
index a1436d3..f6995f6 100644
--- a/src/errorpage.rs
+++ b/src/errorpage.rs
@@ -15,24 +15,35 @@ pub struct ErrorToErrorpage;
impl<State: Clone + Send + Sync + 'static> Middleware<State> for
ErrorToErrorpage {
async fn handle(&self, req: Request<State>, next: Next<'_, State>) ->
tide::Result {
let resource = req.url().path().to_string();
+ let method = req.method();
let mut response = next.run(req).await;
if let Some(err) = response.take_error() {
let status = err.status();
- response = ErrorTemplate {
- resource,
- status,
- message: err.into_inner().to_string(),
+
+ if method == tide::http::Method::Head {
+ // the server MUST NOT send a message body in the response
+ // - RFC 7231 ยง 4.3.2
+ response.take_body();
+ } else {
+ response = ErrorTemplate {
+ resource,
+ status,
+ message: err.into_inner().to_string(),
+ }
+ .into();
+ response.set_status(status);
}
- .into();
+
if status == 405 {
// The origin server MUST generate an Allow header field in
// a 405 response containing a list of the target
- // resource's currently supported methods. - RFC 7231ยง6.5.5
+ // resource's currently supported methods.
+ // - RFC 7231 ยง 6.5.5
//
- // We only ever support GET requests.
- response.insert_header("Allow", "GET");
+ // We only ever support GET or HEAD requests.
+ // tide adds support for HEAD automatically if we implement GET
+ response.insert_header("Allow", "GET, HEAD");
}
- response.set_status(status);
}
Ok(response)
diff --git a/src/main.rs b/src/main.rs
index 2ba9f68..5bee9fb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,8 @@ use askama::Template;
use git2::{Commit, Diff, DiffDelta, Reference, Repository, Tree,
TreeEntry};
use once_cell::sync::Lazy;
use serde::Deserialize;
-use std::fs;
+use std::fs::{self, File};
+use std::io::Read;
use std::path::Path;
use std::str;
use syntect::parsing::SyntaxSet;
@@ -468,10 +469,7 @@ async fn git_data(req: Request<()>) -> tide::Result
{
} else if !path.is_file() {
// Either the requested resource does not exist or it is not
// a file, i.e. a directory.
- Err(tide::Error::from_str(
- 404,
- "The file you tried to access does not exist.",
- ))
+ Err(tide::Error::from_str(404, "This page does not exist."))
} else {
// ok - inside the repo directory
let mut resp = tide::Response::new(200);
@@ -488,6 +486,89 @@ async fn git_data(req: Request<()>) -> tide::Result
{
}
}
+/// Serve a file from ./templates/static/
+async fn static_resource(req: Request<()>) -> tide::Result {
+ use tide::http::conditional::{IfModifiedSince, LastModified};
+
+ // only use a File handle here because we might not need to load the
file
+ let file_mime_option = match req.url().path() {
+ "/style.css" => Some((
+ File::open("templates/static/style.css").unwrap(),
+ tide::http::mime::CSS,
+ )),
+ "/robots.txt" => Some((
+ File::open("templates/static/robots.txt").unwrap(),
+ tide::http::mime::PLAIN,
+ )),
+ _ => None,
+ };
+
+ match file_mime_option {
+ Some((mut file, mime)) => {
+ let metadata = file.metadata().unwrap();
+ let last_modified = metadata.modified().unwrap();
+
+ let header = IfModifiedSince::from_headers(&req).unwrap();
+
+ // check cache validating headers
+ if matches!(header, Some(date) if IfModifiedSince::new(last_modified)
<= date) {
+ // the file has not changed
+ let mut response = Response::new(304);
+ response.set_content_type(mime);
+ LastModified::new(last_modified).apply(&mut response);
+
+ /*
+ A server MAY send a Content-Length header field in a 304
+ response to a conditional GET request; a server MUST NOT send
+ Content-Length in such a response unless its field-value equals
+ the decimal number of octets that would have been sent in the
+ payload body of a 200 response to the same request.
+ - RFC 7230 ยง 3.3.2
+ */
+ response.insert_header("Content-Length", metadata.len().to_string());
+
+ return Ok(response);
+ }
+
+ let mut response = Response::new(200);
+
+ match req.method() {
+ tide::http::Method::Head => {
+ /*
+ A server MAY send a Content-Length header field in a
+ response to a HEAD request; a server MUST NOT send
+ Content-Length in such a response unless its field-value
+ equals the decimal number of octets that would have been
+ sent in the payload body of a response if the same request
+ had used the GET method.
+ - RFC 7230 ยง 3.3.2
+ */
+ response.insert_header(
+ "Content-Length",
+ file.metadata().unwrap().len().to_string(),
+ );
+ }
+ tide::http::Method::Get => {
+ // load the file from disk
+ let mut content = String::new();
+ file.read_to_string(&mut content).unwrap();
+ response.set_body(content);
+ }
+ _ => return Err(tide::Error::from_str(405, "")),
+ }
+
+ response.set_content_type(mime);
+ LastModified::new(last_modified).apply(&mut response);
+ Ok(response)
+ }
+ None if req.method() == tide::http::Method::Get => {
+ Err(tide::Error::from_str(404, "This page does not exist."))
+ }
+ // issue a 405 error since this is used as the catchall
+ None => Err(tide::Error::from_str(405, "")),
+ }
+}
+
mod filters {
use super::*;
@@ -554,10 +635,7 @@ async fn main() -> Result<(), std::io::Error> {
let mut app = tide::new();
app.with(errorpage::ErrorToErrorpage);
app.at("/").get(index);
- app.at("/robots.txt")
- .serve_file("templates/static/robots.txt")?; // TODO configurable
- app.at("/style.css")
- .serve_file("templates/static/style.css")?; // TODO configurable
+
app.at("/:repo_name").get(repo_home);
app.at("/:repo_name/").get(repo_home);
@@ -566,17 +644,17 @@ async fn main() -> Result<(), std::io::Error> {
app.at("/:repo_name/HEAD").get(git_data);
app.at("/:repo_name/objects/*obj").get(git_data);
+ // web pages
app.at("/:repo_name/commit/:commit").get(repo_commit);
app.at("/:repo_name/refs").get(repo_refs);
app.at("/:repo_name/log").get(repo_log);
- app.at("/:repo_name/log/:ref").get(repo_log); // ref optional
+ app.at("/:repo_name/log/:ref").get(repo_log); // ref is optional
app.at("/:repo_name/tree").get(repo_tree);
- app.at("/:repo_name/tree/:ref").get(repo_tree);
+ app.at("/:repo_name/tree/:ref").get(repo_tree); // ref is optional
app.at("/:repo_name/tree/:ref/item/*object_name")
.get(repo_file);
- app.at("*")
- .get(|_| async { Result::<Response,
tide::Error>::Err(tide::Error::from_str(404, "This page does not
exist.")) })
- .all(|_| async { Result::<Response,
tide::Error>::Err(tide::Error::from_str(405, "This method is not
allowed.")) });
+
+ app.at("*").all(static_resource);
// Raw files, patch files
app.listen(format!("[::]:{}", CONFIG.port)).await?;
Ok(())
--
2.20.1
From: johann@qwertqwefsday.eu
Date: Sun, 28 Mar 2021 23:41:43 +0200
Message-Id: bd4608c7-6d8b-eab6-6694-438610ae644c@qwertqwefsday.eu
To: "alex wennerberg" <alex@alexwennerberg.com>, <~aw/patches@lists.sr.ht>
In-Reply-To: CA94H9C49HUO.30OIMANX8NJ0W@debian-alex
--------------------------------------
On 28.03.2021 18:25, alex wennerberg wrote:
Interesting, is there no Tide library to serve a file that includes the 405
handling already?
I did not find something like that. But I also almost added the `time` library before discovering the `IfModifiedSince`. Maybe you can find something?
From: johann@qwertqwefsday.eu
Date: Sun, 28 Mar 2021 23:45:29 +0200
Message-Id: 20210328214529.1369-3-johann@qwertqwefsday.eu
To: <~aw/patches@lists.sr.ht>
In-Reply-To: 20210328214529.1369-1-johann@qwertqwefsday.eu
Cc: "Johann150" <johann@qwertqwefsday.eu>
--------------------------------------
From: Johann150 <johann@qwertqwefsday.eu>
---
src/errorpage.rs | 29 +++++++++----
src/main.rs | 106 ++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 112 insertions(+), 23 deletions(-)
diff --git a/src/errorpage.rs b/src/errorpage.rs
index a1436d3..f6995f6 100644
--- a/src/errorpage.rs
+++ b/src/errorpage.rs
@@ -15,24 +15,35 @@ pub struct ErrorToErrorpage;
impl<State: Clone + Send + Sync + 'static> Middleware<State> for ErrorToErrorpage {
async fn handle(&self, req: Request<State>, next: Next<'_, State>) -> tide::Result {
let resource = req.url().path().to_string();
+ let method = req.method();
let mut response = next.run(req).await;
if let Some(err) = response.take_error() {
let status = err.status();
- response = ErrorTemplate {
- resource,
- status,
- message: err.into_inner().to_string(),
+
+ if method == tide::http::Method::Head {
+ // the server MUST NOT send a message body in the response
+ // - RFC 7231 ยง 4.3.2
+ response.take_body();
+ } else {
+ response = ErrorTemplate {
+ resource,
+ status,
+ message: err.into_inner().to_string(),
+ }
+ .into();
+ response.set_status(status);
}
- .into();
+
if status == 405 {
// The origin server MUST generate an Allow header field in
// a 405 response containing a list of the target
- // resource's currently supported methods. - RFC 7231ยง6.5.5
+ // resource's currently supported methods.
+ // - RFC 7231 ยง 6.5.5
//
- // We only ever support GET requests.
- response.insert_header("Allow", "GET");
+ // We only ever support GET or HEAD requests.
+ // tide adds support for HEAD automatically if we implement GET
+ response.insert_header("Allow", "GET, HEAD");
}
- response.set_status(status);
}
Ok(response)
diff --git a/src/main.rs b/src/main.rs
index 2ba9f68..5bee9fb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,8 @@ use askama::Template;
use git2::{Commit, Diff, DiffDelta, Reference, Repository, Tree, TreeEntry};
use once_cell::sync::Lazy;
use serde::Deserialize;
-use std::fs;
+use std::fs::{self, File};
+use std::io::Read;
use std::path::Path;
use std::str;
use syntect::parsing::SyntaxSet;
@@ -468,10 +469,7 @@ async fn git_data(req: Request<()>) -> tide::Result {
} else if !path.is_file() {
// Either the requested resource does not exist or it is not
// a file, i.e. a directory.
- Err(tide::Error::from_str(
- 404,
- "The file you tried to access does not exist.",
- ))
+ Err(tide::Error::from_str(404, "This page does not exist."))
} else {
// ok - inside the repo directory
let mut resp = tide::Response::new(200);
@@ -488,6 +486,89 @@ async fn git_data(req: Request<()>) -> tide::Result {
}
}
+/// Serve a file from ./templates/static/
+async fn static_resource(req: Request<()>) -> tide::Result {
+ use tide::http::conditional::{IfModifiedSince, LastModified};
+
+ // only use a File handle here because we might not need to load the file
+ let file_mime_option = match req.url().path() {
+ "/style.css" => Some((
+ File::open("templates/static/style.css").unwrap(),
+ tide::http::mime::CSS,
+ )),
+ "/robots.txt" => Some((
+ File::open("templates/static/robots.txt").unwrap(),
+ tide::http::mime::PLAIN,
+ )),
+ _ => None,
+ };
+
+ match file_mime_option {
+ Some((mut file, mime)) => {
+ let metadata = file.metadata().unwrap();
+ let last_modified = metadata.modified().unwrap();
+
+ let header = IfModifiedSince::from_headers(&req).unwrap();
+
+ // check cache validating headers
+ if matches!(header, Some(date) if IfModifiedSince::new(last_modified) <= date) {
+ // the file has not changed
+ let mut response = Response::new(304);
+ response.set_content_type(mime);
+ LastModified::new(last_modified).apply(&mut response);
+
+ /*
+ A server MAY send a Content-Length header field in a 304
+ response to a conditional GET request; a server MUST NOT send
+ Content-Length in such a response unless its field-value equals
+ the decimal number of octets that would have been sent in the
+ payload body of a 200 response to the same request.
+ - RFC 7230 ยง 3.3.2
+ */
+ response.insert_header("Content-Length", metadata.len().to_string());
+
+ return Ok(response);
+ }
+
+ let mut response = Response::new(200);
+
+ match req.method() {
+ tide::http::Method::Head => {
+ /*
+ A server MAY send a Content-Length header field in a
+ response to a HEAD request; a server MUST NOT send
+ Content-Length in such a response unless its field-value
+ equals the decimal number of octets that would have been
+ sent in the payload body of a response if the same request
+ had used the GET method.
+ - RFC 7230 ยง 3.3.2
+ */
+ response.insert_header(
+ "Content-Length",
+ file.metadata().unwrap().len().to_string(),
+ );
+ }
+ tide::http::Method::Get => {
+ // load the file from disk
+ let mut content = String::new();
+ file.read_to_string(&mut content).unwrap();
+ response.set_body(content);
+ }
+ _ => return Err(tide::Error::from_str(405, "")),
+ }
+
+ response.set_content_type(mime);
+ LastModified::new(last_modified).apply(&mut response);
+ Ok(response)
+ }
+ None if req.method() == tide::http::Method::Get => {
+ Err(tide::Error::from_str(404, "This page does not exist."))
+ }
+ // issue a 405 error since this is used as the catchall
+ None => Err(tide::Error::from_str(405, "")),
+ }
+}
+
mod filters {
use super::*;
@@ -554,10 +635,7 @@ async fn main() -> Result<(), std::io::Error> {
let mut app = tide::new();
app.with(errorpage::ErrorToErrorpage);
app.at("/").get(index);
- app.at("/robots.txt")
- .serve_file("templates/static/robots.txt")?; // TODO configurable
- app.at("/style.css")
- .serve_file("templates/static/style.css")?; // TODO configurable
+
app.at("/:repo_name").get(repo_home);
app.at("/:repo_name/").get(repo_home);
@@ -566,17 +644,17 @@ async fn main() -> Result<(), std::io::Error> {
app.at("/:repo_name/HEAD").get(git_data);
app.at("/:repo_name/objects/*obj").get(git_data);
+ // web pages
app.at("/:repo_name/commit/:commit").get(repo_commit);
app.at("/:repo_name/refs").get(repo_refs);
app.at("/:repo_name/log").get(repo_log);
- app.at("/:repo_name/log/:ref").get(repo_log); // ref optional
+ app.at("/:repo_name/log/:ref").get(repo_log); // ref is optional
app.at("/:repo_name/tree").get(repo_tree);
- app.at("/:repo_name/tree/:ref").get(repo_tree);
+ app.at("/:repo_name/tree/:ref").get(repo_tree); // ref is optional
app.at("/:repo_name/tree/:ref/item/*object_name")
.get(repo_file);
- app.at("*")
- .get(|_| async { Result::<Response, tide::Error>::Err(tide::Error::from_str(404, "This page does not exist.")) })
- .all(|_| async { Result::<Response, tide::Error>::Err(tide::Error::from_str(405, "This method is not allowed.")) });
+
+ app.at("*").all(static_resource);
// Raw files, patch files
app.listen(format!("[::]:{}", CONFIG.port)).await?;
Ok(())
--
2.20.1
From: alex@alexwennerberg.com
Date: Sun, 28 Mar 2021 17:11:50 -0700
Message-Id: CA9EELZY245H.NLU03T35QXW1@debian-alex
To: "Johann Galle" <johann@qwertqwefsday.eu>, <~aw/patches@lists.sr.ht>
In-Reply-To: bd4608c7-6d8b-eab6-6694-438610ae644c@qwertqwefsday.eu
--------------------------------------
I'll take a look. I've been doing much less open source work lately,
limiting it to about an hour a day after work, so it may take me a bit
Alex
On Sun Mar 28, 2021 at 2:41 PM PDT, Johann Galle wrote:
On 28.03.2021 18:25, alex wennerberg wrote:
> Interesting, is there no Tide library to serve a file that includes the
405
> handling already?
I did not find something like that. But I also almost added the `time`
library before discovering the `IfModifiedSince`. Maybe you can find
something?