use crate::fixture_bytes; use bstr::ByteVec; use git_transport::client::Transport; use git_transport::{client::http, Protocol}; use std::{ io::{Read, Write}, net::SocketAddr, time::Duration, }; enum Command { ReadAndRespond(Vec), } enum CommandResult { ReadAndRespond(Vec), } pub struct Server { pub addr: SocketAddr, send_command: std::sync::mpsc::SyncSender, recv_result: std::sync::mpsc::Receiver, } impl Server { pub fn new(fixture: Vec) -> Self { let ports = (15411..).take(10); let listener = std::net::TcpListener::bind( ports .map(|port| SocketAddr::from(([127, 0, 0, 1], port))) .collect::>() .as_slice(), ) .expect("one of these ports to be free"); let addr = listener.local_addr().expect("a local address"); let (send_result, recv_result) = std::sync::mpsc::sync_channel(0); let (send_command, recv_commands) = std::sync::mpsc::sync_channel(0); std::thread::spawn(move || { for command in recv_commands { match command { Command::ReadAndRespond(response) => { let (mut stream, _) = listener.accept().expect("accept to always work"); stream .set_read_timeout(Some(Duration::from_millis(50))) .expect("timeout to always work"); stream .set_write_timeout(Some(Duration::from_millis(50))) .expect("timeout to always work"); let mut out = Vec::new(); stream.read_to_end(&mut out).ok(); stream.write_all(&response).expect("write to always work"); stream.flush().expect("flush to work"); if send_result.send(CommandResult::ReadAndRespond(out)).is_err() { break; } } } } }); send_command .send(Command::ReadAndRespond(fixture)) .expect("send to go through when thread is up"); Server { addr, send_command, recv_result, } } pub fn next_read_and_respond_with(&self, fixture: Vec) { self.send_command .send(Command::ReadAndRespond(fixture)) .expect("thread to be waiting"); } pub fn ignore_this_result(&self) { self.recv_result.recv().ok(); } pub fn received(&self) -> Vec { match self.recv_result.recv().expect("thread to be up") { CommandResult::ReadAndRespond(received) => received, } } pub fn addr(&self) -> &SocketAddr { &self.addr } pub fn received_as_string(&self) -> String { self.received().into_string().expect("utf8 only") } } pub fn serve_once(name: &str) -> Server { Server::new(fixture_bytes(name)) } pub fn serve_and_connect( name: &str, path: &str, version: Protocol, ) -> Result<(Server, http::Transport), crate::Error> { let server = serve_once(name); let url = format!( "http://{}:{}/{}", &server.addr().ip().to_string(), &server.addr().port(), path ); let client = git_transport::client::http::connect(&url, version)?; assert_eq!(url, client.to_url()); Ok((server, client)) }