Split into c2s and s2s endpoints
This commit is contained in:
parent
6a73ee7a43
commit
8f6e7a0d02
|
@ -1,17 +1,32 @@
|
|||
use crate::error::GementionError;
|
||||
use crate::models::mentions::{Mention, MentionUpload};
|
||||
use crate::models::mentions::{C2SMentionRequest, Mention};
|
||||
use crate::models::web::MentionResponse;
|
||||
use crate::{verification::verify};
|
||||
use crate::validation::Validation;
|
||||
use crate::verification::verify;
|
||||
use fluffer::Client;
|
||||
|
||||
// Receive a mention over Titan.
|
||||
// - Make Gemini request to see if target page supports gemention.
|
||||
// - If so, store mention in DB.
|
||||
pub(crate) async fn receive_mention(client: Client) -> Result<MentionResponse, GementionError> {
|
||||
// TODO change to return MentionResponse or something like that.
|
||||
let titan = MentionUpload::try_from(&client)?;
|
||||
let mut mention = Mention::try_from(titan)?;
|
||||
verify(&mut mention).await?;
|
||||
/// Receive a client-to-server Gemention: i.e. a user posting a
|
||||
/// comment to a gemention-enabled page. The client must identify
|
||||
/// themselves with a cert that at least has a username.
|
||||
pub(crate) async fn receive_c2s_gemention(
|
||||
client: Client,
|
||||
) -> Result<MentionResponse, GementionError> {
|
||||
let mention_upload = C2SMentionRequest::try_from(&client)?.validate()?;
|
||||
let mut mention = Mention::try_from(mention_upload)?;
|
||||
verify(&client.url, &mut mention).await?;
|
||||
|
||||
Ok(MentionResponse::from(mention))
|
||||
}
|
||||
|
||||
/// Receive a server-to-server Gemention: another capsule linking to a
|
||||
/// gemention-enabled page on this capsule. There is an exchange of
|
||||
/// titan keys so that the servers can talk to one another.
|
||||
pub(crate) async fn receive_s2s_gemention(
|
||||
client: Client,
|
||||
) -> Result<MentionResponse, GementionError> {
|
||||
let mention_upload = C2SMentionRequest::try_from(&client)?;
|
||||
let mut mention = Mention::try_from(mention_upload)?;
|
||||
verify(&client.url, &mut mention).await?;
|
||||
|
||||
Ok(MentionResponse::from(mention))
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ use async_trait::async_trait;
|
|||
use fluffer::GemBytes;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::validation::c2s::C2SValidationError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub(crate) enum GementionError {
|
||||
#[error("No content found for target")]
|
||||
|
@ -30,6 +32,9 @@ pub(crate) enum GementionError {
|
|||
#[error("value was not utf8: {0}")]
|
||||
Utf8Error(#[from] Utf8Error),
|
||||
|
||||
#[error("c2s validation error: {0}")]
|
||||
C2SValidationError(#[from] C2SValidationError),
|
||||
|
||||
#[error("generic error: {0}")]
|
||||
UnclassifiedError(#[from] anyhow::Error),
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use fluffer::AppErr;
|
|||
mod comments;
|
||||
mod error;
|
||||
mod models;
|
||||
mod validation;
|
||||
mod routes;
|
||||
mod verification;
|
||||
|
||||
|
|
|
@ -29,15 +29,20 @@ fn parse_content_body(raw_body: &[u8]) -> Result<&str, GementionError> {
|
|||
}
|
||||
|
||||
/// Wraps an incoming Titan request and provides handy methods for
|
||||
/// extracting the expected information.
|
||||
pub(crate) struct MentionUpload<'a> {
|
||||
/// extracting the expected information for a client-to-server
|
||||
/// gemention.
|
||||
pub(crate) struct C2SMentionRequest<'a> {
|
||||
client: &'a Client,
|
||||
receiving_endpoint: &'a Url,
|
||||
}
|
||||
|
||||
impl<'a> MentionUpload<'a> {
|
||||
pub fn from_client(client: &'a Client) -> Result<MentionUpload, GementionError> {
|
||||
impl<'a> C2SMentionRequest<'a> {
|
||||
pub fn from_client(client: &'a Client) -> Result<C2SMentionRequest, GementionError> {
|
||||
if let Some(_) = client.titan {
|
||||
Ok(Self { client: &client })
|
||||
Ok(Self {
|
||||
client: &client,
|
||||
receiving_endpoint: &client.url,
|
||||
})
|
||||
} else {
|
||||
Err(GementionError::NotTitanResource)
|
||||
}
|
||||
|
@ -59,6 +64,10 @@ impl<'a> MentionUpload<'a> {
|
|||
titan!(self).size
|
||||
}
|
||||
|
||||
pub fn certificate(&self) -> Option<String> {
|
||||
self.client.certificate()
|
||||
}
|
||||
|
||||
pub fn fingerprint(&self) -> Option<String> {
|
||||
self.client.fingerprint()
|
||||
}
|
||||
|
@ -74,13 +83,17 @@ impl<'a> MentionUpload<'a> {
|
|||
pub fn content(&self) -> Result<&str, GementionError> {
|
||||
parse_content_body(self.raw_body())
|
||||
}
|
||||
|
||||
pub fn receiving_endpoint(&self) -> &Url {
|
||||
self.receiving_endpoint
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a Client> for MentionUpload<'a> {
|
||||
impl<'a> TryFrom<&'a Client> for C2SMentionRequest<'a> {
|
||||
type Error = GementionError;
|
||||
|
||||
fn try_from(client: &'a Client) -> Result<Self, Self::Error> {
|
||||
MentionUpload::from_client(client)
|
||||
C2SMentionRequest::from_client(client)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,10 +201,10 @@ impl TryFrom<&[u8]> for MentionType {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<MentionUpload<'a>> for Mention {
|
||||
impl<'a> TryFrom<C2SMentionRequest<'a>> for Mention {
|
||||
type Error = GementionError;
|
||||
|
||||
fn try_from(resource: MentionUpload<'a>) -> Result<Self, Self::Error> {
|
||||
fn try_from(resource: C2SMentionRequest<'a>) -> Result<Self, Self::Error> {
|
||||
if resource.mime() == "text/plain" {
|
||||
// Be flexible on mention type: if first line isn't a
|
||||
// mention type, just assume reply.
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use crate::error::GementionError;
|
||||
use std::fmt::Display;
|
||||
|
||||
use super::mentions::Mention;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum VerificationStatus {
|
||||
Verified {
|
||||
|
|
|
@ -70,7 +70,7 @@ impl GemBytes for MentionResponse {
|
|||
let status_line = self.status_line();
|
||||
let body = if self.should_have_body() {
|
||||
format!(
|
||||
"{}\n\n{}\n\n{},",
|
||||
"{}\n\n{}\n\n{}",
|
||||
self.headline(),
|
||||
self.description(),
|
||||
self.mention_body()
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use fluffer::App;
|
||||
use crate::comments::receive_mention;
|
||||
use crate::comments::{receive_c2s_gemention, receive_s2s_gemention};
|
||||
|
||||
pub(crate) fn create_app() -> App {
|
||||
App::default()
|
||||
.titan("/receive/*target", receive_mention, 20_000)
|
||||
.titan("/c2s/receive/*target", receive_c2s_gemention, 2048)
|
||||
.titan("/s2s/receive/*target", receive_s2s_gemention, 2048)
|
||||
.route("/", |_| async {
|
||||
"# Welcome\n=> titan://localhost/receive/agnos.is/posts/webmentions-test.gmi Receive Mention"
|
||||
"# Welcome\n=> titan://localhost/c2s/receive/agnos.is/posts/webmentions-test.gmi Receive Mention"
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
use crate::{models::mentions::C2SMentionRequest, error::GementionError};
|
||||
use super::Validation;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum C2SValidationError {
|
||||
#[error("a username is required on the client certificate")]
|
||||
UsernameRequired,
|
||||
|
||||
#[error("a client certificate is required")]
|
||||
ClientCertificateRequired,
|
||||
}
|
||||
|
||||
impl Validation for C2SMentionRequest<'_> {
|
||||
type Error = C2SValidationError;
|
||||
|
||||
fn validate(self) -> Result<Self, Self::Error> {
|
||||
if self.username().is_none() {
|
||||
return Err(C2SValidationError::UsernameRequired);
|
||||
}
|
||||
|
||||
if self.certificate().is_none() {
|
||||
return Err(C2SValidationError::ClientCertificateRequired);
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
pub(crate) mod c2s;
|
||||
|
||||
pub trait Validation {
|
||||
type Error;
|
||||
|
||||
fn validate(self) -> Result<Self, Self::Error> where Self: Sized;
|
||||
}
|
|
@ -69,9 +69,9 @@ fn verify_mentions<S: AsRef<str>>(
|
|||
}
|
||||
}
|
||||
|
||||
async fn verify_mention(mention: &mut Mention) -> Result<(), GementionError> {
|
||||
async fn verify_mention(expected_link: &Url, mention: &mut Mention) -> Result<(), GementionError> {
|
||||
let url = Url::parse(&format!("gemini://{}", mention.target()))?;
|
||||
let expected_link = Url::parse(OUR_ENDPOINT)?.join(mention.target())?;
|
||||
println!("expected link is {}", expected_link);
|
||||
|
||||
let resp = germ_request(&url).await?;
|
||||
let meta = GeminiMetadata::from_string(resp.meta());
|
||||
|
@ -89,6 +89,6 @@ async fn verify_mention(mention: &mut Mention) -> Result<(), GementionError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn verify(mention: &mut Mention) -> Result<(), GementionError> {
|
||||
verify_mention(mention).await
|
||||
pub(crate) async fn verify(expected_url: &Url, mention: &mut Mention) -> Result<(), GementionError> {
|
||||
verify_mention(expected_url, mention).await
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue