message.rs (9172B)
1 use crate::{Error, Result}; 2 use ewebsock::{WsEvent, WsMessage}; 3 4 #[derive(Debug, Eq, PartialEq)] 5 pub struct CommandResult<'a> { 6 event_id: &'a str, 7 status: bool, 8 message: &'a str, 9 } 10 11 #[derive(Debug, Eq, PartialEq)] 12 pub enum RelayMessage<'a> { 13 OK(CommandResult<'a>), 14 Eose(&'a str), 15 Event(&'a str, &'a str), 16 Notice(&'a str), 17 } 18 19 #[derive(Debug)] 20 pub enum RelayEvent<'a> { 21 Opened, 22 Closed, 23 Other(&'a WsMessage), 24 Error(Error), 25 Message(RelayMessage<'a>), 26 } 27 28 impl<'a> From<&'a WsEvent> for RelayEvent<'a> { 29 fn from(event: &'a WsEvent) -> RelayEvent<'a> { 30 match event { 31 WsEvent::Opened => RelayEvent::Opened, 32 WsEvent::Closed => RelayEvent::Closed, 33 WsEvent::Message(ref ws_msg) => ws_msg.into(), 34 WsEvent::Error(s) => RelayEvent::Error(Error::Generic(s.to_owned())), 35 } 36 } 37 } 38 39 impl<'a> From<&'a WsMessage> for RelayEvent<'a> { 40 fn from(wsmsg: &'a WsMessage) -> RelayEvent<'a> { 41 match wsmsg { 42 WsMessage::Text(s) => match RelayMessage::from_json(s).map(RelayEvent::Message) { 43 Ok(msg) => msg, 44 Err(err) => RelayEvent::Error(err), 45 }, 46 wsmsg => RelayEvent::Other(wsmsg), 47 } 48 } 49 } 50 51 impl<'a> RelayMessage<'a> { 52 pub fn eose(subid: &'a str) -> Self { 53 RelayMessage::Eose(subid) 54 } 55 56 pub fn notice(msg: &'a str) -> Self { 57 RelayMessage::Notice(msg) 58 } 59 60 pub fn ok(event_id: &'a str, status: bool, message: &'a str) -> Self { 61 RelayMessage::OK(CommandResult { 62 event_id, 63 status, 64 message, 65 }) 66 } 67 68 pub fn event(ev: &'a str, sub_id: &'a str) -> Self { 69 RelayMessage::Event(sub_id, ev) 70 } 71 72 pub fn from_json(msg: &'a str) -> Result<RelayMessage<'a>> { 73 if msg.is_empty() { 74 return Err(Error::Empty); 75 } 76 77 // Notice 78 // Relay response format: ["NOTICE", <message>] 79 if msg.len() >= 12 && &msg[0..=9] == "[\"NOTICE\"," { 80 // TODO: there could be more than one space, whatever 81 let start = if msg.as_bytes().get(10).copied() == Some(b' ') { 82 12 83 } else { 84 11 85 }; 86 let end = msg.len() - 2; 87 return Ok(Self::notice(&msg[start..end])); 88 } 89 90 // Event 91 // Relay response format: ["EVENT", <subscription id>, <event JSON>] 92 if &msg[0..=7] == "[\"EVENT\"" { 93 let mut start = 9; 94 while let Some(&b' ') = msg.as_bytes().get(start) { 95 start += 1; // Move past optional spaces 96 } 97 if let Some(comma_index) = msg[start..].find(',') { 98 let subid_end = start + comma_index; 99 let subid = &msg[start..subid_end].trim().trim_matches('"'); 100 return Ok(Self::event(msg, subid)); 101 } else { 102 return Ok(Self::event(msg, "fixme")); 103 } 104 } 105 106 // EOSE (NIP-15) 107 // Relay response format: ["EOSE", <subscription_id>] 108 if &msg[0..=7] == "[\"EOSE\"," { 109 let start = if msg.as_bytes().get(8).copied() == Some(b' ') { 110 10 111 } else { 112 9 113 }; 114 let end = msg.len() - 2; 115 return Ok(Self::eose(&msg[start..end])); 116 } 117 118 // OK (NIP-20) 119 // Relay response format: ["OK",<event_id>, <true|false>, <message>] 120 if &msg[0..=5] == "[\"OK\"," && msg.len() >= 78 { 121 // TODO: fix this 122 let event_id = &msg[7..71]; 123 let booly = &msg[73..77]; 124 let status: bool = if booly == "true" { 125 true 126 } else if booly == "false" { 127 false 128 } else { 129 return Err(Error::DecodeFailed); 130 }; 131 132 return Ok(Self::ok(event_id, status, "fixme")); 133 } 134 135 Err(Error::DecodeFailed) 136 } 137 } 138 139 #[cfg(test)] 140 mod tests { 141 use super::*; 142 143 #[test] 144 fn test_handle_valid_notice() -> Result<()> { 145 let valid_notice_msg = r#"["NOTICE","Invalid event format!"]"#; 146 let handled_valid_notice_msg = RelayMessage::notice("Invalid event format!"); 147 148 assert_eq!( 149 RelayMessage::from_json(valid_notice_msg)?, 150 handled_valid_notice_msg 151 ); 152 153 Ok(()) 154 } 155 #[test] 156 fn test_handle_invalid_notice() { 157 //Missing content 158 let invalid_notice_msg = r#"["NOTICE"]"#; 159 //The content is not string 160 let invalid_notice_msg_content = r#"["NOTICE": 404]"#; 161 162 assert!(matches!( 163 RelayMessage::from_json(invalid_notice_msg).unwrap_err(), 164 Error::DecodeFailed 165 )); 166 assert!(matches!( 167 RelayMessage::from_json(invalid_notice_msg_content).unwrap_err(), 168 Error::DecodeFailed 169 )); 170 } 171 172 /* 173 #[test] 174 fn test_handle_valid_event() -> Result<()> { 175 use tracing::debug; 176 177 let valid_event_msg = r#"["EVENT", "random_string", {"id":"70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5","pubkey":"379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe","created_at":1612809991,"kind":1,"tags":[],"content":"test","sig":"273a9cd5d11455590f4359500bccb7a89428262b96b3ea87a756b770964472f8c3e87f5d5e64d8d2e859a71462a3f477b554565c4f2f326cb01dd7620db71502"}]"#; 178 179 let id = "70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5"; 180 let pubkey = "379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe"; 181 let created_at = 1612809991; 182 let kind = 1; 183 let tags = vec![]; 184 let content = "test"; 185 let sig = "273a9cd5d11455590f4359500bccb7a89428262b96b3ea87a756b770964472f8c3e87f5d5e64d8d2e859a71462a3f477b554565c4f2f326cb01dd7620db71502"; 186 187 let handled_event = Note::new_dummy(id, pubkey, created_at, kind, tags, content, sig).expect("ev"); 188 debug!("event {:?}", handled_event); 189 190 let msg = RelayMessage::from_json(valid_event_msg).expect("valid json"); 191 debug!("msg {:?}", msg); 192 193 let note_json = serde_json::to_string(&handled_event).expect("json ev"); 194 195 assert_eq!( 196 msg, 197 RelayMessage::event(¬e_json, "random_string") 198 ); 199 200 Ok(()) 201 } 202 203 #[test] 204 fn test_handle_invalid_event() { 205 //Mising Event field 206 let invalid_event_msg = r#"["EVENT","random_string"]"#; 207 //Event JSON with incomplete content 208 let invalid_event_msg_content = r#"["EVENT","random_string",{"id":"70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5","pubkey":"379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe"}]"#; 209 210 assert!(matches!( 211 RelayMessage::from_json(invalid_event_msg).unwrap_err(), 212 Error::DecodeFailed 213 )); 214 215 assert!(matches!( 216 RelayMessage::from_json(invalid_event_msg_content).unwrap_err(), 217 Error::DecodeFailed 218 )); 219 } 220 */ 221 222 #[test] 223 fn test_handle_valid_eose() -> Result<()> { 224 let valid_eose_msg = r#"["EOSE","random-subscription-id"]"#; 225 let handled_valid_eose_msg = RelayMessage::eose("random-subscription-id"); 226 227 assert_eq!( 228 RelayMessage::from_json(valid_eose_msg)?, 229 handled_valid_eose_msg 230 ); 231 232 Ok(()) 233 } 234 235 // TODO: fix these tests 236 /* 237 #[test] 238 fn test_handle_invalid_eose() { 239 // Missing subscription ID 240 assert!(matches!( 241 RelayMessage::from_json(r#"["EOSE"]"#).unwrap_err(), 242 Error::DecodeFailed 243 )); 244 245 // The subscription ID is not string 246 assert!(matches!( 247 RelayMessage::from_json(r#"["EOSE",404]"#).unwrap_err(), 248 Error::DecodeFailed 249 )); 250 } 251 252 #[test] 253 fn test_handle_valid_ok() -> Result<()> { 254 let valid_ok_msg = r#"["OK","b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30",true,"pow: difficulty 25>=24"]"#; 255 let handled_valid_ok_msg = RelayMessage::ok( 256 "b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30", 257 true, 258 "pow: difficulty 25>=24".into(), 259 ); 260 261 assert_eq!(RelayMessage::from_json(valid_ok_msg)?, handled_valid_ok_msg); 262 263 Ok(()) 264 } 265 */ 266 267 #[test] 268 fn test_handle_invalid_ok() { 269 // Missing params 270 assert!(matches!( 271 RelayMessage::from_json( 272 r#"["OK","b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30"]"# 273 ) 274 .unwrap_err(), 275 Error::DecodeFailed 276 )); 277 278 // Invalid status 279 assert!( 280 matches!(RelayMessage::from_json(r#"["OK","b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30",hello,""]"#).unwrap_err(), 281 Error::DecodeFailed) 282 ); 283 284 // Invalid message 285 assert!( 286 matches!(RelayMessage::from_json(r#"["OK","b1a649ebe8b435ec71d3784793f3bbf4b93e64e17568a741aecd4c7ddeafce30",hello,404]"#).unwrap_err(), 287 Error::DecodeFailed) 288 ); 289 } 290 }