25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 1 | use std::collections::{BTreeSet, HashMap}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 2 | use std::net::SocketAddr; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 3 | use std::sync::Arc; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 4 | use std::time::Duration; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 5 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 6 | use anyhow::{Context as _, Result}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 7 | use futures_util::{Sink, SinkExt as _, StreamExt as _}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 8 | use nostr::event::Event; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 9 | use nostr::filter::{Filter, MatchEventOptions}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 10 | use nostr_database::{NostrDatabase as _, RejectedReason, SaveEventStatus}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 11 | use serde_json::{Value, json}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 12 | use tokio::io::{AsyncRead, AsyncWrite}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 13 | use tokio::sync::broadcast; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 14 | use tokio::time::timeout; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 15 | use tokio_tungstenite::WebSocketStream; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 16 | use tokio_tungstenite::tungstenite::{ |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 17 | Error as WebSocketError, Message, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 18 | protocol::{Role, WebSocketConfig}, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 19 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 20 | use tokio_util::sync::CancellationToken; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 21 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 22 | use crate::config::Limits; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 23 | use crate::policy::{Policy, QueryPolicyResult, WritePolicyResult}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 24 | use crate::store::Store; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 25 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 26 | #[derive(Clone)] |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 27 | pub struct Relay { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 28 | store: Arc<Store>, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 29 | policy: Policy, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 30 | limits: Limits, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 31 | events: broadcast::Sender<Event>, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 32 | stop: CancellationToken, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 33 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 34 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 35 | struct Subscription { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 36 | filters: Vec<Filter>, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 37 | bytes: usize, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 38 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 39 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 40 | #[derive(Default)] |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 41 | struct Session { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 42 | subscriptions: HashMap<String, Subscription>, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 43 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 44 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 45 | impl Relay { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 46 | pub(crate) fn new( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 47 | store: Arc<Store>, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 48 | policy: Policy, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 49 | limits: Limits, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 50 | stop: CancellationToken, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 51 | ) -> Self { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 52 | let (events, _) = broadcast::channel(limits.max_connections.max(32)); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 53 | Self { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 54 | store, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 55 | policy, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 56 | limits, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 57 | events, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 58 | stop, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 59 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 60 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 61 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 62 | pub(crate) async fn add_event(&self, event: Event) -> Result<SaveEventStatus> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 63 | event.verify().context("invalid event")?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 64 | let status = self.store.save_event(&event).await?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 65 | if status.is_success() { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 66 | match self.events.send(event) { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 67 | Ok(_) | Err(_) => {} |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 68 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 69 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 70 | Ok(status) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 71 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 72 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 73 | pub(crate) async fn take_connection<S>(&self, stream: S, address: SocketAddr) -> Result<()> |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 74 | where |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 75 | S: AsyncRead + AsyncWrite + Unpin, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 76 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 77 | let websocket = WebSocketStream::from_raw_socket( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 78 | stream, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 79 | Role::Server, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 80 | Some( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 81 | WebSocketConfig::default() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 82 | .max_message_size(Some(self.limits.max_message_bytes)) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 83 | .max_frame_size(Some(self.limits.max_message_bytes)), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 84 | ), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 85 | ) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 86 | .await; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 87 | let (mut sink, mut source) = websocket.split(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 88 | let mut events = self.events.subscribe(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 89 | let mut session = Session::default(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 90 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 91 | loop { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 92 | tokio::select! { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 93 | biased; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 94 | () = self.stop.cancelled() => return Ok(()), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 95 | received = source.next() => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 96 | let Some(message) = received else { return Ok(()); }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 97 | match message? { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 98 | Message::Text(text) => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 99 | for response in self.handle_text(&mut session, address, &text).await { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 100 | self.send(&mut sink, response).await?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 101 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 102 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 103 | Message::Ping(payload) => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 104 | self.send_message(&mut sink, Message::Pong(payload)).await?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 105 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 106 | Message::Close(_) => return Ok(()), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 107 | Message::Binary(_) | Message::Pong(_) | Message::Frame(_) => {} |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 108 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 109 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 110 | received = events.recv() => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 111 | let event = received.context("live event stream lagged")?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 112 | let ids = session.subscriptions.iter() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 113 | .filter(|entry| entry.1.filters.iter().any(|filter| filter.match_event(&event, MatchEventOptions::new()))) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 114 | .map(|entry| entry.0.clone()) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 115 | .collect::<Vec<_>>(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 116 | for id in ids { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 117 | self.send(&mut sink, json!(["EVENT", id, event])).await?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 118 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 119 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 120 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 121 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 122 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 123 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 124 | async fn send<S>(&self, sink: &mut S, value: Value) -> Result<()> |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 125 | where |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 126 | S: Sink<Message, Error = WebSocketError> + Unpin, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 127 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 128 | self.send_message(sink, Message::Text(value.to_string().into())) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 129 | .await |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 130 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 131 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 132 | async fn send_message<S>(&self, sink: &mut S, message: Message) -> Result<()> |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 133 | where |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 134 | S: Sink<Message, Error = WebSocketError> + Unpin, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 135 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 136 | timeout( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 137 | Duration::from_secs(self.limits.write_timeout_seconds), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 138 | sink.send(message), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 139 | ) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 140 | .await |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 141 | .context("websocket write timed out")??; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 142 | Ok(()) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 143 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 144 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 145 | async fn handle_text( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 146 | &self, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 147 | session: &mut Session, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 148 | address: SocketAddr, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 149 | text: &str, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 150 | ) -> Vec<Value> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 151 | let Ok(value) = serde_json::from_str::<Value>(text) else { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 152 | return vec![json!(["NOTICE", "invalid json"])]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 153 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 154 | let Some(frame) = value.as_array() else { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 155 | return vec![json!(["NOTICE", "message must be an array"])]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 156 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 157 | match frame.first().and_then(Value::as_str) { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 158 | Some("EVENT") => vec![self.handle_event(frame, address).await], |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 159 | Some("REQ") => self.handle_req(session, frame, address, text.len()).await, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 160 | Some("CLOSE") => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 161 | if let Some(id) = frame.get(1).and_then(Value::as_str) { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 162 | session.subscriptions.remove(id); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 163 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 164 | Vec::new() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 165 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 166 | _ => vec![json!(["NOTICE", "unsupported message"])], |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 167 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 168 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 169 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 170 | async fn handle_event(&self, frame: &[Value], address: SocketAddr) -> Value { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 171 | let Some(value) = frame.get(1) else { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 172 | return json!(["NOTICE", "EVENT requires an event"]); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 173 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 174 | let id = value |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 175 | .get("id") |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 176 | .and_then(Value::as_str) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 177 | .unwrap_or_default() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 178 | .to_owned(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 179 | let Ok(event) = serde_json::from_value::<Event>(value.clone()) else { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 180 | return json!(["OK", id, false, "invalid: malformed event"]); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 181 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 182 | if event.verify().is_err() { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 183 | return json!(["OK", event.id, false, "invalid: bad id or signature"]); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 184 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 185 | match self.policy.admit_event(&event, &address).await { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 186 | WritePolicyResult::Accept => {} |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 187 | WritePolicyResult::Respond { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 188 | prefix, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 189 | message, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 190 | status, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 191 | } => return json!(["OK", event.id, status, format!("{prefix}: {message}")]), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 192 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 193 | let event_id = event.id; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 194 | match self.add_event(event).await { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 195 | Ok(SaveEventStatus::Success) => json!(["OK", event_id, true, ""]), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 196 | Ok(SaveEventStatus::Rejected(RejectedReason::Duplicate)) => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 197 | json!(["OK", event_id, true, "duplicate: already stored"]) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 198 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 199 | Ok(SaveEventStatus::Rejected(reason)) => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 200 | json!(["OK", event_id, false, format!("blocked: {reason:?}")]) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 201 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 202 | Err(error) => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 203 | tracing::error!(event = %event_id, %error, "event storage failed"); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 204 | json!(["OK", event_id, false, "error: database unavailable"]) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 205 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 206 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 207 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 208 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 209 | async fn handle_req( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 210 | &self, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 211 | session: &mut Session, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 212 | frame: &[Value], |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 213 | address: SocketAddr, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 214 | request_bytes: usize, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 215 | ) -> Vec<Value> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 216 | let Some(id) = frame.get(1).and_then(Value::as_str) else { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 217 | return vec![json!(["NOTICE", "REQ requires a subscription id"])]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 218 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 219 | if id.len() > 64 { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 220 | return vec![closed(id, "blocked: subscription id is too long")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 221 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 222 | if frame.len() < 3 { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 223 | return vec![closed(id, "blocked: REQ requires a filter")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 224 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 225 | if frame.len() - 2 > self.limits.max_filters { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 226 | return vec![closed(id, "rate-limited: too many filters")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 227 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 228 | if session.subscriptions.len() >= self.limits.max_subscriptions |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 229 | && !session.subscriptions.contains_key(id) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 230 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 231 | return vec![closed(id, "rate-limited: too many subscriptions")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 232 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 233 | let old_bytes = session.subscriptions.get(id).map_or(0, |entry| entry.bytes); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 234 | let active_bytes = session |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 235 | .subscriptions |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 236 | .values() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 237 | .map(|entry| entry.bytes) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 238 | .sum::<usize>() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 239 | .saturating_sub(old_bytes); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 240 | let max_subscription_bytes = self |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 241 | .limits |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 242 | .max_filter_bytes |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 243 | .saturating_mul(self.limits.max_subscriptions); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 244 | if active_bytes.saturating_add(request_bytes) > max_subscription_bytes { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 245 | return vec![closed(id, "rate-limited: subscription byte limit exceeded")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 246 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 247 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 248 | let mut filters = Vec::with_capacity(frame.len() - 2); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 249 | for value in &frame[2..] { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 250 | let Ok(filter) = serde_json::from_value::<Filter>(value.clone()) else { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 251 | return vec![closed(id, "blocked: invalid filter")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 252 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 253 | match self.policy.admit_query(&filter, &address).await { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 254 | QueryPolicyResult::Accept => filters.push(filter), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 255 | QueryPolicyResult::Reject { prefix, message } => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 256 | return vec![closed(id, &format!("{prefix}: {message}"))]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 257 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 258 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 259 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 260 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 261 | let mut events = BTreeSet::new(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 262 | for filter in &filters { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 263 | let implicit = filter.limit.is_none(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 264 | let limit = filter.limit.unwrap_or(self.limits.max_query_results); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 265 | let mut query = filter.clone(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 266 | query.limit = Some(if implicit { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 267 | limit.saturating_add(1) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 268 | } else { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 269 | limit |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 270 | }); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 271 | let found = match self.store.query(query).await { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 272 | Ok(found) => found, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 273 | Err(error) => { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 274 | let message = if error.to_string().contains("history byte limit exceeded") { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 275 | "rate-limited: history byte limit exceeded" |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 276 | } else { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 277 | tracing::error!(%error, "history query failed"); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 278 | "error: database unavailable" |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 279 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 280 | return vec![closed(id, message)]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 281 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 282 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 283 | if implicit && found.len() > limit { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 284 | return vec![closed(id, "rate-limited: history result limit exceeded")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 285 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 286 | events.extend(found); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 287 | if events.len() > self.limits.max_query_results { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 288 | return vec![closed(id, "rate-limited: too many results")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 289 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 290 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 291 | let bytes = events |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 292 | .iter() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 293 | .map(|event| event.as_json().len()) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 294 | .sum::<usize>(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 295 | if bytes > self.limits.max_query_bytes { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 296 | return vec![closed(id, "rate-limited: history byte limit exceeded")]; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 297 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 298 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 299 | let mut response = Vec::with_capacity(events.len() + 1); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 300 | for event in events { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 301 | if !event.is_expired() { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 302 | response.push(json!(["EVENT", id, event])); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 303 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 304 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 305 | response.push(json!(["EOSE", id])); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 306 | session.subscriptions.insert( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 307 | id.to_owned(), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 308 | Subscription { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 309 | filters, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 310 | bytes: request_bytes, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 311 | }, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 312 | ); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 313 | response |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 314 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 315 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 316 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 317 | fn closed(id: &str, message: &str) -> Value { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 318 | json!(["CLOSED", id, message]) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 319 | } |