| ... | @@ -1,8 +1,9 @@ | ... | @@ -1,8 +1,9 @@ |
| 1 | use super::super::{GONE, OPENING, Pipe, Shared, State}; | 1 | use super::super::super::{GONE, OPENING, Pipe, Shared, State}; |
| 2 | use super::*; | 2 | use super::*; |
| 3 | use std::{ | 3 | use std::{ |
| 4 | io::{Read, Write}, | 4 | io::{Read, Write}, |
| 5 | net::{Shutdown, TcpListener, TcpStream}, | 5 | net::{Shutdown, TcpListener, TcpStream}, |
| | 6 | sync::atomic::AtomicUsize, |
| 6 | }; | 7 | }; |
| 7 | | 8 | |
| 8 | struct Barrier { | 9 | struct Barrier { |
| ... | @@ -144,7 +145,7 @@ impl Handover { | ... | @@ -144,7 +145,7 @@ impl Handover { |
| 144 | } | 145 | } |
| 145 | } | 146 | } |
| 146 | | 147 | |
| 147 | fn open(&mut self, host: &Hello, direct: bool) -> (TcpStream, wire::Sealer) { | 148 | fn open(&mut self, host: &Hello, direct: bool) -> (TcpStream, wire::Sealer, wire::Sealer) { |
| 148 | let listener = TcpListener::bind("127.0.0.1:0").unwrap(); | 149 | let listener = TcpListener::bind("127.0.0.1:0").unwrap(); |
| 149 | let mut remote = | 150 | let mut remote = |
| 150 | TcpStream::connect_timeout(&listener.local_addr().unwrap(), OPENING).unwrap(); | 151 | TcpStream::connect_timeout(&listener.local_addr().unwrap(), OPENING).unwrap(); |
| ... | @@ -180,7 +181,7 @@ impl Handover { | ... | @@ -180,7 +181,7 @@ impl Handover { |
| 180 | let (message, body) = receive.receive(&mut remote).unwrap(); | 181 | let (message, body) = receive.receive(&mut remote).unwrap(); |
| 181 | assert_eq!(message, kind::HELLO); | 182 | assert_eq!(message, kind::HELLO); |
| 182 | assert_eq!(minicbor::decode::<Hello>(&body).unwrap(), self.shared.me); | 183 | assert_eq!(minicbor::decode::<Hello>(&body).unwrap(), self.shared.me); |
| 183 | (remote, send) | 184 | (remote, send, receive) |
| 184 | } | 185 | } |
| 185 | | 186 | |
| 186 | fn heard(&self) { | 187 | fn heard(&self) { |
| ... | @@ -198,25 +199,17 @@ impl Drop for Handover { | ... | @@ -198,25 +199,17 @@ impl Drop for Handover { |
| 198 | pipe.shutdown(); | 199 | pipe.shutdown(); |
| 199 | } | 200 | } |
| 200 | self.shared.state.lock().unwrap().peers.clear(); | 201 | self.shared.state.lock().unwrap().peers.clear(); |
| 201 | let deadline = std::time::Instant::now() + OPENING; | 202 | let readers_done = self |
| 202 | for (_, reader) in &self.readers { | 203 | .readers |
| 203 | while !reader.is_finished() && std::time::Instant::now() < deadline { | 204 | .iter() |
| 204 | thread::yield_now(); | 205 | .all(|(done, _)| done.recv_timeout(OPENING) != Err(mpsc::RecvTimeoutError::Timeout)); |
| 205 | } | | |
| 206 | } | | |
| 207 | let readers_done = self.readers.iter().all(|(_, reader)| reader.is_finished()); | | |
| 208 | self.guest.host.lock().unwrap().take(); | 206 | self.guest.host.lock().unwrap().take(); |
| 209 | let count = self.pipes.len(); | 207 | let count = self.pipes.len(); |
| 210 | self.pipes.clear(); | 208 | self.pipes.clear(); |
| 211 | let retired = (0..count).all(|_| self.retired.1.recv_timeout(OPENING).is_ok()); | 209 | let joined = readers_done |
| 212 | let mut joined = readers_done && retired; | 210 | && (0..count).all(|_| self.retired.1.recv_timeout(OPENING).is_ok()) |
| 213 | if readers_done { | 211 | && (self.readers.drain(..)).all(|(_, reader)| reader.join().is_ok()); |
| 214 | for (_, reader) in self.readers.drain(..) { | | |
| 215 | joined &= reader.join().is_ok(); | | |
| 216 | } | | |
| 217 | } | | |
| 218 | if !thread::panicking() { | 212 | if !thread::panicking() { |
| 219 | println!("HANDOVER_READERS_JOINED_AND_PIPE_OWNERS_RETIRED={joined};PIPES={count}"); | | |
| 220 | assert!( | 213 | assert!( |
| 221 | joined, | 214 | joined, |
| 222 | "handover reader/writer owners did not retire and join" | 215 | "handover reader/writer owners did not retire and join" |
| ... | @@ -230,7 +223,7 @@ fn handover_stale_frame_cannot_end_promoted_guest() { | ... | @@ -230,7 +223,7 @@ fn handover_stale_frame_cannot_end_promoted_guest() { |
| 230 | let mut fixture = Handover::new(None); | 223 | let mut fixture = Handover::new(None); |
| 231 | let mut host = Hello::new("Old host route".into(), None).unwrap(); | 224 | let mut host = Hello::new("Old host route".into(), None).unwrap(); |
| 232 | host.serves = Some(fixture.guest.share); | 225 | host.serves = Some(fixture.guest.share); |
| 233 | let (mut old, mut send) = fixture.open(&host, false); | 226 | let (mut old, mut send, _) = fixture.open(&host, false); |
| 234 | fixture.heard(); | 227 | fixture.heard(); |
| 235 | let (gate, copied, resume) = barrier(); | 228 | let (gate, copied, resume) = barrier(); |
| 236 | fixture.releases.push(resume.clone()); | 229 | fixture.releases.push(resume.clone()); |
| ... | @@ -245,7 +238,7 @@ fn handover_stale_frame_cannot_end_promoted_guest() { | ... | @@ -245,7 +238,7 @@ fn handover_stale_frame_cannot_end_promoted_guest() { |
| 245 | .unwrap(); | 238 | .unwrap(); |
| 246 | copied.recv_timeout(OPENING).unwrap(); | 239 | copied.recv_timeout(OPENING).unwrap(); |
| 247 | host.name = "Promoted host route".into(); | 240 | host.name = "Promoted host route".into(); |
| 248 | let (_new, _) = fixture.open(&host, true); | 241 | let (_new, _, _) = fixture.open(&host, true); |
| 249 | fixture.heard(); | 242 | fixture.heard(); |
| 250 | assert!( | 243 | assert!( |
| 251 | fixture.shared.state.lock().unwrap().peers[&host.peer] | 244 | fixture.shared.state.lock().unwrap().peers[&host.peer] |
| ... | @@ -257,10 +250,42 @@ fn handover_stale_frame_cannot_end_promoted_guest() { | ... | @@ -257,10 +250,42 @@ fn handover_stale_frame_cannot_end_promoted_guest() { |
| 257 | fixture.readers[0].0.recv_timeout(OPENING).unwrap(); | 250 | fixture.readers[0].0.recv_timeout(OPENING).unwrap(); |
| 258 | let ended = *fixture.guest.ended.lock().unwrap(); | 251 | let ended = *fixture.guest.ended.lock().unwrap(); |
| 259 | drop(fixture); | 252 | drop(fixture); |
| 260 | println!("HANDOVER_STALE_FRAME_ENDED={ended:?}"); | | |
| 261 | assert_eq!(ended, None, "stale frame ended the promoted guest"); | 253 | assert_eq!(ended, None, "stale frame ended the promoted guest"); |
| 262 | } | 254 | } |
| 263 | | 255 | |
| | 256 | #[test] |
| | 257 | fn handover_reply_from_retired_route_reaches_its_waiter() { |
| | 258 | let mut fixture = Handover::new(None); |
| | 259 | let mut host = Hello::new("Old host route".into(), None).unwrap(); |
| | 260 | host.serves = Some(fixture.guest.share); |
| | 261 | let (mut old, mut send, _) = fixture.open(&host, false); |
| | 262 | fixture.heard(); |
| | 263 | let (answer, waiting) = crate::task::response::<Reply>(); |
| | 264 | fixture.guest.pending.lock().unwrap().insert(75, answer); |
| | 265 | let (gate, copied, resume) = barrier(); |
| | 266 | fixture.releases.push(resume.clone()); |
| | 267 | *fixture.pipes[0].body.lock().unwrap() = Some(gate); |
| | 268 | let reply = Reply { |
| | 269 | id: 75, |
| | 270 | length: Some(12), |
| | 271 | ..Reply::default() |
| | 272 | }; |
| | 273 | send.send(&mut old, kind::REPLY, &reply).unwrap(); |
| | 274 | copied.recv_timeout(OPENING).unwrap(); |
| | 275 | host.name = "Promoted host route".into(); |
| | 276 | let (_new, _, _) = fixture.open(&host, true); |
| | 277 | fixture.heard(); |
| | 278 | resume.send(()).unwrap(); |
| | 279 | fixture.readers[0].0.recv_timeout(OPENING).unwrap(); |
| | 280 | let answered = waiting.try_recv().ok(); |
| | 281 | drop(fixture); |
| | 282 | assert_eq!( |
| | 283 | answered, |
| | 284 | Some(reply), |
| | 285 | "a reply read off a retired route missed its waiter" |
| | 286 | ); |
| | 287 | } |
| | 288 | |
| 264 | #[test] | 289 | #[test] |
| 265 | fn handover_stale_met_cannot_replace_promoted_guest_line() { | 290 | fn handover_stale_met_cannot_replace_promoted_guest_line() { |
| 266 | let (gate, delayed, resume) = barrier(); | 291 | let (gate, delayed, resume) = barrier(); |
| ... | @@ -268,28 +293,17 @@ fn handover_stale_met_cannot_replace_promoted_guest_line() { | ... | @@ -268,28 +293,17 @@ fn handover_stale_met_cannot_replace_promoted_guest_line() { |
| 268 | fixture.releases.push(resume.clone()); | 293 | fixture.releases.push(resume.clone()); |
| 269 | let mut host = Hello::new("Old host route".into(), None).unwrap(); | 294 | let mut host = Hello::new("Old host route".into(), None).unwrap(); |
| 270 | host.serves = Some(fixture.guest.share); | 295 | host.serves = Some(fixture.guest.share); |
| 271 | let (_old, _) = fixture.open(&host, false); | 296 | let (_old, _, _) = fixture.open(&host, false); |
| 272 | delayed.recv_timeout(OPENING).unwrap(); | 297 | delayed.recv_timeout(OPENING).unwrap(); |
| 273 | host.name = "Promoted host route".into(); | 298 | host.name = "Promoted host route".into(); |
| 274 | let (_new, _) = fixture.open(&host, true); | 299 | let (_new, _, _) = fixture.open(&host, true); |
| 275 | let overlap = match fixture.observed.recv_timeout(OPENING) { | 300 | fixture.heard(); |
| 276 | Ok(()) => { | 301 | assert_eq!( |
| 277 | assert_eq!( | 302 | fixture.guest.host.lock().unwrap().as_ref().unwrap().0.name, |
| 278 | fixture.guest.host.lock().unwrap().as_ref().unwrap().0.name, | 303 | host.name |
| 279 | host.name | 304 | ); |
| 280 | ); | 305 | resume.send(()).unwrap(); |
| 281 | resume.send(()).unwrap(); | 306 | fixture.heard(); |
| 282 | fixture.heard(); | | |
| 283 | true | | |
| 284 | } | | |
| 285 | Err(mpsc::RecvTimeoutError::Timeout) => { | | |
| 286 | resume.send(()).unwrap(); | | |
| 287 | fixture.heard(); | | |
| 288 | fixture.heard(); | | |
| 289 | false | | |
| 290 | } | | |
| 291 | Err(error) => panic!("handover event channel closed: {error}"), | | |
| 292 | }; | | |
| 293 | assert_eq!( | 307 | assert_eq!( |
| 294 | fixture.shared.state.lock().unwrap().peers[&host.peer] | 308 | fixture.shared.state.lock().unwrap().peers[&host.peer] |
| 295 | .peer | 309 | .peer |
| ... | @@ -308,7 +322,6 @@ fn handover_stale_met_cannot_replace_promoted_guest_line() { | ... | @@ -308,7 +322,6 @@ fn handover_stale_met_cannot_replace_promoted_guest_line() { |
| 308 | .name | 322 | .name |
| 309 | .clone(); | 323 | .clone(); |
| 310 | drop(fixture); | 324 | drop(fixture); |
| 311 | println!("HANDOVER_MET_PROMOTION_OVERLAP={overlap}"); | | |
| 312 | assert_eq!( | 325 | assert_eq!( |
| 313 | selected, host.name, | 326 | selected, host.name, |
| 314 | "stale Met replaced the promoted guest Line" | 327 | "stale Met replaced the promoted guest Line" |
| ... | @@ -364,12 +377,12 @@ fn handover_old_left_preserves_promoted_host_and_pending_reply() { | ... | @@ -364,12 +377,12 @@ fn handover_old_left_preserves_promoted_host_and_pending_reply() { |
| 364 | }); | 377 | }); |
| 365 | let mut host = Hello::new("Old host route".into(), None).unwrap(); | 378 | let mut host = Hello::new("Old host route".into(), None).unwrap(); |
| 366 | host.serves = Some(fixture.guest.share); | 379 | host.serves = Some(fixture.guest.share); |
| 367 | let (old, _) = fixture.open(&host, false); | 380 | let (old, _, _) = fixture.open(&host, false); |
| 368 | observed(&events, kind::HELLO); | 381 | observed(&events, kind::HELLO); |
| 369 | old.shutdown(Shutdown::Both).unwrap(); | 382 | old.shutdown(Shutdown::Both).unwrap(); |
| 370 | removed.recv_timeout(OPENING).unwrap(); | 383 | removed.recv_timeout(OPENING).unwrap(); |
| 371 | host.name = "Promoted host route".into(); | 384 | host.name = "Promoted host route".into(); |
| 372 | let (_new, _) = fixture.open(&host, true); | 385 | let (_new, _, _) = fixture.open(&host, true); |
| 373 | observed(&events, kind::HELLO); | 386 | observed(&events, kind::HELLO); |
| 374 | let (answer, waiting) = crate::task::response::<Reply>(); | 387 | let (answer, waiting) = crate::task::response::<Reply>(); |
| 375 | fixture.guest.pending.lock().unwrap().insert(73, answer); | 388 | fixture.guest.pending.lock().unwrap().insert(73, answer); |
| ... | @@ -396,7 +409,7 @@ fn handover_selected_loss_clears_host_and_disconnects_pending_reply() { | ... | @@ -396,7 +409,7 @@ fn handover_selected_loss_clears_host_and_disconnects_pending_reply() { |
| 396 | let mut fixture = Handover::new(None); | 409 | let mut fixture = Handover::new(None); |
| 397 | let mut host = Hello::new("Selected host".into(), None).unwrap(); | 410 | let mut host = Hello::new("Selected host".into(), None).unwrap(); |
| 398 | host.serves = Some(fixture.guest.share); | 411 | host.serves = Some(fixture.guest.share); |
| 399 | let (remote, _) = fixture.open(&host, true); | 412 | let (remote, _, _) = fixture.open(&host, true); |
| 400 | fixture.heard(); | 413 | fixture.heard(); |
| 401 | let (answer, waiting) = crate::task::response::<Reply>(); | 414 | let (answer, waiting) = crate::task::response::<Reply>(); |
| 402 | fixture.guest.pending.lock().unwrap().insert(74, answer); | 415 | fixture.guest.pending.lock().unwrap().insert(74, answer); |
| ... | @@ -434,34 +447,17 @@ fn served() -> (tempfile::TempDir, Arc<Served>) { | ... | @@ -434,34 +447,17 @@ fn served() -> (tempfile::TempDir, Arc<Served>) { |
| 434 | #[test] | 447 | #[test] |
| 435 | fn handover_copied_host_request_cannot_enter_promoted_queue() { | 448 | fn handover_copied_host_request_cannot_enter_promoted_queue() { |
| 436 | let (_directory, served) = served(); | 449 | let (_directory, served) = served(); |
| 437 | let queues = Arc::new(Mutex::new(Vec::new())); | | |
| 438 | let mut fixture = Handover::new(None); | 450 | let mut fixture = Handover::new(None); |
| 439 | let serving = Arc::clone(&served); | 451 | let serving = Arc::clone(&served); |
| 440 | let received = Arc::clone(&queues); | | |
| 441 | let events = fixture.intercept(move |source, event| match event { | 452 | let events = fixture.intercept(move |source, event| match event { |
| 442 | Event::Met(hello, line) => { | 453 | Event::Met(hello, line) => assert!(serving.admit(source, hello, line)), |
| 443 | let (queue, waiting) = mpsc::sync_channel(4); | | |
| 444 | let retired = serving.guests.lock().unwrap().insert( | | |
| 445 | hello.peer, | | |
| 446 | Admitted { | | |
| 447 | hello: Arc::clone(hello), | | |
| 448 | line: (*line).clone(), | | |
| 449 | queue, | | |
| 450 | starts: BURST, | | |
| 451 | counted: Instant::now(), | | |
| 452 | held: Vec::new(), | | |
| 453 | }, | | |
| 454 | ); | | |
| 455 | drop(retired); | | |
| 456 | received.lock().unwrap().push(waiting); | | |
| 457 | } | | |
| 458 | Event::Frame { from, kind, body } => serving.queue(source, from, *kind, body), | 454 | Event::Frame { from, kind, body } => serving.queue(source, from, *kind, body), |
| 459 | Event::Left(hello) => serving.forget(&hello.peer, Some((source, hello))), | 455 | Event::Left(hello) => serving.forget(&hello.peer, Some((source, hello))), |
| 460 | _ => {} | 456 | _ => {} |
| 461 | }); | 457 | }); |
| 462 | let mut host = Hello::new("Old route".into(), None).unwrap(); | 458 | let mut host = Hello::new("Old route".into(), None).unwrap(); |
| 463 | host.serves = Some(fixture.guest.share); | 459 | host.serves = Some(fixture.guest.share); |
| 464 | let (mut old, mut send) = fixture.open(&host, false); | 460 | let (mut old, mut send, _) = fixture.open(&host, false); |
| 465 | observed(&events, kind::HELLO); | 461 | observed(&events, kind::HELLO); |
| 466 | let (gate, copied, resume) = barrier(); | 462 | let (gate, copied, resume) = barrier(); |
| 467 | fixture.releases.push(resume.clone()); | 463 | fixture.releases.push(resume.clone()); |
| ... | @@ -471,6 +467,8 @@ fn handover_copied_host_request_cannot_enter_promoted_queue() { | ... | @@ -471,6 +467,8 @@ fn handover_copied_host_request_cannot_enter_promoted_queue() { |
| 471 | kind::PUT, | 467 | kind::PUT, |
| 472 | &Request { | 468 | &Request { |
| 473 | id: 80, | 469 | id: 80, |
| | 470 | handle: Some(1), |
| | 471 | offset: Some(0), |
| 474 | bytes: Some(vec![1, 2, 3]), | 472 | bytes: Some(vec![1, 2, 3]), |
| 475 | ..Request::default() | 473 | ..Request::default() |
| 476 | }, | 474 | }, |
| ... | @@ -478,31 +476,41 @@ fn handover_copied_host_request_cannot_enter_promoted_queue() { | ... | @@ -478,31 +476,41 @@ fn handover_copied_host_request_cannot_enter_promoted_queue() { |
| 478 | .unwrap(); | 476 | .unwrap(); |
| 479 | copied.recv_timeout(OPENING).unwrap(); | 477 | copied.recv_timeout(OPENING).unwrap(); |
| 480 | host.name = "Promoted route".into(); | 478 | host.name = "Promoted route".into(); |
| 481 | let (mut new, mut send) = fixture.open(&host, true); | 479 | let (mut new, mut send, mut receive) = fixture.open(&host, true); |
| 482 | observed(&events, kind::HELLO); | 480 | observed(&events, kind::HELLO); |
| 483 | resume.send(()).unwrap(); | 481 | resume.send(()).unwrap(); |
| 484 | fixture.readers[0].0.recv_timeout(OPENING).unwrap(); | 482 | fixture.readers[0].0.recv_timeout(OPENING).unwrap(); |
| 485 | let no_stale_request = matches!( | | |
| 486 | queues.lock().unwrap()[1].try_recv(), | | |
| 487 | Err(mpsc::TryRecvError::Empty) | | |
| 488 | ); | | |
| 489 | send.send( | 483 | send.send( |
| 490 | &mut new, | 484 | &mut new, |
| 491 | kind::PUT, | 485 | kind::PUT, |
| 492 | &Request { | 486 | &Request { |
| 493 | id: 81, | 487 | id: 81, |
| | 488 | handle: Some(2), |
| | 489 | offset: Some(0), |
| 494 | bytes: Some(vec![4, 5, 6]), | 490 | bytes: Some(vec![4, 5, 6]), |
| 495 | ..Request::default() | 491 | ..Request::default() |
| 496 | }, | 492 | }, |
| 497 | ) | 493 | ) |
| 498 | .unwrap(); | 494 | .unwrap(); |
| 499 | let (kind, body) = queues.lock().unwrap()[1].recv_timeout(OPENING).unwrap(); | 495 | let reply = loop { |
| 500 | let accepted = kind == kind::PUT && minicbor::decode::<Request>(&body).unwrap().id == 81; | 496 | let (kind, body) = receive.receive(&mut new).unwrap(); |
| | 497 | if kind == kind::REPLY { |
| | 498 | break minicbor::decode::<Reply>(&body).unwrap(); |
| | 499 | } |
| | 500 | }; |
| | 501 | let puts = served |
| | 502 | .puts |
| | 503 | .lock() |
| | 504 | .unwrap() |
| | 505 | .keys() |
| | 506 | .copied() |
| | 507 | .collect::<Vec<_>>(); |
| 501 | served.guests.lock().unwrap().clear(); | 508 | served.guests.lock().unwrap().clear(); |
| 502 | drop(fixture); | 509 | drop(fixture); |
| 503 | assert!( | 510 | assert_eq!( |
| 504 | no_stale_request && accepted, | 511 | (reply.id, reply.failure, puts), |
| 505 | "retired request entered the promoted queue or current request was lost" | 512 | (81, None, vec![(host.peer, 2)]), |
| | 513 | "retired request reached the promoted guest or current request was lost" |
| 506 | ); | 514 | ); |
| 507 | } | 515 | } |
| 508 | | 516 | |
| ... | @@ -514,25 +522,8 @@ fn handover_old_left_preserves_promoted_host_uploads_and_snapshots() { | ... | @@ -514,25 +522,8 @@ fn handover_old_left_preserves_promoted_host_uploads_and_snapshots() { |
| 514 | fixture.releases.push(resume.clone()); | 522 | fixture.releases.push(resume.clone()); |
| 515 | let gate = Mutex::new(Some(gate)); | 523 | let gate = Mutex::new(Some(gate)); |
| 516 | let serving = Arc::clone(&served); | 524 | let serving = Arc::clone(&served); |
| 517 | let waiting = Arc::new(Mutex::new(Vec::new())); | | |
| 518 | let keep_queues = Arc::clone(&waiting); | | |
| 519 | let events = fixture.intercept(move |source, event| match event { | 525 | let events = fixture.intercept(move |source, event| match event { |
| 520 | Event::Met(hello, line) => { | 526 | Event::Met(hello, line) => assert!(serving.admit(source, hello, line)), |
| 521 | let (queue, received) = mpsc::sync_channel(1); | | |
| 522 | let retired = serving.guests.lock().unwrap().insert( | | |
| 523 | hello.peer, | | |
| 524 | Admitted { | | |
| 525 | hello: Arc::clone(hello), | | |
| 526 | line: (*line).clone(), | | |
| 527 | queue, | | |
| 528 | starts: BURST, | | |
| 529 | counted: Instant::now(), | | |
| 530 | held: Vec::new(), | | |
| 531 | }, | | |
| 532 | ); | | |
| 533 | drop(retired); | | |
| 534 | keep_queues.lock().unwrap().push(received); | | |
| 535 | } | | |
| 536 | Event::Left(hello) => { | 527 | Event::Left(hello) => { |
| 537 | if let Some(gate) = gate.lock().unwrap().take() { | 528 | if let Some(gate) = gate.lock().unwrap().take() { |
| 538 | gate.wait().unwrap(); | 529 | gate.wait().unwrap(); |
| ... | @@ -543,12 +534,12 @@ fn handover_old_left_preserves_promoted_host_uploads_and_snapshots() { | ... | @@ -543,12 +534,12 @@ fn handover_old_left_preserves_promoted_host_uploads_and_snapshots() { |
| 543 | }); | 534 | }); |
| 544 | let mut host = Hello::new("Old route".into(), None).unwrap(); | 535 | let mut host = Hello::new("Old route".into(), None).unwrap(); |
| 545 | host.serves = Some(fixture.guest.share); | 536 | host.serves = Some(fixture.guest.share); |
| 546 | let (old, _) = fixture.open(&host, false); | 537 | let (old, _, _) = fixture.open(&host, false); |
| 547 | observed(&events, kind::HELLO); | 538 | observed(&events, kind::HELLO); |
| 548 | old.shutdown(Shutdown::Both).unwrap(); | 539 | old.shutdown(Shutdown::Both).unwrap(); |
| 549 | removed.recv_timeout(OPENING).unwrap(); | 540 | removed.recv_timeout(OPENING).unwrap(); |
| 550 | host.name = "Promoted route".into(); | 541 | host.name = "Promoted route".into(); |
| 551 | let (_new, _) = fixture.open(&host, true); | 542 | let (_new, _, _) = fixture.open(&host, true); |
| 552 | observed(&events, kind::HELLO); | 543 | observed(&events, kind::HELLO); |
| 553 | served | 544 | served |
| 554 | .puts | 545 | .puts |
| ... | @@ -588,18 +579,10 @@ fn handover_deferred_presence_cannot_replace_promoted_host_room() { | ... | @@ -588,18 +579,10 @@ fn handover_deferred_presence_cannot_replace_promoted_host_room() { |
| 588 | let mut fixture = Handover::new(None); | 579 | let mut fixture = Handover::new(None); |
| 589 | let mut host = Hello::new("Old host route".into(), None).unwrap(); | 580 | let mut host = Hello::new("Old host route".into(), None).unwrap(); |
| 590 | host.serves = Some(fixture.guest.share); | 581 | host.serves = Some(fixture.guest.share); |
| 591 | let (_old, _) = fixture.open(&host, false); | 582 | let (_old, _, _) = fixture.open(&host, false); |
| 592 | fixture.heard(); | 583 | fixture.heard(); |
| 593 | let from = Arc::clone(&fixture.guest.host.lock().unwrap().as_ref().unwrap().0); | 584 | let from = Arc::clone(&fixture.guest.host.lock().unwrap().as_ref().unwrap().0); |
| 594 | let secret = [61; 16]; | 585 | let secret = [61; 16]; |
| 595 | let live = Live::start( | | |
| 596 | fixture.guest.me.clone(), | | |
| 597 | &Room::Notebook(secret), | | |
| 598 | None, | | |
| 599 | None, | | |
| 600 | |_| {}, | | |
| 601 | ) | | |
| 602 | .unwrap(); | | |
| 603 | let guest = Arc::clone(&fixture.guest); | 586 | let guest = Arc::clone(&fixture.guest); |
| 604 | let source = Arc::clone(&fixture.shared); | 587 | let source = Arc::clone(&fixture.shared); |
| 605 | let here_owner = Arc::clone(&fixture.guest); | 588 | let here_owner = Arc::clone(&fixture.guest); |
| ... | @@ -608,14 +591,14 @@ fn handover_deferred_presence_cannot_replace_promoted_host_room() { | ... | @@ -608,14 +591,14 @@ fn handover_deferred_presence_cannot_replace_promoted_host_room() { |
| 608 | let (finished, done) = mpsc::channel(); | 591 | let (finished, done) = mpsc::channel(); |
| 609 | let installer = thread::spawn(move || { | 592 | let installer = thread::spawn(move || { |
| 610 | entered.send(()).unwrap(); | 593 | entered.send(()).unwrap(); |
| 611 | guest.install_presence(&source, &from, secret, live); | 594 | guest.join_presence(&source, &from, secret); |
| 612 | let _ = finished.send(()); | 595 | let _ = finished.send(()); |
| 613 | true | 596 | true |
| 614 | }); | 597 | }); |
| 615 | fixture.readers.push((done, installer)); | 598 | fixture.readers.push((done, installer)); |
| 616 | started.recv_timeout(OPENING).unwrap(); | 599 | started.recv_timeout(OPENING).unwrap(); |
| 617 | host.name = "Promoted host route".into(); | 600 | host.name = "Promoted host route".into(); |
| 618 | let (_new, _) = fixture.open(&host, true); | 601 | let (_new, _, _) = fixture.open(&host, true); |
| 619 | fixture.heard(); | 602 | fixture.heard(); |
| 620 | drop(here); | 603 | drop(here); |
| 621 | fixture.readers[1].0.recv_timeout(OPENING).unwrap(); | 604 | fixture.readers[1].0.recv_timeout(OPENING).unwrap(); |
| ... | @@ -639,18 +622,10 @@ fn handover_presence_install_seeds_latest_here() { | ... | @@ -639,18 +622,10 @@ fn handover_presence_install_seeds_latest_here() { |
| 639 | let mut fixture = Handover::new(None); | 622 | let mut fixture = Handover::new(None); |
| 640 | let mut host = Hello::new("Current host".into(), None).unwrap(); | 623 | let mut host = Hello::new("Current host".into(), None).unwrap(); |
| 641 | host.serves = Some(fixture.guest.share); | 624 | host.serves = Some(fixture.guest.share); |
| 642 | let (_remote, _) = fixture.open(&host, true); | 625 | let (_remote, _, _) = fixture.open(&host, true); |
| 643 | fixture.heard(); | 626 | fixture.heard(); |
| 644 | let from = Arc::clone(&fixture.guest.host.lock().unwrap().as_ref().unwrap().0); | 627 | let from = Arc::clone(&fixture.guest.host.lock().unwrap().as_ref().unwrap().0); |
| 645 | let secret = [62; 16]; | 628 | let secret = [62; 16]; |
| 646 | let live = Live::start( | | |
| 647 | fixture.guest.me.clone(), | | |
| 648 | &Room::Notebook(secret), | | |
| 649 | None, | | |
| 650 | None, | | |
| 651 | |_| {}, | | |
| 652 | ) | | |
| 653 | .unwrap(); | | |
| 654 | let guest = Arc::clone(&fixture.guest); | 629 | let guest = Arc::clone(&fixture.guest); |
| 655 | let source = Arc::clone(&fixture.shared); | 630 | let source = Arc::clone(&fixture.shared); |
| 656 | let mut here = fixture.guest.here.lock().unwrap(); | 631 | let mut here = fixture.guest.here.lock().unwrap(); |
| ... | @@ -662,7 +637,7 @@ fn handover_presence_install_seeds_latest_here() { | ... | @@ -662,7 +637,7 @@ fn handover_presence_install_seeds_latest_here() { |
| 662 | let (finished, done) = mpsc::channel(); | 637 | let (finished, done) = mpsc::channel(); |
| 663 | let installer = thread::spawn(move || { | 638 | let installer = thread::spawn(move || { |
| 664 | entered.send(()).unwrap(); | 639 | entered.send(()).unwrap(); |
| 665 | guest.install_presence(&source, &from, secret, live); | 640 | guest.join_presence(&source, &from, secret); |
| 666 | let _ = finished.send(()); | 641 | let _ = finished.send(()); |
| 667 | true | 642 | true |
| 668 | }); | 643 | }); |
| ... | @@ -699,7 +674,7 @@ pub(super) fn promoted_routes<T>( | ... | @@ -699,7 +674,7 @@ pub(super) fn promoted_routes<T>( |
| 699 | shared.secret = room.secret(); | 674 | shared.secret = room.secret(); |
| 700 | shared.room = room; | 675 | shared.room = room; |
| 701 | let host = Hello::new("Pairing device".into(), None).unwrap(); | 676 | let host = Hello::new("Pairing device".into(), None).unwrap(); |
| 702 | let (_old, _) = fixture.open(&host, false); | 677 | let (_old, _, _) = fixture.open(&host, false); |
| 703 | fixture.heard(); | 678 | fixture.heard(); |
| 704 | let (old, old_line) = { | 679 | let (old, old_line) = { |
| 705 | let state = fixture.shared.state.lock().unwrap(); | 680 | let state = fixture.shared.state.lock().unwrap(); |
| ... | @@ -707,7 +682,7 @@ pub(super) fn promoted_routes<T>( | ... | @@ -707,7 +682,7 @@ pub(super) fn promoted_routes<T>( |
| 707 | (Arc::clone(&link.peer.hello), link.line.clone()) | 682 | (Arc::clone(&link.peer.hello), link.line.clone()) |
| 708 | }; | 683 | }; |
| 709 | before(&fixture.shared, &old, &old_line); | 684 | before(&fixture.shared, &old, &old_line); |
| 710 | let (_new, _) = fixture.open(&host, true); | 685 | let (_new, _, _) = fixture.open(&host, true); |
| 711 | fixture.heard(); | 686 | fixture.heard(); |
| 712 | let (new, new_line) = { | 687 | let (new, new_line) = { |
| 713 | let state = fixture.shared.state.lock().unwrap(); | 688 | let state = fixture.shared.state.lock().unwrap(); |
| ... | @@ -719,3 +694,147 @@ pub(super) fn promoted_routes<T>( | ... | @@ -719,3 +694,147 @@ pub(super) fn promoted_routes<T>( |
| 719 | drop(fixture); | 694 | drop(fixture); |
| 720 | result | 695 | result |
| 721 | } | 696 | } |
| | 697 | |
| | 698 | #[test] |
| | 699 | fn handover_pair_pending_keeps_promoted_identity_and_rejects_retired_allow() { |
| | 700 | let directory = tempfile::tempdir().unwrap(); |
| | 701 | let folder = directory.path().join("notebook"); |
| | 702 | std::fs::create_dir(&folder).unwrap(); |
| | 703 | let sharing = Sharing { |
| | 704 | approve: true, |
| | 705 | code: super::super::super::code::format(701, "ABCDEF").unwrap(), |
| | 706 | ..Sharing::new("").unwrap() |
| | 707 | }; |
| | 708 | let room = Room::share(&sharing.code, &sharing.password); |
| | 709 | let host = Host::start( |
| | 710 | crate::session::Notebook::open(&folder, directory.path().join("cache")) |
| | 711 | .unwrap() |
| | 712 | .into_storage(), |
| | 713 | Hello::new("Notebook owner".into(), None).unwrap(), |
| | 714 | sharing, |
| | 715 | "Fixture", |
| | 716 | None, |
| | 717 | None, |
| | 718 | || {}, |
| | 719 | |_| Ok(()), |
| | 720 | ) |
| | 721 | .unwrap(); |
| | 722 | let callback = Arc::clone(&host.pairing.lock().unwrap().as_ref().unwrap().shared); |
| | 723 | let valid = promoted_routes( |
| | 724 | room, |
| | 725 | |source, old, old_line| { |
| | 726 | (callback.events)(source, Event::Met(old, old_line)); |
| | 727 | }, |
| | 728 | |source, old, old_line, new, new_line| { |
| | 729 | host.allow(&old.peer).unwrap(); |
| | 730 | let no_grant = host.sharing().members.is_empty(); |
| | 731 | (callback.events)(source, Event::Met(new, new_line)); |
| | 732 | (callback.events)(source, Event::Met(old, old_line)); |
| | 733 | (callback.events)(source, Event::Left(old)); |
| | 734 | let kept = host |
| | 735 | .requests() |
| | 736 | .first() |
| | 737 | .is_some_and(|request| Arc::ptr_eq(request, new)); |
| | 738 | host.stop(); |
| | 739 | kept && no_grant |
| | 740 | }, |
| | 741 | ); |
| | 742 | assert!( |
| | 743 | valid, |
| | 744 | "pairing admitted a retired route or retired Left removed the promoted request" |
| | 745 | ); |
| | 746 | } |
| | 747 | |
| | 748 | #[test] |
| | 749 | fn handover_pair_retired_met_cannot_persist_another_credential() { |
| | 750 | let directory = tempfile::tempdir().unwrap(); |
| | 751 | let folder = directory.path().join("notebook"); |
| | 752 | std::fs::create_dir(&folder).unwrap(); |
| | 753 | let sharing = Sharing { |
| | 754 | approve: false, |
| | 755 | code: super::super::super::code::format(702, "ABCDEF").unwrap(), |
| | 756 | ..Sharing::new("").unwrap() |
| | 757 | }; |
| | 758 | let room = Room::share(&sharing.code, &sharing.password); |
| | 759 | let kept = Arc::new(AtomicUsize::new(0)); |
| | 760 | let count = Arc::clone(&kept); |
| | 761 | let host = Host::start( |
| | 762 | crate::session::Notebook::open(&folder, directory.path().join("cache")) |
| | 763 | .unwrap() |
| | 764 | .into_storage(), |
| | 765 | Hello::new("Notebook owner".into(), None).unwrap(), |
| | 766 | sharing, |
| | 767 | "Fixture", |
| | 768 | None, |
| | 769 | None, |
| | 770 | || {}, |
| | 771 | move |_| { |
| | 772 | count.fetch_add(1, Ordering::SeqCst); |
| | 773 | Ok(()) |
| | 774 | }, |
| | 775 | ) |
| | 776 | .unwrap(); |
| | 777 | let callback = Arc::clone(&host.pairing.lock().unwrap().as_ref().unwrap().shared); |
| | 778 | let valid = promoted_routes( |
| | 779 | room, |
| | 780 | |_, _, _| {}, |
| | 781 | |source, old, old_line, new, new_line| { |
| | 782 | (callback.events)(source, Event::Met(new, new_line)); |
| | 783 | let after_current = kept.load(Ordering::SeqCst); |
| | 784 | (callback.events)(source, Event::Met(old, old_line)); |
| | 785 | let refused = kept.load(Ordering::SeqCst) == after_current |
| | 786 | && after_current == 2 |
| | 787 | && host.sharing().members.len() == 1; |
| | 788 | host.stop(); |
| | 789 | refused |
| | 790 | }, |
| | 791 | ); |
| | 792 | assert!(valid, "retired pairing Met persisted another credential"); |
| | 793 | } |
| | 794 | |
| | 795 | #[test] |
| | 796 | fn handover_retired_membership_rejects_deferred_access_met() { |
| | 797 | let directory = tempfile::tempdir().unwrap(); |
| | 798 | let folder = directory.path().join("notebook"); |
| | 799 | std::fs::create_dir(&folder).unwrap(); |
| | 800 | let secret = [71; 16]; |
| | 801 | let sharing = Sharing { |
| | 802 | code: super::super::super::code::format(703, "ABCDEF").unwrap(), |
| | 803 | members: vec![Device { |
| | 804 | secret, |
| | 805 | name: "Retired device".into(), |
| | 806 | device: None, |
| | 807 | }], |
| | 808 | ..Sharing::new("").unwrap() |
| | 809 | }; |
| | 810 | let host = Host::start( |
| | 811 | crate::session::Notebook::open(&folder, directory.path().join("cache")) |
| | 812 | .unwrap() |
| | 813 | .into_storage(), |
| | 814 | Hello::new("Notebook owner".into(), None).unwrap(), |
| | 815 | sharing, |
| | 816 | "Fixture", |
| | 817 | None, |
| | 818 | None, |
| | 819 | || {}, |
| | 820 | |_| Ok(()), |
| | 821 | ) |
| | 822 | .unwrap(); |
| | 823 | let callback = Arc::clone(&host.members.access.lock().unwrap()[&secret].shared); |
| | 824 | let refused = promoted_routes( |
| | 825 | Room::Notebook(secret), |
| | 826 | |_, _, _| {}, |
| | 827 | |source, _, _, current, line| { |
| | 828 | host.remove(&secret).unwrap(); |
| | 829 | (callback.events)(source, Event::Met(current, line)); |
| | 830 | let refused = host.members.served.guests.lock().unwrap().is_empty() |
| | 831 | && host.sharing().members.is_empty(); |
| | 832 | host.stop(); |
| | 833 | refused |
| | 834 | }, |
| | 835 | ); |
| | 836 | assert!( |
| | 837 | refused, |
| | 838 | "deferred access callback readmitted a removed device" |
| | 839 | ); |
| | 840 | } |