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