forked from mirrors/relay
Remove media caching, just proxy
This commit is contained in:
parent
094331a447
commit
e9303ad9f6
8 changed files with 19 additions and 209 deletions
|
@ -1,14 +1,7 @@
|
||||||
use crate::{
|
use crate::{db::Db, error::Error};
|
||||||
db::{Db, MediaMeta},
|
|
||||||
error::Error,
|
|
||||||
};
|
|
||||||
use activitystreams::iri_string::types::IriString;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::web::Bytes;
|
|
||||||
use std::time::{Duration, SystemTime};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
static MEDIA_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 2);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MediaCache {
|
pub struct MediaCache {
|
||||||
db: Db,
|
db: Db,
|
||||||
|
@ -29,32 +22,6 @@ impl MediaCache {
|
||||||
self.db.media_url(uuid).await
|
self.db.media_url(uuid).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", name = "Is media outdated", skip(self))]
|
|
||||||
pub(crate) async fn is_outdated(&self, uuid: Uuid) -> Result<bool, Error> {
|
|
||||||
if let Some(meta) = self.db.media_meta(uuid).await? {
|
|
||||||
if meta.saved_at + MEDIA_DURATION > SystemTime::now() {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", name = "Get media bytes", skip(self))]
|
|
||||||
pub(crate) async fn get_bytes(&self, uuid: Uuid) -> Result<Option<(String, Bytes)>, Error> {
|
|
||||||
if let Some(meta) = self.db.media_meta(uuid).await? {
|
|
||||||
if meta.saved_at + MEDIA_DURATION > SystemTime::now() {
|
|
||||||
return self
|
|
||||||
.db
|
|
||||||
.media_bytes(uuid)
|
|
||||||
.await
|
|
||||||
.map(|opt| opt.map(|bytes| (meta.media_type, bytes)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "Store media url", skip_all, fields(url = url.to_string().as_str()))]
|
#[tracing::instrument(name = "Store media url", skip_all, fields(url = url.to_string().as_str()))]
|
||||||
pub(crate) async fn store_url(&self, url: IriString) -> Result<Uuid, Error> {
|
pub(crate) async fn store_url(&self, url: IriString) -> Result<Uuid, Error> {
|
||||||
let uuid = Uuid::new_v4();
|
let uuid = Uuid::new_v4();
|
||||||
|
@ -63,23 +30,4 @@ impl MediaCache {
|
||||||
|
|
||||||
Ok(uuid)
|
Ok(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "store media bytes", skip(self, bytes))]
|
|
||||||
pub(crate) async fn store_bytes(
|
|
||||||
&self,
|
|
||||||
uuid: Uuid,
|
|
||||||
media_type: String,
|
|
||||||
bytes: Bytes,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.db
|
|
||||||
.save_bytes(
|
|
||||||
uuid,
|
|
||||||
MediaMeta {
|
|
||||||
media_type,
|
|
||||||
saved_at: SystemTime::now(),
|
|
||||||
},
|
|
||||||
bytes,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
53
src/db.rs
53
src/db.rs
|
@ -3,7 +3,6 @@ use crate::{
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
};
|
};
|
||||||
use activitystreams::iri_string::types::IriString;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::web::Bytes;
|
|
||||||
use rsa::{
|
use rsa::{
|
||||||
pkcs8::{DecodePrivateKey, EncodePrivateKey},
|
pkcs8::{DecodePrivateKey, EncodePrivateKey},
|
||||||
RsaPrivateKey,
|
RsaPrivateKey,
|
||||||
|
@ -26,8 +25,6 @@ struct Inner {
|
||||||
settings: Tree,
|
settings: Tree,
|
||||||
media_url_media_id: Tree,
|
media_url_media_id: Tree,
|
||||||
media_id_media_url: Tree,
|
media_id_media_url: Tree,
|
||||||
media_id_media_bytes: Tree,
|
|
||||||
media_id_media_meta: Tree,
|
|
||||||
actor_id_info: Tree,
|
actor_id_info: Tree,
|
||||||
actor_id_instance: Tree,
|
actor_id_instance: Tree,
|
||||||
actor_id_contact: Tree,
|
actor_id_contact: Tree,
|
||||||
|
@ -63,12 +60,6 @@ impl std::fmt::Debug for Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub(crate) struct MediaMeta {
|
|
||||||
pub(crate) media_type: String,
|
|
||||||
pub(crate) saved_at: SystemTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct Info {
|
pub struct Info {
|
||||||
pub(crate) software: String,
|
pub(crate) software: String,
|
||||||
|
@ -253,8 +244,6 @@ impl Db {
|
||||||
settings: db.open_tree("settings")?,
|
settings: db.open_tree("settings")?,
|
||||||
media_url_media_id: db.open_tree("media-url-media-id")?,
|
media_url_media_id: db.open_tree("media-url-media-id")?,
|
||||||
media_id_media_url: db.open_tree("media-id-media-url")?,
|
media_id_media_url: db.open_tree("media-id-media-url")?,
|
||||||
media_id_media_bytes: db.open_tree("media-id-media-bytes")?,
|
|
||||||
media_id_media_meta: db.open_tree("media-id-media-meta")?,
|
|
||||||
actor_id_info: db.open_tree("actor-id-info")?,
|
actor_id_info: db.open_tree("actor-id-info")?,
|
||||||
actor_id_instance: db.open_tree("actor-id-instance")?,
|
actor_id_instance: db.open_tree("actor-id-instance")?,
|
||||||
actor_id_contact: db.open_tree("actor-id-contact")?,
|
actor_id_contact: db.open_tree("actor-id-contact")?,
|
||||||
|
@ -392,25 +381,6 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn save_bytes(
|
|
||||||
&self,
|
|
||||||
id: Uuid,
|
|
||||||
meta: MediaMeta,
|
|
||||||
bytes: Bytes,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.unblock(move |inner| {
|
|
||||||
let vec = serde_json::to_vec(&meta)?;
|
|
||||||
|
|
||||||
inner
|
|
||||||
.media_id_media_bytes
|
|
||||||
.insert(id.as_bytes(), bytes.as_ref())?;
|
|
||||||
inner.media_id_media_meta.insert(id.as_bytes(), vec)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn media_id(&self, url: IriString) -> Result<Option<Uuid>, Error> {
|
pub(crate) async fn media_id(&self, url: IriString) -> Result<Option<Uuid>, Error> {
|
||||||
self.unblock(move |inner| {
|
self.unblock(move |inner| {
|
||||||
if let Some(ivec) = inner.media_url_media_id.get(url.as_str().as_bytes())? {
|
if let Some(ivec) = inner.media_url_media_id.get(url.as_str().as_bytes())? {
|
||||||
|
@ -433,29 +403,6 @@ impl Db {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn media_bytes(&self, id: Uuid) -> Result<Option<Bytes>, Error> {
|
|
||||||
self.unblock(move |inner| {
|
|
||||||
if let Some(ivec) = inner.media_id_media_bytes.get(id.as_bytes())? {
|
|
||||||
Ok(Some(Bytes::copy_from_slice(&ivec)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn media_meta(&self, id: Uuid) -> Result<Option<MediaMeta>, Error> {
|
|
||||||
self.unblock(move |inner| {
|
|
||||||
if let Some(ivec) = inner.media_id_media_meta.get(id.as_bytes())? {
|
|
||||||
let meta = serde_json::from_slice(&ivec)?;
|
|
||||||
Ok(Some(meta))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn blocks(&self) -> Result<Vec<String>, Error> {
|
pub(crate) async fn blocks(&self) -> Result<Vec<String>, Error> {
|
||||||
self.unblock(|inner| Ok(inner.blocks().collect())).await
|
self.unblock(|inner| Ok(inner.blocks().collect())).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,9 +130,6 @@ pub(crate) enum ErrorKind {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
HostMismatch(#[from] CheckError),
|
HostMismatch(#[from] CheckError),
|
||||||
|
|
||||||
#[error("Invalid or missing content type")]
|
|
||||||
ContentType,
|
|
||||||
|
|
||||||
#[error("Couldn't flush buffer")]
|
#[error("Couldn't flush buffer")]
|
||||||
FlushBuffer,
|
FlushBuffer,
|
||||||
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
use crate::{error::Error, jobs::JobState};
|
|
||||||
use background_jobs::ActixJob;
|
|
||||||
use std::{future::Future, pin::Pin};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub(crate) struct CacheMedia {
|
|
||||||
uuid: Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CacheMedia {
|
|
||||||
pub(crate) fn new(uuid: Uuid) -> Self {
|
|
||||||
CacheMedia { uuid }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "Cache media", skip(state))]
|
|
||||||
async fn perform(self, state: JobState) -> Result<(), Error> {
|
|
||||||
if !state.media.is_outdated(self.uuid).await? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(url) = state.media.get_url(self.uuid).await? {
|
|
||||||
let (content_type, bytes) = state.requests.fetch_bytes(url.as_str()).await?;
|
|
||||||
|
|
||||||
state
|
|
||||||
.media
|
|
||||||
.store_bytes(self.uuid, content_type, bytes)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActixJob for CacheMedia {
|
|
||||||
type State = JobState;
|
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::CacheMedia";
|
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::UrlKind,
|
config::UrlKind,
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
jobs::{cache_media::CacheMedia, Boolish, JobState},
|
jobs::{Boolish, JobState},
|
||||||
};
|
};
|
||||||
use activitystreams::{iri, iri_string::types::IriString};
|
use activitystreams::{iri, iri_string::types::IriString};
|
||||||
use background_jobs::ActixJob;
|
use background_jobs::ActixJob;
|
||||||
|
@ -75,8 +75,6 @@ impl QueryInstance {
|
||||||
|
|
||||||
let avatar = state.config.generate_url(UrlKind::Media(uuid));
|
let avatar = state.config.generate_url(UrlKind::Media(uuid));
|
||||||
|
|
||||||
state.job_server.queue(CacheMedia::new(uuid)).await?;
|
|
||||||
|
|
||||||
state
|
state
|
||||||
.node_cache
|
.node_cache
|
||||||
.set_contact(
|
.set_contact(
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
pub mod apub;
|
pub mod apub;
|
||||||
mod cache_media;
|
|
||||||
mod contact;
|
mod contact;
|
||||||
mod deliver;
|
mod deliver;
|
||||||
mod deliver_many;
|
mod deliver_many;
|
||||||
|
@ -8,7 +7,7 @@ mod nodeinfo;
|
||||||
mod process_listeners;
|
mod process_listeners;
|
||||||
|
|
||||||
pub(crate) use self::{
|
pub(crate) use self::{
|
||||||
cache_media::CacheMedia, contact::QueryContact, deliver::Deliver, deliver_many::DeliverMany,
|
contact::QueryContact, deliver::Deliver, deliver_many::DeliverMany,
|
||||||
instance::QueryInstance, nodeinfo::QueryNodeinfo,
|
instance::QueryInstance, nodeinfo::QueryNodeinfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,7 +58,6 @@ pub(crate) fn create_workers(
|
||||||
.register::<QueryNodeinfo>()
|
.register::<QueryNodeinfo>()
|
||||||
.register::<QueryInstance>()
|
.register::<QueryInstance>()
|
||||||
.register::<Listeners>()
|
.register::<Listeners>()
|
||||||
.register::<CacheMedia>()
|
|
||||||
.register::<QueryContact>()
|
.register::<QueryContact>()
|
||||||
.register::<apub::Announce>()
|
.register::<apub::Announce>()
|
||||||
.register::<apub::Follow>()
|
.register::<apub::Follow>()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::error::{Error, ErrorKind};
|
use crate::error::{Error, ErrorKind};
|
||||||
use activitystreams::iri_string::types::IriString;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::{http::header::Date, web::Bytes};
|
use actix_web::http::header::Date;
|
||||||
use awc::{error::SendRequestError, Client, ClientResponse};
|
use awc::{error::SendRequestError, Client, ClientResponse};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use http_signature_normalization_actix::prelude::*;
|
use http_signature_normalization_actix::prelude::*;
|
||||||
|
@ -294,11 +294,9 @@ impl Requests {
|
||||||
Ok(serde_json::from_slice(body.as_ref())?)
|
Ok(serde_json::from_slice(body.as_ref())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Fetch Bytes", skip(self), fields(signing_string))]
|
#[tracing::instrument(name = "Fetch response", skip(self), fields(signing_string))]
|
||||||
pub(crate) async fn fetch_bytes(&self, url: &str) -> Result<(String, Bytes), Error> {
|
pub(crate) async fn fetch_response(&self, url: IriString) -> Result<ClientResponse, Error> {
|
||||||
let parsed_url = url.parse::<IriString>()?;
|
if !self.breakers.should_try(&url) {
|
||||||
|
|
||||||
if !self.breakers.should_try(&parsed_url) {
|
|
||||||
return Err(ErrorKind::Breaker.into());
|
return Err(ErrorKind::Breaker.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,9 +305,10 @@ impl Requests {
|
||||||
|
|
||||||
let client: Client = self.client.borrow().clone();
|
let client: Client = self.client.borrow().clone();
|
||||||
let res = client
|
let res = client
|
||||||
.get(url)
|
.get(url.as_str())
|
||||||
.insert_header(("Accept", "*/*"))
|
.insert_header(("Accept", "*/*"))
|
||||||
.insert_header(Date(SystemTime::now().into()))
|
.insert_header(Date(SystemTime::now().into()))
|
||||||
|
.no_decompress()
|
||||||
.signature(
|
.signature(
|
||||||
self.config.clone(),
|
self.config.clone(),
|
||||||
self.key_id.clone(),
|
self.key_id.clone(),
|
||||||
|
@ -322,26 +321,9 @@ impl Requests {
|
||||||
.send()
|
.send()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut res = self.check_response(&parsed_url, res).await?;
|
let res = self.check_response(&url, res).await?;
|
||||||
|
|
||||||
let content_type = if let Some(content_type) = res.headers().get("content-type") {
|
Ok(res)
|
||||||
if let Ok(s) = content_type.to_str() {
|
|
||||||
s.to_owned()
|
|
||||||
} else {
|
|
||||||
return Err(ErrorKind::ContentType.into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(ErrorKind::ContentType.into());
|
|
||||||
};
|
|
||||||
|
|
||||||
let bytes = match res.body().limit(1024 * 1024 * 4).await {
|
|
||||||
Err(e) => {
|
|
||||||
return Err(ErrorKind::ReceiveResponse(url.to_string(), e.to_string()).into());
|
|
||||||
}
|
|
||||||
Ok(bytes) => bytes,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((content_type, bytes))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
use crate::{data::MediaCache, error::Error, requests::Requests};
|
use crate::{data::MediaCache, error::Error, requests::Requests};
|
||||||
use actix_web::{
|
use actix_web::{body::BodyStream, web, HttpResponse};
|
||||||
http::header::{CacheControl, CacheDirective},
|
|
||||||
web, HttpResponse,
|
|
||||||
};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[tracing::instrument(name = "Media", skip(media, requests))]
|
#[tracing::instrument(name = "Media", skip(media, requests))]
|
||||||
|
@ -13,30 +10,17 @@ pub(crate) async fn route(
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let uuid = uuid.into_inner();
|
let uuid = uuid.into_inner();
|
||||||
|
|
||||||
if let Some((content_type, bytes)) = media.get_bytes(uuid).await? {
|
|
||||||
return Ok(cached(content_type, bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(url) = media.get_url(uuid).await? {
|
if let Some(url) = media.get_url(uuid).await? {
|
||||||
let (content_type, bytes) = requests.fetch_bytes(url.as_str()).await?;
|
let res = requests.fetch_response(url).await?;
|
||||||
|
|
||||||
media
|
let mut response = HttpResponse::build(res.status());
|
||||||
.store_bytes(uuid, content_type.clone(), bytes.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
return Ok(cached(content_type, bytes));
|
for (name, value) in res.headers().iter().filter(|(h, _)| *h != "connection") {
|
||||||
|
response.insert_header((name.clone(), value.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(response.body(BodyStream::new(res)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(HttpResponse::NotFound().finish())
|
Ok(HttpResponse::NotFound().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cached(content_type: String, bytes: web::Bytes) -> HttpResponse {
|
|
||||||
HttpResponse::Ok()
|
|
||||||
.insert_header(CacheControl(vec![
|
|
||||||
CacheDirective::Public,
|
|
||||||
CacheDirective::MaxAge(60 * 60 * 24),
|
|
||||||
CacheDirective::Extension("immutable".to_owned(), None),
|
|
||||||
]))
|
|
||||||
.content_type(content_type)
|
|
||||||
.body(bytes)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue