Restructure handling code to ensure EVENT always gets an OK reply (except for early errors)

This commit is contained in:
Mike Dilger 2024-02-18 10:02:10 +13:00
parent 775f15e864
commit a1c81acd8f
2 changed files with 55 additions and 34 deletions

View File

@ -25,6 +25,9 @@ pub enum ChorusError {
// Auth failure // Auth failure
AuthFailure, AuthFailure,
// Auth required
AuthRequired,
// Bad event id // Bad event id
BadEventId, BadEventId,
@ -52,6 +55,9 @@ pub enum ChorusError {
// End of Input // End of Input
EndOfInput, EndOfInput,
// Event is Invalid
EventIsInvalid(String),
// Http // Http
Http(hyper::http::Error), Http(hyper::http::Error),
@ -88,6 +94,9 @@ pub enum ChorusError {
// No private key // No private key
NoPrivateKey, NoPrivateKey,
// Restricted
Restricted,
// Rustls // Rustls
Rustls(tokio_rustls::rustls::Error), Rustls(tokio_rustls::rustls::Error),
@ -114,6 +123,7 @@ impl std::fmt::Display for ChorusError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
ChorusError::AuthFailure => write!(f, "AUTH failure"), ChorusError::AuthFailure => write!(f, "AUTH failure"),
ChorusError::AuthRequired => write!(f, "AUTH required"),
ChorusError::BadEventId => write!(f, "Bad event id, does not match hash"), ChorusError::BadEventId => write!(f, "Bad event id, does not match hash"),
ChorusError::BadHexInput => write!(f, "Bad hex input"), ChorusError::BadHexInput => write!(f, "Bad hex input"),
ChorusError::BufferTooSmall => write!(f, "Output buffer too small"), ChorusError::BufferTooSmall => write!(f, "Output buffer too small"),
@ -123,6 +133,7 @@ impl std::fmt::Display for ChorusError {
ChorusError::Crypto(e) => write!(f, "{e}"), ChorusError::Crypto(e) => write!(f, "{e}"),
ChorusError::Duplicate => write!(f, "Duplicate"), ChorusError::Duplicate => write!(f, "Duplicate"),
ChorusError::EndOfInput => write!(f, "End of input"), ChorusError::EndOfInput => write!(f, "End of input"),
ChorusError::EventIsInvalid(s) => write!(f, "Event is invalid: {s}"),
ChorusError::Http(e) => write!(f, "{e}"), ChorusError::Http(e) => write!(f, "{e}"),
ChorusError::Hyper(e) => write!(f, "{e}"), ChorusError::Hyper(e) => write!(f, "{e}"),
ChorusError::Io(e) => write!(f, "{e}"), ChorusError::Io(e) => write!(f, "{e}"),
@ -147,6 +158,7 @@ impl std::fmt::Display for ChorusError {
), ),
ChorusError::Lmdb(e) => write!(f, "{e}"), ChorusError::Lmdb(e) => write!(f, "{e}"),
ChorusError::NoPrivateKey => write!(f, "Private Key Not Found"), ChorusError::NoPrivateKey => write!(f, "Private Key Not Found"),
ChorusError::Restricted => write!(f, "Restricted"),
ChorusError::Rustls(e) => write!(f, "{e}"), ChorusError::Rustls(e) => write!(f, "{e}"),
ChorusError::Tungstenite(e) => write!(f, "{e}"), ChorusError::Tungstenite(e) => write!(f, "{e}"),
ChorusError::Scraper => write!(f, "Filter is underspecified. Scrapers are not allowed"), ChorusError::Scraper => write!(f, "Filter is underspecified. Scrapers are not allowed"),

View File

@ -115,6 +115,8 @@ impl WebSocketService {
} }
pub async fn event(&mut self, msg: String, mut inpos: usize) -> Result<(), Error> { pub async fn event(&mut self, msg: String, mut inpos: usize) -> Result<(), Error> {
const PERSONAL_MSG: &str = "this personal relay only accepts events related to its users";
let input = msg.as_bytes(); let input = msg.as_bytes();
eat_whitespace(input, &mut inpos); eat_whitespace(input, &mut inpos);
@ -123,54 +125,61 @@ impl WebSocketService {
// Read the event into the session buffer // Read the event into the session buffer
let (_incount, event) = Event::from_json(&input[inpos..], &mut self.buffer)?; let (_incount, event) = Event::from_json(&input[inpos..], &mut self.buffer)?;
let id = event.id();
let reply = match self.event_inner().await {
Ok(()) => NostrReply::Ok(id, true, NostrReplyPrefix::None, "".to_string()),
Err(e) => match e.inner {
ChorusError::AuthRequired => NostrReply::Ok(
id,
false,
NostrReplyPrefix::AuthRequired,
PERSONAL_MSG.to_owned(),
),
ChorusError::Duplicate => {
NostrReply::Ok(id, false, NostrReplyPrefix::Duplicate, "".to_string())
}
ChorusError::EventIsInvalid(why) => {
NostrReply::Ok(id, false, NostrReplyPrefix::Invalid, why)
}
ChorusError::Restricted => NostrReply::Ok(
id,
false,
NostrReplyPrefix::Restricted,
PERSONAL_MSG.to_owned(),
),
_ => NostrReply::Ok(id, false, NostrReplyPrefix::Error, format!("{}", e)),
},
};
self.websocket.send(Message::text(reply.as_json())).await?;
Ok(())
}
async fn event_inner(&mut self) -> Result<(), Error> {
// Delineate the event back out of the session buffer
let event = Event::delineate(&self.buffer)?;
if GLOBALS.config.read().await.verify_events { if GLOBALS.config.read().await.verify_events {
// Verify the event is valid (id is hash, signature is valid) // Verify the event is valid (id is hash, signature is valid)
if let Err(e) = event.verify() { if let Err(e) = event.verify() {
let reply = NostrReply::Ok( return Err(ChorusError::EventIsInvalid(format!("{}", e)).into());
event.id(),
false,
NostrReplyPrefix::Invalid,
format!("{}", e),
);
self.websocket.send(Message::text(reply.as_json())).await?;
return Ok(());
} }
} }
// Screen the event to see if we are willing to accept it // Screen the event to see if we are willing to accept it
if !screen_event(&event, self.user).await? { if !screen_event(&event, self.user).await? {
let prefix = if self.user.is_some() { if self.user.is_some() {
NostrReplyPrefix::Restricted return Err(ChorusError::Restricted.into());
} else { } else {
NostrReplyPrefix::AuthRequired return Err(ChorusError::AuthRequired.into());
}; }
let reply = NostrReply::Ok(
event.id(),
false,
prefix,
"this personal relay only accepts events related to its users".to_owned(),
);
self.websocket.send(Message::text(reply.as_json())).await?;
return Ok(());
} }
// Store and index the event // Store and index the event
let reply = match GLOBALS.store.get().unwrap().store_event(&event) { let offset = GLOBALS.store.get().unwrap().store_event(&event)?;
Ok(offset) => { GLOBALS.new_events.send(offset)?; // advertise the new event
GLOBALS.new_events.send(offset)?; // advertise the new event
NostrReply::Ok(event.id(), true, NostrReplyPrefix::None, "".to_owned())
}
Err(e) => {
if matches!(e.inner, ChorusError::Duplicate) {
NostrReply::Ok(event.id(), true, NostrReplyPrefix::Duplicate, "".to_owned())
} else {
NostrReply::Ok(event.id(), false, NostrReplyPrefix::Error, format!("{e}"))
}
}
};
self.websocket.send(Message::text(reply.as_json())).await?;
Ok(()) Ok(())
} }