diff options
author | Vika <vika@fireburn.ru> | 2024-08-26 21:00:24 +0300 |
---|---|---|
committer | Vika <vika@fireburn.ru> | 2024-08-26 21:05:04 +0300 |
commit | 6d8e906f7c3d850120f94c7a784690442503aced (patch) | |
tree | 1d90d8998e5bd09a546b733bc6db7a24ce8230b2 | |
parent | 806f5fbfabd914d27ff3fb2e822e1c3869068859 (diff) | |
download | kittybox-6d8e906f7c3d850120f94c7a784690442503aced.tar.zst |
Explicitly allow caching IndieAuth client metadata
This might save a round-trip for clients that know how to cache things. Such as Kittybox's HTTP fetcher.
-rw-r--r-- | src/login.rs | 25 |
1 files changed, 23 insertions, 2 deletions
diff --git a/src/login.rs b/src/login.rs index e105bcc..646c832 100644 --- a/src/login.rs +++ b/src/login.rs @@ -1,11 +1,12 @@ -use std::borrow::Cow; +use std::{borrow::Cow, str::FromStr}; use futures_util::FutureExt; use axum::{extract::{FromRef, Host, Query, State}, http::HeaderValue, response::IntoResponse, Form}; -use axum_extra::extract::{cookie::{self, Cookie}, SignedCookieJar}; +use axum_extra::{extract::{cookie::{self, Cookie}, SignedCookieJar}, headers::HeaderMapExt, TypedHeader}; use hyper::{header::{CACHE_CONTROL, LOCATION}, StatusCode}; use kittybox_frontend_renderer::{Template, LoginPage, LogoutPage}; use kittybox_indieauth::{AuthorizationResponse, Error, GrantType, PKCEVerifier, Scope, Scopes}; +use sha2::Digest; use crate::database::Storage; @@ -321,7 +322,24 @@ async fn client_metadata<S: Storage + Send + Sync + 'static>( State(storage): State<S>, // XXX: blocked on https://github.com/hyperium/headers/pull/162 //TypedHeader(accept): TypedHeader<axum_extra::headers::Accept> + cached: Option<TypedHeader<axum_extra::headers::IfNoneMatch>>, ) -> axum::response::Response { + let etag = { + let mut digest = sha2::Sha256::new(); + digest.update(env!("CARGO_PKG_NAME").as_bytes()); + digest.update(b" "); + digest.update(env!("CARGO_PKG_VERSION").as_bytes()); + digest.update(b" "); + digest.update(crate::OAUTH2_SOFTWARE_ID.as_bytes()); + + let etag = String::from("W/") + &hex::encode(digest.finalize()); + axum_extra::headers::ETag::from_str(&etag).unwrap() + }; + if let Some(cached) = cached { + if cached.precondition_passes(&etag) { + return StatusCode::NOT_MODIFIED.into_response() + } + } let client_uri: url::Url = format!("https://{}/", host).parse().unwrap(); let client_id: url::Url = { let mut url = client_uri.clone(); @@ -345,6 +363,9 @@ async fn client_metadata<S: Storage + Send + Sync + 'static>( let mut response = metadata.into_response(); // Indicate to upstream caches this endpoint does different things depending on the Accept: header. response.headers_mut().append("Vary", HeaderValue::from_static("Accept")); + // Cache this metadata for an hour. + response.headers_mut().append("Cache-Control", HeaderValue::from_static("max-age=600")); + response.headers_mut().typed_insert(etag); response } |