mirror of
https://github.com/scsibug/nostr-rs-relay.git
synced 2024-11-22 09:09:07 -05:00
feat: reject events that are too large
A new configuration setting controls the maximum size of event messages, and sends a notice to the client if they exceed it. Fixes https://todo.sr.ht/~gheartsfield/nostr-rs-relay/14
This commit is contained in:
parent
8f3891c781
commit
4171a8870e
12
config.toml
12
config.toml
|
@ -3,7 +3,7 @@
|
||||||
# Directory for SQLite files. Defaults to the current directory. Can
|
# Directory for SQLite files. Defaults to the current directory. Can
|
||||||
# also be specified (and overriden) with the "--db dirname" command
|
# also be specified (and overriden) with the "--db dirname" command
|
||||||
# line option.
|
# line option.
|
||||||
data_directory = "data"
|
data_directory = "."
|
||||||
|
|
||||||
[network]
|
[network]
|
||||||
# Bind to this network address
|
# Bind to this network address
|
||||||
|
@ -15,17 +15,21 @@ port = 8080
|
||||||
# Reject events that have timestamps greater than this many seconds in
|
# Reject events that have timestamps greater than this many seconds in
|
||||||
# the future. Defaults to rejecting anything greater than 30 minutes
|
# the future. Defaults to rejecting anything greater than 30 minutes
|
||||||
# from the current time.
|
# from the current time.
|
||||||
#reject_future_seconds = 1800
|
reject_future_seconds = 1800
|
||||||
|
|
||||||
[limits]
|
[limits]
|
||||||
# Limit events created per second, averaged over one minute. Must be
|
# Limit events created per second, averaged over one minute. Must be
|
||||||
# an integer. If not set (or set to 0), defaults to unlimited.
|
# an integer. If not set (or set to 0), defaults to unlimited.
|
||||||
messages_per_sec = 0
|
messages_per_sec = 0
|
||||||
|
|
||||||
# Maximum WebSocket message in bytes. Defaults to 128k.
|
# Limit the maximum size of an EVENT message. Defaults to 128 KB.
|
||||||
|
# Set to 0 for unlimited.
|
||||||
|
max_event_bytes = 131072
|
||||||
|
|
||||||
|
# Maximum WebSocket message in bytes. Defaults to 128 KB.
|
||||||
max_ws_message_bytes = 131072
|
max_ws_message_bytes = 131072
|
||||||
|
|
||||||
# Maximum WebSocket frame size in bytes. Defaults to 128k.
|
# Maximum WebSocket frame size in bytes. Defaults to 128 KB.
|
||||||
max_ws_frame_bytes = 131072
|
max_ws_frame_bytes = 131072
|
||||||
|
|
||||||
# Broadcast buffer size, in number of events. This prevents slow
|
# Broadcast buffer size, in number of events. This prevents slow
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub struct Retention {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct Limits {
|
pub struct Limits {
|
||||||
pub messages_per_sec: Option<u32>, // Artificially slow down event writing to limit disk consumption (averaged over 1 minute)
|
pub messages_per_sec: Option<u32>, // Artificially slow down event writing to limit disk consumption (averaged over 1 minute)
|
||||||
pub max_event_bytes: Option<usize>,
|
pub max_event_bytes: Option<usize>, // Maximum size of an EVENT message
|
||||||
pub max_ws_message_bytes: Option<usize>,
|
pub max_ws_message_bytes: Option<usize>,
|
||||||
pub max_ws_frame_bytes: Option<usize>,
|
pub max_ws_frame_bytes: Option<usize>,
|
||||||
pub broadcast_buffer: usize, // events to buffer for subscribers (prevents slow readers from consuming memory)
|
pub broadcast_buffer: usize, // events to buffer for subscribers (prevents slow readers from consuming memory)
|
||||||
|
|
|
@ -145,7 +145,7 @@ pub async fn db_writer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
if let Ok(_) = shutdown.try_recv() {
|
if shutdown.try_recv().is_ok() {
|
||||||
info!("shutting down database writer");
|
info!("shutting down database writer");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ pub enum Error {
|
||||||
CloseParseFailed,
|
CloseParseFailed,
|
||||||
#[error("Event validation failed")]
|
#[error("Event validation failed")]
|
||||||
EventInvalid,
|
EventInvalid,
|
||||||
|
#[error("Event too large")]
|
||||||
|
EventMaxLengthError(usize),
|
||||||
#[error("Subscription identifier max length exceeded")]
|
#[error("Subscription identifier max length exceeded")]
|
||||||
SubIdMaxLengthError,
|
SubIdMaxLengthError,
|
||||||
#[error("Maximum concurrent subscription count reached")]
|
#[error("Maximum concurrent subscription count reached")]
|
||||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -23,11 +23,9 @@ use tokio::sync::oneshot;
|
||||||
use tungstenite::protocol::WebSocketConfig;
|
use tungstenite::protocol::WebSocketConfig;
|
||||||
|
|
||||||
fn db_from_args(args: Vec<String>) -> Option<String> {
|
fn db_from_args(args: Vec<String>) -> Option<String> {
|
||||||
if args.len() == 3 {
|
if args.len() == 3 && args.get(1) == Some(&"--db".to_owned()) {
|
||||||
if args.get(1) == Some(&"--db".to_owned()) {
|
|
||||||
return args.get(2).map(|x| x.to_owned());
|
return args.get(2).map(|x| x.to_owned());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +42,7 @@ fn main() -> Result<(), Error> {
|
||||||
let mut c = config::Settings::new();
|
let mut c = config::Settings::new();
|
||||||
// update with database location
|
// update with database location
|
||||||
if let Some(db) = db_dir {
|
if let Some(db) = db_dir {
|
||||||
c.database.data_directory = db.to_owned();
|
c.database.data_directory = db;
|
||||||
}
|
}
|
||||||
*settings = c;
|
*settings = c;
|
||||||
}
|
}
|
||||||
|
@ -253,6 +251,10 @@ async fn nostr_server(
|
||||||
debug!("got connection close/error, disconnecting client: {}",cid);
|
debug!("got connection close/error, disconnecting client: {}",cid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Some(Err(Error::EventMaxLengthError(s))) => {
|
||||||
|
info!("client {} sent event larger ({} bytes) than max size", cid, s);
|
||||||
|
nostr_stream.send(NoticeRes("event exceeded max size".to_owned())).await.ok();
|
||||||
|
},
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
info!("got non-fatal error from client: {}, error: {:?}", cid, e);
|
info!("got non-fatal error from client: {}, error: {:?}", cid, e);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Nostr protocol layered over WebSocket
|
//! Nostr protocol layered over WebSocket
|
||||||
use crate::close::CloseCmd;
|
use crate::close::CloseCmd;
|
||||||
|
use crate::config;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::event::EventCmd;
|
use crate::event::EventCmd;
|
||||||
use crate::subscription::Subscription;
|
use crate::subscription::Subscription;
|
||||||
|
@ -51,14 +52,23 @@ pub fn wrap_ws_in_nostr(ws: WebSocketStream<TcpStream>) -> NostrStream {
|
||||||
impl Stream for NostrStream {
|
impl Stream for NostrStream {
|
||||||
type Item = Result<NostrMessage>;
|
type Item = Result<NostrMessage>;
|
||||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
// get the configuration
|
||||||
/// Convert Message to NostrMessage
|
/// Convert Message to NostrMessage
|
||||||
fn convert(msg: String) -> Result<NostrMessage> {
|
fn convert(msg: String) -> Result<NostrMessage> {
|
||||||
debug!("raw msg: {}", msg);
|
let config = config::SETTINGS.read().unwrap();
|
||||||
let event_size = msg.len();
|
|
||||||
debug!("event size is {} bytes", event_size);
|
|
||||||
let parsed_res: Result<NostrMessage> = serde_json::from_str(&msg).map_err(|e| e.into());
|
let parsed_res: Result<NostrMessage> = serde_json::from_str(&msg).map_err(|e| e.into());
|
||||||
match parsed_res {
|
match parsed_res {
|
||||||
Ok(m) => Ok(m),
|
Ok(m) => {
|
||||||
|
if let NostrMessage::EventMsg(_) = m {
|
||||||
|
if let Some(max_size) = config.limits.max_event_bytes {
|
||||||
|
// check length, ensure that some max size is set.
|
||||||
|
if msg.len() > max_size && max_size > 0 {
|
||||||
|
return Err(Error::EventMaxLengthError(msg.len()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(m)
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("proto parse error: {:?}", e);
|
debug!("proto parse error: {:?}", e);
|
||||||
Err(Error::ProtoParseError)
|
Err(Error::ProtoParseError)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user