Compare commits

...

1 commit

Author SHA1 Message Date
Aode (lion) d1d2743dfd Start work on Resource type, add GAT 2021-11-21 20:51:43 -06:00
10 changed files with 247 additions and 163 deletions

View file

@ -1,3 +1,5 @@
#![feature(generic_associated_types)]
use actix_web::{
error::BlockingError,
web::{self, ServiceConfig},
@ -47,18 +49,13 @@ pub trait Verifier {
type Verify: Verify<Error = Self::VerifyError> + VerifyBuilder;
}
pub trait RepoFactoryX<D: Dereference> {
pub trait RepoFactory {
type RepoError;
type RepoFactory: for<'a> RepoFactory<'a, D, RepoError = Self::RepoError>;
type Repo<'a>: Repo<Error = Self::RepoError> + 'a
where
Self: 'a;
fn factory(&self) -> &Self::RepoFactory;
}
pub trait RepoFactory<'a, D: Dereference> {
type RepoError;
type Repo: for<'b> Repo<'b, D, Error = Self::RepoError> + 'a;
fn repo(&'a self) -> Self::Repo;
fn repo<'a>(&'a self) -> Self::Repo<'a>;
}
/// Ingest activitypub objects at a given endpoint
@ -90,10 +87,10 @@ where
Activity: for<'de> serde::de::Deserialize<'de> + 'static,
Metadata: FromRequest + 'static,
I: for<'a> Ingest<'a, Activity, Metadata> + 'static,
V: RepoFactoryX<ObjectId<PublicKeyType>> + Verifier + DigestFactory + Clone + 'static,
V: RepoFactory + Verifier + DigestFactory + Clone + 'static,
<V as DigestFactory>::Digest: Clone,
E: ResponseError + From<VerifyError> + From<V::VerifyError> + 'static,
E: From<<V as RepoFactoryX<ObjectId<PublicKeyType>>>::RepoError>,
E: From<<V as RepoFactory>::RepoError>,
{
move |service_config: &mut ServiceConfig| {
let signature = VerifySignature::new(
@ -148,14 +145,14 @@ pub fn serve_objects<D, R, V, E>(
require_signature: bool,
) -> impl FnOnce(&mut ServiceConfig)
where
D: Dereference + From<Url> + 'static,
D: Dereference + From<Url> + Send + Sync + 'static,
<D as Dereference>::Output: serde::ser::Serialize,
R: for<'a> RepoFactory<'a, D> + 'static,
V: RepoFactoryX<ObjectId<PublicKeyType>> + Verifier + Clone + 'static,
R: RepoFactory + 'static,
V: RepoFactory + Verifier + Clone + 'static,
E: ResponseError
+ From<VerifyError>
+ From<V::VerifyError>
+ From<<V as RepoFactoryX<ObjectId<PublicKeyType>>>::RepoError>
+ From<<V as RepoFactory>::RepoError>
+ 'static,
{
move |service_config: &mut ServiceConfig| {
@ -212,9 +209,9 @@ async fn serve_object_handler<D, R>(
serve_info: web::Data<ServeInfo<R>>,
) -> HttpResponse
where
D: Dereference + From<Url> + 'static,
D: Dereference + From<Url> + Send + Sync + 'static,
<D as Dereference>::Output: serde::ser::Serialize,
R: for<'a> RepoFactory<'a, D> + 'static,
R: RepoFactory + 'static,
{
let uri = req.uri().to_string();
let url = format!("{}{}", serve_info.local_host, uri);
@ -225,7 +222,7 @@ where
let d = D::from(url);
let repo = serve_info.repo_factory.repo();
let res = repo.fetch(&d).await;
let res = repo.fetch(d).await;
match res {
Ok(Some(object)) => HttpResponse::Ok()
@ -363,7 +360,7 @@ async fn verify<'a, V, E>(
signing_string: String,
) -> Result<bool, E>
where
V: Verifier + RepoFactoryX<ObjectId<PublicKeyType>>,
V: Verifier + RepoFactory,
E: From<VerifyError> + From<V::RepoError> + From<V::VerifyError> + 'static,
{
match algorithm {
@ -373,11 +370,14 @@ where
Some(other) => return Err(VerifyError::Algorithm(other.to_string()).into()),
};
let key_id = object_id(key_id.parse().map_err(|_| VerifyError::KeyId(key_id))?);
let key_id = match key_id.parse() {
Ok(url) => object_id(url),
Err(_) => return Err(VerifyError::KeyId(key_id).into()),
};
let repo = verifier.factory().repo();
let repo = verifier.repo();
let response = repo
.fetch(&key_id)
.fetch(key_id.clone())
.await
.map_err(E::from)?
.ok_or(VerifyError::KeyNotFound)?;
@ -409,11 +409,11 @@ where
impl<V, E> SignatureVerify for VerifyMiddleware<V, ObjectId<PublicKeyType>, E>
where
V: RepoFactoryX<ObjectId<PublicKeyType>> + Verifier + Clone + 'static,
V: RepoFactory + Verifier + Clone + 'static,
E: ResponseError
+ From<VerifyError>
+ From<V::VerifyError>
+ From<<V as RepoFactoryX<ObjectId<PublicKeyType>>>::RepoError>
+ From<<V as RepoFactory>::RepoError>
+ 'static,
{
type Error = E;

View file

@ -1,3 +1,5 @@
#![feature(generic_associated_types)]
use actix_http::error::BlockingError;
use apub_core::{
deref::{Dereference, Repo},
@ -100,7 +102,7 @@ where
async fn do_fetch<Id: Dereference>(
&self,
id: &Id,
id: Id,
) -> Result<Option<<Id as Dereference>::Output>, AwcError<SignError<Crypto>>> {
let mut response = self
.client
@ -121,21 +123,23 @@ where
}
}
impl<'a, Id, CurrentSession, Crypto> Repo<'a, Id> for AwcClient<'a, CurrentSession, Crypto>
impl<'b, CurrentSession, Crypto> Repo for AwcClient<'b, CurrentSession, Crypto>
where
Id: Dereference + 'a,
CurrentSession: Session,
Crypto: SignFactory,
{
type Error = AwcError<SignError<Crypto>>;
type Future = Pin<
Box<dyn Future<Output = Result<Option<<Id as Dereference>::Output>, Self::Error>> + 'a>,
>;
type Future<'a, Id: Dereference + 'a>
where
'b: 'a,
Self: 'a,
= Pin<Box<dyn Future<Output = Result<Option<Id::Output>, Self::Error>> + 'a>>;
fn fetch(&'a self, id: &'a Id) -> Self::Future {
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id> {
let url = id.url().clone();
Box::pin(apub_core::session::guard(
self.do_fetch(id),
id.url(),
url,
&self.session,
))
}
@ -188,7 +192,7 @@ where
Ok(())
},
inbox,
inbox.clone(),
&self.session,
))
}

View file

@ -1,14 +1,17 @@
use std::{future::Future, rc::Rc, sync::Arc};
use url::Url;
pub trait Repo<'a, D: Dereference> {
pub trait Repo {
type Error: std::error::Error + 'static;
type Future: Future<Output = Result<Option<D::Output>, Self::Error>> + 'a;
type Future<'a, Id: Dereference + 'a>: Future<Output = Result<Option<Id::Output>, Self::Error>>
+ 'a
where
Self: 'a;
fn fetch(&'a self, id: &'a D) -> Self::Future;
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id>;
}
pub trait Dereference {
pub trait Dereference: Clone + Send + Sync {
type Output: serde::de::DeserializeOwned;
fn url(&self) -> &Url;
@ -25,17 +28,6 @@ where
}
}
impl<'a, T> Dereference for &'a mut T
where
T: Dereference,
{
type Output = T::Output;
fn url(&self) -> &Url {
T::url(self)
}
}
impl<T> Dereference for Box<T>
where
T: Dereference,
@ -47,17 +39,6 @@ where
}
}
impl<T> Dereference for Rc<T>
where
T: Dereference,
{
type Output = T::Output;
fn url(&self) -> &Url {
T::url(self)
}
}
impl<T> Dereference for Arc<T>
where
T: Dereference,
@ -69,62 +50,79 @@ where
}
}
impl<'a, 'b, D: Dereference, T> Repo<'a, D> for &'b T
impl<'b, T> Repo for &'b T
where
T: Repo<'a, D>,
T: Repo,
{
type Error = T::Error;
type Future = T::Future;
type Future<'a, Id: Dereference + 'a>
where
'b: 'a,
T: 'a,
= T::Future<'a, Id>;
fn fetch(&'a self, id: &'a D) -> Self::Future {
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id> {
T::fetch(self, id)
}
}
impl<'a, 'b, D: Dereference, T> Repo<'a, D> for &'b mut T
impl<'b, T> Repo for &'b mut T
where
T: Repo<'a, D>,
T: Repo,
{
type Error = T::Error;
type Future = T::Future;
type Future<'a, Id: Dereference + 'a>
where
'b: 'a,
T: 'a,
= T::Future<'a, Id>;
fn fetch(&'a self, id: &'a D) -> Self::Future {
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id> {
T::fetch(self, id)
}
}
impl<'a, D: Dereference, T> Repo<'a, D> for Box<T>
impl<T> Repo for Box<T>
where
T: Repo<'a, D>,
T: Repo,
{
type Error = T::Error;
type Future = T::Future;
type Future<'a, Id: Dereference + 'a>
where
T: 'a,
= T::Future<'a, Id>;
fn fetch(&'a self, id: &'a D) -> Self::Future {
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id> {
T::fetch(self, id)
}
}
impl<'a, D: Dereference, T> Repo<'a, D> for Rc<T>
impl<T> Repo for Rc<T>
where
T: Repo<'a, D>,
T: Repo,
{
type Error = T::Error;
type Future = T::Future;
type Future<'a, Id: Dereference + 'a>
where
T: 'a,
= T::Future<'a, Id>;
fn fetch(&'a self, id: &'a D) -> Self::Future {
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id> {
T::fetch(self, id)
}
}
impl<'a, D: Dereference, T> Repo<'a, D> for Arc<T>
impl<T> Repo for Arc<T>
where
T: Repo<'a, D>,
T: Repo,
{
type Error = T::Error;
type Future = T::Future;
type Future<'a, Id: Dereference + 'a>
where
T: 'a,
= T::Future<'a, Id>;
fn fetch(&'a self, id: &'a D) -> Self::Future {
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id> {
T::fetch(self, id)
}
}

View file

@ -1,7 +1,10 @@
#![feature(generic_associated_types)]
pub mod deliver;
pub mod deref;
pub mod digest;
pub mod ingest;
pub mod object_id;
pub mod resource;
pub mod session;
pub mod signature;

89
apub-core/src/resource.rs Normal file
View file

@ -0,0 +1,89 @@
use crate::deref::{Dereference, Repo};
use std::{future::Future, marker::PhantomData};
use url::Url;
pub trait ActivityPub<'a> {
type Error;
type Resource: Resource<ActivityPub = Self>;
type Future: Future<
Output = Result<<Self::Resource as Resource>::InternalRepresentation, Self::Error>,
>;
fn to_internal(&'a self, resource: &'a Self::Resource) -> Self::Future;
}
pub trait InternalRepresentation<'a> {
type Error;
type Resource: Resource<InternalRepresentation = Self>;
type Future: Future<Output = Result<<Self::Resource as Resource>::ActivityPub, Self::Error>>;
fn to_activitypub(&'a self, resource: &'a Self::Resource) -> Self::Future;
}
pub trait Delete<'a> {
type Error;
type Resource: Resource<ActivityPub = Self>;
type Future: Future<Output = Result<<Self::Resource as Resource>::Tombstone, Self::Error>>;
fn delete(&'a self, resource: &'a Self::Resource) -> Self::Future;
}
pub trait Resource {
type Tombstone: serde::ser::Serialize + for<'de> serde::de::Deserialize<'de>;
type ActivityPub: for<'a> ActivityPub<'a>
+ for<'b> Delete<'b>
+ serde::ser::Serialize
+ for<'de> serde::de::Deserialize<'de>;
type InternalRepresentation: for<'a> InternalRepresentation<'a>;
}
pub async fn fetch<Res, Rep>(
id: Url,
repo: Rep,
) -> Result<Option<Either<Res::ActivityPub, Res::Tombstone>>, Rep::Error>
where
Res: Resource,
Rep: Repo,
{
let id: ObjectId<Res> = object_id(id);
repo.fetch(id).await
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(untagged)]
pub enum Either<L, R> {
Left(L),
Right(R),
}
struct ObjectId<R> {
url: Url,
_resource: PhantomData<fn() -> R>,
}
impl<R> Clone for ObjectId<R> {
fn clone(&self) -> Self {
Self {
url: self.url.clone(),
_resource: PhantomData,
}
}
}
fn object_id<R: Resource>(url: Url) -> ObjectId<R> {
ObjectId {
url,
_resource: PhantomData,
}
}
impl<R> Dereference for ObjectId<R>
where
R: Resource,
{
type Output = Either<R::ActivityPub, R::Tombstone>;
fn url(&self) -> &Url {
&self.url
}
}

View file

@ -23,23 +23,23 @@ pub struct RequestCountSession {
#[derive(Clone, Debug)]
pub struct SessionError;
pub async fn guard<Fut, T, E, S>(fut: Fut, url: &Url, session: &S) -> Result<T, E>
pub async fn guard<Fut, T, E, S>(fut: Fut, url: Url, session: &S) -> Result<T, E>
where
Fut: Future<Output = Result<T, E>>,
E: From<SessionError>,
S: Session,
{
if !session.should_procede(url) {
if !session.should_procede(&url) {
return Err(SessionError.into());
}
match fut.await {
Ok(t) => {
session.mark_success(url);
session.mark_success(&url);
Ok(t)
}
Err(e) => {
session.mark_failure(url);
session.mark_failure(&url);
Err(e)
}
}

View file

@ -1,3 +1,5 @@
#![feature(generic_associated_types)]
use apub_core::deref::{Dereference, Repo};
use std::{
future::Future,
@ -20,8 +22,7 @@ pub enum Error<Left, Right> {
Right(#[source] Right),
}
type ClientError<'a, Id, Local, Http> =
Error<<Local as Repo<'a, Id>>::Error, <Http as Repo<'a, Id>>::Error>;
type ClientError<Local, Http> = Error<<Local as Repo>::Error, <Http as Repo>::Error>;
impl<Local, Http> Client<Local, Http> {
pub fn new(local_domain: String, local: Local, http: Http) -> Self {
@ -34,27 +35,29 @@ impl<Local, Http> Client<Local, Http> {
pub async fn dereference<'a, Id>(
&'a self,
id: &'a Id,
) -> Result<Option<<Id as Dereference>::Output>, ClientError<'a, Id, Local, Http>>
id: Id,
) -> Result<Option<<Id as Dereference>::Output>, ClientError<Local, Http>>
where
Id: Dereference,
Local: Repo<'a, Id> + 'a,
Http: Repo<'a, Id> + 'a,
Id: Dereference + Clone + 'a,
Local: Repo + 'a,
Http: Repo + 'a,
{
self.fetch(id).await
}
}
impl<'a, Id, Local, Http> Repo<'a, Id> for Client<Local, Http>
impl<Local, Http> Repo for Client<Local, Http>
where
Id: Dereference + 'a,
Local: Repo<'a, Id> + 'a,
Http: Repo<'a, Id> + 'a,
Local: Repo,
Http: Repo,
{
type Error = ClientError<'a, Id, Local, Http>;
type Future = DereferenceFuture<'a, Id, Local, Http>;
type Error = ClientError<Local, Http>;
type Future<'a, Id: Dereference + 'a>
where
Self: 'a,
= DereferenceFuture<'a, Id, Local, Http>;
fn fetch(&'a self, id: &'a Id) -> Self::Future {
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id> {
DereferenceFuture {
id,
client: self,
@ -66,19 +69,23 @@ where
pin_project_lite::pin_project! {
#[project = DereferenceFutureInnerProj]
#[project_replace = DereferenceFutureInnerProjReplace]
enum DereferenceFutureInner<'a, Id: Dereference, Local, Http>
enum DereferenceFutureInner<'a, Id, Local, Http>
where
Local: Repo<'a, Id>,
Http: Repo<'a, Id>,
Id: Dereference,
Id: 'a,
Local: Repo,
Local: 'a,
Http: Repo,
Http: 'a,
{
Pending,
Local {
#[pin]
future: <Local as Repo<'a, Id>>::Future,
future: <Local as Repo>::Future<'a, Id>,
},
Http {
#[pin]
future: <Http as Repo<'a, Id>>::Future,
future: <Http as Repo>::Future<'a, Id>,
}
}
}
@ -86,10 +93,10 @@ pin_project_lite::pin_project! {
pin_project_lite::pin_project! {
pub struct DereferenceFuture<'a, Id: Dereference, Local, Http>
where
Local: Repo<'a, Id>,
Http: Repo<'a, Id>,
Local: Repo,
Http: Repo,
{
id: &'a Id,
id: Id,
client: &'a Client<Local, Http>,
#[pin]
@ -100,10 +107,10 @@ pin_project_lite::pin_project! {
impl<'a, Id, Local, Http> Future for DereferenceFuture<'a, Id, Local, Http>
where
Id: Dereference + 'a,
Local: Repo<'a, Id>,
Http: Repo<'a, Id>,
Local: Repo,
Http: Repo,
{
type Output = Result<Option<<Id as Dereference>::Output>, ClientError<'a, Id, Local, Http>>;
type Output = Result<Option<<Id as Dereference>::Output>, ClientError<Local, Http>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
@ -111,7 +118,7 @@ where
match this.state.as_mut().project() {
DereferenceFutureInnerProj::Pending => {
this.state.project_replace(DereferenceFutureInner::Local {
future: this.client.local.fetch(this.id),
future: this.client.local.fetch(this.id.clone()),
});
self.poll(cx)
@ -124,7 +131,7 @@ where
}
Poll::Ready(Ok(None)) => {
this.state.project_replace(DereferenceFutureInner::Http {
future: this.client.http.fetch(this.id),
future: this.client.http.fetch(this.id.clone()),
});
self.poll(cx)

View file

@ -1,3 +1,5 @@
#![feature(generic_associated_types)]
use apub_core::{
deref::{Dereference, Repo},
digest::{Digest, DigestBuilder, DigestFactory},
@ -90,7 +92,7 @@ where
async fn do_fetch<Id>(
&self,
id: &Id,
id: Id,
) -> Result<Option<<Id as Dereference>::Output>, ReqwestError<SignTraitError<Crypto>>>
where
Id: Dereference,
@ -112,27 +114,24 @@ where
}
}
impl<'a, Id, CurrentSession, Crypto> Repo<'a, Id> for ReqwestClient<'a, CurrentSession, Crypto>
impl<'b, CurrentSession, Crypto> Repo for ReqwestClient<'b, CurrentSession, Crypto>
where
Id: Dereference + Send + Sync,
<Id as Dereference>::Output: 'static,
CurrentSession: Session + Send + Sync,
Crypto: SignFactory + Send + Sync,
<Crypto as SignFactory>::KeyId: Send,
{
type Error = ReqwestError<SignTraitError<Crypto>>;
type Future = Pin<
Box<
dyn Future<Output = Result<Option<<Id as Dereference>::Output>, Self::Error>>
+ Send
+ 'a,
>,
>;
type Future<'a, Id: Dereference + 'a>
where
'b: 'a,
Self: 'a,
= Pin<Box<dyn Future<Output = Result<Option<Id::Output>, Self::Error>> + Send + 'a>>;
fn fetch(&'a self, id: &'a Id) -> Self::Future {
fn fetch<'a, Id: Dereference + 'a>(&'a self, id: Id) -> Self::Future<'a, Id> {
let url = id.url().clone();
Box::pin(apub_core::session::guard(
self.do_fetch(id),
id.url(),
url,
&self.session,
))
}
@ -184,7 +183,7 @@ where
Ok(())
},
inbox,
inbox.clone(),
&self.session,
))
}

View file

@ -1,7 +1,7 @@
#![feature(generic_associated_types)]
use actix_web::{middleware::Logger, web, App, HttpServer, ResponseError};
use apub_actix_web::{
inbox, serve_objects, RepoFactory, RepoFactoryX, SignatureConfig, Verifier, VerifyError,
};
use apub_actix_web::{inbox, serve_objects, RepoFactory, SignatureConfig, Verifier, VerifyError};
use apub_core::{
deref::{Dereference, Repo},
digest::DigestFactory,
@ -57,15 +57,14 @@ impl MemoryRepo {
impl ResponseError for ServerError {}
impl<'a, D> Repo<'a, D> for MemoryRepo
where
D: Dereference,
D::Output: 'static,
{
impl Repo for MemoryRepo {
type Error = serde_json::Error;
type Future = Ready<Result<Option<D::Output>, Self::Error>>;
type Future<'a, Id: Dereference + 'a>
where
Self: 'a,
= Ready<Result<Option<Id::Output>, Self::Error>>;
fn fetch(&'a self, id: &'a D) -> Self::Future {
fn fetch<'a, D: Dereference + 'a>(&'a self, id: D) -> Self::Future<'a, D> {
if let Some(obj_ref) = self.inner.get(id.url()) {
match serde_json::from_value(obj_ref.clone()) {
Ok(output) => ready(Ok(Some(output))),
@ -77,15 +76,14 @@ where
}
}
impl<'a, D> RepoFactory<'a, D> for MemoryRepo
where
D: Dereference,
D::Output: 'static,
{
impl RepoFactory for MemoryRepo {
type RepoError = serde_json::Error;
type Repo = &'a MemoryRepo;
type Repo<'a>
where
Self: 'a,
= &'a MemoryRepo;
fn repo(&'a self) -> Self::Repo {
fn repo<'a>(&'a self) -> Self::Repo<'a> {
self
}
}
@ -95,32 +93,18 @@ impl Verifier for RequestVerifier {
type Verify = RsaVerifier;
}
impl<'a, D> RepoFactory<'a, D> for RequestVerifier
where
D: Dereference,
D::Output: 'static,
{
impl RepoFactory for RequestVerifier {
type RepoError = serde_json::Error;
type Repo = &'a MemoryRepo;
type Repo<'a>
where
Self: 'a,
= &'a MemoryRepo;
fn repo(&'a self) -> Self::Repo {
fn repo<'a>(&'a self) -> Self::Repo<'a> {
&self.repo
}
}
impl<D> RepoFactoryX<D> for RequestVerifier
where
D: Dereference,
D::Output: 'static,
{
type RepoError = serde_json::Error;
type RepoFactory = Self;
fn factory(&self) -> &Self::RepoFactory {
self
}
}
impl DigestFactory for RequestVerifier {
type Digest = Sha256Digest;
}

View file

@ -113,7 +113,7 @@ where
repo: &'a R,
) -> Result<Option<<Self as Dereference>::Output>, Box<dyn std::error::Error>>
where
R: Repo<'a, Self>,
R: Repo,
{
Ok(repo.fetch(self).await?)
}