mirror of
https://github.com/mikedilger/chorus.git
synced 2026-08-01 07:21:39 +00:00
Fix: errors related to strings not json-escaped
This commit is contained in:
parent
c03da62981
commit
97c040b01c
16
src/lib.rs
16
src/lib.rs
@ -374,7 +374,7 @@ impl WebSocketService {
|
||||
if m.len() > self.burst_tokens {
|
||||
log::info!(target: "Client", "{}: Rate limited exceeded", self.peer);
|
||||
let reply = NostrReply::Notice("Rate limit exceeded.".into());
|
||||
self.websocket.send(Message::text(reply.as_json())).await?;
|
||||
self.websocket.send(Message::text(reply.as_json()?)).await?;
|
||||
let error = ChorusError::RateLimitExceeded;
|
||||
self.error_punishment += error.punishment();
|
||||
return Err(error.into());
|
||||
@ -419,7 +419,7 @@ impl WebSocketService {
|
||||
|
||||
// Offer AUTH to clients right off the bat
|
||||
let reply = NostrReply::Auth(self.challenge.clone());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
|
||||
let mut last_message_at = Instant::now();
|
||||
|
||||
@ -493,7 +493,7 @@ impl WebSocketService {
|
||||
let message = NostrReply::Event(subid, event);
|
||||
// note, this is not currently counted in throttling
|
||||
self.websocket
|
||||
.send(Message::text(message.as_json()))
|
||||
.send(Message::text(message.as_json()?))
|
||||
.await?;
|
||||
continue 'subs;
|
||||
}
|
||||
@ -529,7 +529,7 @@ impl WebSocketService {
|
||||
if message.len() > self.burst_tokens {
|
||||
log::info!(target: "Client", "{}: Rate limited exceeded", self.peer);
|
||||
let reply = NostrReply::Notice("Rate limit exceeded.".into());
|
||||
self.websocket.send(Message::text(reply.as_json())).await?;
|
||||
self.websocket.send(Message::text(reply.as_json()?)).await?;
|
||||
let error = ChorusError::RateLimitExceeded;
|
||||
self.error_punishment += error.punishment();
|
||||
return Err(error.into());
|
||||
@ -556,15 +556,15 @@ impl WebSocketService {
|
||||
if !self.replied {
|
||||
if let Some(subid) = &self.negentropy_sub {
|
||||
let reply = NostrReply::NegErr(subid, format!("error: {e}"));
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
} else {
|
||||
let reply = NostrReply::Notice(format!("error: {}", e.inner));
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
}
|
||||
}
|
||||
if self.error_punishment >= 1.0 {
|
||||
let reply = NostrReply::Notice("Closing due to error(s)".into());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Err(ChorusError::ErrorClose.into());
|
||||
}
|
||||
}
|
||||
@ -573,7 +573,7 @@ impl WebSocketService {
|
||||
let reply = NostrReply::Notice(
|
||||
"binary messages are not processed by this relay".to_owned(),
|
||||
);
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
log::info!(target: "Client",
|
||||
"{}: Received unhandled binary message: {:02X?}",
|
||||
self.peer,
|
||||
|
||||
48
src/nostr.rs
48
src/nostr.rs
@ -46,7 +46,7 @@ impl WebSocketService {
|
||||
} else {
|
||||
log::warn!(target: "Client", "{}: Received unhandled text message: {}", self.peer, msg);
|
||||
let reply = NostrReply::Notice("Command unrecognized".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -106,7 +106,7 @@ impl WebSocketService {
|
||||
}
|
||||
_ => NostrReply::Closed(&subid, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
||||
};
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
@ -141,7 +141,7 @@ impl WebSocketService {
|
||||
NostrReplyPrefix::AuthRequired,
|
||||
"DM kinds were included in the filters".to_owned(),
|
||||
);
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@ -199,11 +199,11 @@ impl WebSocketService {
|
||||
}
|
||||
}
|
||||
let reply = NostrReply::Count(subid, events.len(), opthll);
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
} else {
|
||||
for event in events.drain(..) {
|
||||
let reply = NostrReply::Event(subid, event);
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
}
|
||||
|
||||
// New policy Feb 2025: Redactions trigger a "CLOSED: auth-required" because
|
||||
@ -216,18 +216,18 @@ impl WebSocketService {
|
||||
NostrReplyPrefix::AuthRequired,
|
||||
"At least one matching event requires AUTH".to_owned(),
|
||||
);
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if completes {
|
||||
// Closed
|
||||
let reply = NostrReply::Closed(subid, NostrReplyPrefix::None, "".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
} else {
|
||||
// EOSE
|
||||
let reply = NostrReply::Eose(subid);
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -307,11 +307,11 @@ impl WebSocketService {
|
||||
},
|
||||
_ => NostrReply::Ok(id, false, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
||||
};
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
Err(e)
|
||||
} else {
|
||||
let reply = NostrReply::Ok(id, true, NostrReplyPrefix::None, "".to_string());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -388,7 +388,7 @@ impl WebSocketService {
|
||||
// message, and clients just presume it was closed.
|
||||
/*
|
||||
let reply = NostrReply::Closed(subid, NostrReplyPrefix::None, "".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
*/
|
||||
|
||||
Ok(())
|
||||
@ -416,11 +416,11 @@ impl WebSocketService {
|
||||
}
|
||||
_ => NostrReply::Ok(id, false, NostrReplyPrefix::Error, format!("{}", e.inner)),
|
||||
};
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
Err(e)
|
||||
} else {
|
||||
let reply = NostrReply::Ok(id, true, NostrReplyPrefix::None, "".to_string());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -499,7 +499,7 @@ impl WebSocketService {
|
||||
if !GLOBALS.config.read().enable_negentropy {
|
||||
let reply =
|
||||
NostrReply::NegErr(&subid, "blocked: Negentropy sync is disabled".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -533,14 +533,14 @@ impl WebSocketService {
|
||||
// NEG-ERR if the message was empty
|
||||
if incoming_msg.is_empty() {
|
||||
let reply = NostrReply::NegErr(&subid, "error: Empty negentropy message".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If the version is too high, respond with our version number
|
||||
if incoming_msg[0] != 0x61 {
|
||||
let reply = NostrReply::NegMsg(&subid, vec![0x61]);
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -594,11 +594,11 @@ impl WebSocketService {
|
||||
match neg.reconcile(&Bytes::from(incoming_msg)) {
|
||||
Ok(response) => {
|
||||
let reply = NostrReply::NegMsg(&subid, response.as_bytes().to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
}
|
||||
Err(e) => {
|
||||
let reply = NostrReply::NegErr(&subid, format!("{e}"));
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -634,7 +634,7 @@ impl WebSocketService {
|
||||
if !GLOBALS.config.read().enable_negentropy {
|
||||
let reply =
|
||||
NostrReply::NegErr(&subid, "blocked: Negentropy sync is disabled".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -656,7 +656,7 @@ impl WebSocketService {
|
||||
// NEG-ERR if the message was empty
|
||||
if incoming_msg.is_empty() {
|
||||
let reply = NostrReply::NegErr(&subid, "error: Empty negentropy message".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -664,14 +664,14 @@ impl WebSocketService {
|
||||
// have already happened in NEG-OPEN)
|
||||
if incoming_msg[0] != 0x61 {
|
||||
let reply = NostrReply::NegErr(&subid, "Version mismatch".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Look up the events we have
|
||||
let Some(nsv) = self.neg_subscriptions.get(&subid) else {
|
||||
let reply = NostrReply::NegErr(&subid, "Subscription not found".to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@ -679,11 +679,11 @@ impl WebSocketService {
|
||||
match neg.reconcile(&Bytes::from(incoming_msg)) {
|
||||
Ok(response) => {
|
||||
let reply = NostrReply::NegMsg(&subid, response.as_bytes().to_owned());
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
}
|
||||
Err(e) => {
|
||||
let reply = NostrReply::NegErr(&subid, format!("{e}"));
|
||||
self.send(Message::text(reply.as_json())).await?;
|
||||
self.send(Message::text(reply.as_json()?)).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
52
src/reply.rs
52
src/reply.rs
@ -1,3 +1,4 @@
|
||||
use crate::Error;
|
||||
use pocket_types::{write_hex, Event, Hll8, Id};
|
||||
use std::fmt;
|
||||
|
||||
@ -45,40 +46,59 @@ pub enum NostrReply<'a> {
|
||||
}
|
||||
|
||||
impl NostrReply<'_> {
|
||||
pub fn as_json(&self) -> String {
|
||||
match self {
|
||||
NostrReply::Auth(challenge) => format!(r#"["AUTH","{challenge}"]"#),
|
||||
NostrReply::Event(subid, event) => format!(r#"["EVENT","{subid}",{}]"#, event),
|
||||
NostrReply::Ok(id, ok, prefix, msg) => format!(r#"["OK","{id}",{ok},"{prefix}{msg}"]"#),
|
||||
NostrReply::Eose(subid) => format!(r#"["EOSE","{subid}"]"#),
|
||||
pub fn as_json(&self) -> Result<String, Error> {
|
||||
Ok(match self {
|
||||
NostrReply::Auth(challenge) => {
|
||||
let esc_challenge = escape(challenge)?;
|
||||
format!(r#"["AUTH","{esc_challenge}"]"#)
|
||||
}
|
||||
NostrReply::Event(subid, event) => {
|
||||
let esc_subid = escape(subid)?;
|
||||
format!(r#"["EVENT","{esc_subid}",{event}]"#)
|
||||
}
|
||||
NostrReply::Ok(id, ok, prefix, msg) => {
|
||||
let esc_msg = escape(msg)?;
|
||||
format!(r#"["OK","{id}",{ok},"{prefix}{esc_msg}"]"#)
|
||||
}
|
||||
NostrReply::Eose(subid) => {
|
||||
let esc_subid = escape(subid)?;
|
||||
format!(r#"["EOSE","{esc_subid}"]"#)
|
||||
}
|
||||
NostrReply::Closed(subid, prefix, msg) => {
|
||||
format!(r#"["CLOSED","{subid}","{prefix}{msg}"]"#)
|
||||
}
|
||||
NostrReply::Notice(msg) => format!(r#"["NOTICE","{msg}"]"#),
|
||||
NostrReply::Notice(msg) => {
|
||||
let esc_msg = escape(msg)?;
|
||||
format!(r#"["NOTICE","{esc_msg}"]"#)
|
||||
}
|
||||
NostrReply::Count(subid, c, opthll) => {
|
||||
let esc_subid = escape(subid)?;
|
||||
if let Some(hll) = opthll {
|
||||
let hll = hll.to_hex_string();
|
||||
format!(r#"["COUNT","{subid}",{{"count":{c}, "hll":"{hll}"}}]"#)
|
||||
format!(r#"["COUNT","{esc_subid}",{{"count":{c}, "hll":"{hll}"}}]"#)
|
||||
} else {
|
||||
format!(r#"["COUNT","{subid}",{{"count":{c}}}]"#)
|
||||
format!(r#"["COUNT","{esc_subid}",{{"count":{c}}}]"#)
|
||||
}
|
||||
}
|
||||
NostrReply::NegErr(subid, reason) => {
|
||||
format!(r#"["NEG-ERR","{subid}","{reason}"]"#)
|
||||
let esc_subid = escape(subid)?;
|
||||
let esc_reason = escape(reason)?;
|
||||
format!(r#"["NEG-ERR","{esc_subid}","{esc_reason}"]"#)
|
||||
}
|
||||
NostrReply::NegMsg(subid, msg) => {
|
||||
let esc_subid = escape(subid)?;
|
||||
// write msg as hex
|
||||
let mut buf: Vec<u8> = vec![0; msg.len() * 2];
|
||||
write_hex!(msg, &mut buf, msg.len()).unwrap();
|
||||
let msg_hex = unsafe { std::str::from_utf8_unchecked(&buf) };
|
||||
format!(r#"["NEG-MSG","{subid}","{}"]"#, msg_hex)
|
||||
format!(r#"["NEG-MSG","{esc_subid}","{}"]"#, msg_hex)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NostrReply<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.as_json())
|
||||
}
|
||||
fn escape(s: &str) -> Result<String, Error> {
|
||||
let v: Vec<u8> = Vec::with_capacity(256);
|
||||
let e = pocket_types::json::json_escape(s.as_bytes(), v)?;
|
||||
Ok(unsafe { String::from_utf8_unchecked(e) })
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user