diff options
-rw-r--r-- | Cargo.lock | 224 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/encoding/headermap.rs | 18 | ||||
-rw-r--r-- | src/encoding/headers.rs | 71 | ||||
-rw-r--r-- | src/encoding/method.rs | 18 | ||||
-rw-r--r-- | src/encoding/request.rs | 1 | ||||
-rw-r--r-- | src/encoding/uri.rs | 1 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/transaction/mod.rs | 58 | ||||
-rw-r--r-- | src/transport/client.rs | 0 | ||||
-rw-r--r-- | src/transport/mod.rs | 11 | ||||
-rw-r--r-- | src/transport/tcp.rs | 58 |
12 files changed, 447 insertions, 17 deletions
@@ -62,7 +62,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -72,7 +72,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -82,6 +82,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] name = "backtrace" version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -103,6 +109,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] name = "cc" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -161,6 +179,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -179,6 +203,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -200,6 +234,27 @@ dependencies = [ ] [[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] name = "object" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -209,6 +264,29 @@ dependencies = [ ] [[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -270,6 +348,15 @@ dependencies = [ ] [[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags", +] + +[[package]] name = "regex" version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -305,6 +392,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] name = "sip" version = "0.1.0" dependencies = [ @@ -317,6 +419,22 @@ dependencies = [ ] [[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] name = "syn" version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -334,7 +452,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -357,11 +495,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -370,30 +532,48 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" @@ -406,24 +586,48 @@ checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -tokio = "1.38.0" +tokio = { version = "1.38.0", features = ["full"] } anyhow = "1.0.86" log = "0.4.22" rand = "0.9.0-alpha.1" diff --git a/src/encoding/headermap.rs b/src/encoding/headermap.rs index 313d8b5..2f9f097 100644 --- a/src/encoding/headermap.rs +++ b/src/encoding/headermap.rs @@ -13,11 +13,17 @@ impl HeaderMap { self.0.push((H::NAME.to_string(), format!("{h}"))); self } - pub fn get<H: Header>(&self) -> Option<Result<H>> { + pub fn insert<H: Header>(&mut self, h: H) { + self.0.push((H::NAME.to_string(), format!("{h}"))); + } + pub fn get_raw(&self, name: &str) -> Option<&str> { self.0 .iter() - .find(|(k, _)| k == H::NAME) - .map(|(_, v)| H::from_str(v)) + .find(|(k, _)| k.eq_ignore_ascii_case(name)) + .map(|(_, v)| v.as_str()) + } + pub fn get<H: Header>(&self) -> Option<Result<H>> { + self.get_raw(H::NAME).map(H::from_str) } pub fn insert_raw(&mut self, key: String, value: String) { self.0.push((key, value)) @@ -32,3 +38,9 @@ impl Display for HeaderMap { Ok(()) } } + +impl FromIterator<(String, String)> for HeaderMap { + fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self { + Self(Vec::from_iter(iter)) + } +} diff --git a/src/encoding/headers.rs b/src/encoding/headers.rs index d837ee5..6bffc7e 100644 --- a/src/encoding/headers.rs +++ b/src/encoding/headers.rs @@ -1,7 +1,10 @@ +use super::{headermap::HeaderMap, method::Method}; +use anyhow::{anyhow, bail}; use std::{fmt::Display, str::FromStr}; macro_rules! header { ($hname:literal, struct $name:ident($type:ty)) => { + #[derive(Debug)] pub struct $name(pub $type); impl Header for $name { const NAME: &'static str = $hname; @@ -26,7 +29,6 @@ pub trait Header: FromStr<Err = anyhow::Error> + Display { header!("Content-Length", struct ContentLength(usize)); header!("Call-ID", struct CallID(String)); -header!("CSeq", struct CSeq(String)); header!("Via", struct Via(String)); header!("Contact", struct Contact(String)); header!("Max-Forwards", struct MaxForwards(usize)); @@ -34,3 +36,70 @@ header!("From", struct From(String)); header!("To", struct To(String)); header!("User-Agent", struct UserAgent(String)); header!("Allow", struct Allow(String)); + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct CSeq(pub u32, pub Method); + +impl Header for CSeq { + const NAME: &'static str = "CSeq"; +} +impl Display for CSeq { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} {}", self.0, self.1) + } +} +impl FromStr for CSeq { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let (seq, method) = s.split_once(" ").ok_or(anyhow!("method missing"))?; + Ok(CSeq(seq.parse()?, method.parse()?)) + } +} + +#[derive(Debug)] +pub struct WWWAuthenticate { + pub realm: String, + pub nonce: String, +} +impl Header for WWWAuthenticate { + const NAME: &'static str = "WWW-Authenticate"; +} +impl Display for WWWAuthenticate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Digest realm={:?}, nonce={:?}", self.realm, self.nonce) + } +} +impl FromStr for WWWAuthenticate { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + // TODO this is totally wrong + let kvs = s + .strip_prefix("Digest ") + .ok_or(anyhow!("type not digest"))? + .split(", ") + .map(|e| { + let Some((k, v)) = e.split_once("=") else { + bail!("not a KV-pair") + }; + Ok(( + k.to_string(), + v.strip_prefix("\"") + .ok_or(anyhow!("start quote missing"))? + .strip_suffix("\"") + .ok_or(anyhow!("end quote missing"))? + .to_string(), + )) + }) + .try_collect::<HeaderMap>()?; + Ok(WWWAuthenticate { + realm: kvs + .get_raw("realm") + .ok_or(anyhow!("realm missing"))? + .to_string(), + nonce: kvs + .get_raw("nonce") + .ok_or(anyhow!("nonce missing"))? + .to_string(), + }) + } +} diff --git a/src/encoding/method.rs b/src/encoding/method.rs index 5f8110a..73829b0 100644 --- a/src/encoding/method.rs +++ b/src/encoding/method.rs @@ -1,5 +1,7 @@ -use std::fmt::Display; +use anyhow::bail; +use std::{fmt::Display, str::FromStr}; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Method { Register, Invite, @@ -21,3 +23,17 @@ impl Display for Method { }) } } +impl FromStr for Method { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(match s { + "REGISTER" => Method::Register, + "INVITE" => Method::Invite, + "ACK" => Method::Ack, + "OPTION" => Method::Option, + "CANCEL" => Method::Cancel, + "BYE" => Method::Bye, + _ => bail!("unknown method"), + }) + } +} diff --git a/src/encoding/request.rs b/src/encoding/request.rs index 03b93c9..124522c 100644 --- a/src/encoding/request.rs +++ b/src/encoding/request.rs @@ -1,6 +1,7 @@ use super::{headermap::HeaderMap, method::Method, uri::Uri}; use std::fmt::Display; +#[derive(Debug)] pub struct Request { pub method: Method, pub uri: Uri, diff --git a/src/encoding/uri.rs b/src/encoding/uri.rs index 2d77df2..64572ba 100644 --- a/src/encoding/uri.rs +++ b/src/encoding/uri.rs @@ -1,5 +1,6 @@ use std::fmt::Display; +#[derive(Debug)] pub struct Uri { pub content: String, } @@ -1,2 +1,4 @@ +#![feature(iterator_try_collect)] pub mod encoding; pub mod transport; +pub mod transaction; diff --git a/src/transaction/mod.rs b/src/transaction/mod.rs new file mode 100644 index 0000000..2953cb7 --- /dev/null +++ b/src/transaction/mod.rs @@ -0,0 +1,58 @@ +use crate::{ + encoding::{headers::CSeq, request::Request, response::Response}, + transport::Transport, +}; +use anyhow::{anyhow, Result}; +use std::{ + collections::HashMap, + sync::atomic::{AtomicU32, Ordering}, +}; +use tokio::sync::{ + mpsc::{self, channel}, + RwLock, +}; + +pub struct TransactionUser<T> { + transport: T, + sequence: AtomicU32, + pending_requests: RwLock<HashMap<CSeq, mpsc::Sender<Response>>>, +} + +impl<T: Transport> TransactionUser<T> { + pub fn new(transport: T) -> Self { + Self { + sequence: 0.into(), + pending_requests: Default::default(), + transport, + } + } + + pub async fn process_responses(&self) -> Result<()> { + let resp = self.transport.recv().await?; + let cseq = resp + .headers + .get() + .ok_or(anyhow!("response cseq missing"))??; + self.pending_requests + .write() + .await + .get_mut(&cseq) + .ok_or(anyhow!("message was not requested"))? + .send(resp) + .await?; + Ok(()) + } + + pub async fn transact(&self, mut request: Request) -> Result<mpsc::Receiver<Response>> { + let seq = self.sequence.fetch_add(1, Ordering::Relaxed); + let cseq = CSeq(seq, request.method); + request.headers.insert(cseq); + + let (tx, rx) = channel(4); + + self.transport.send(request).await?; + self.pending_requests.write().await.insert(cseq, tx); + + Ok(rx) + } +} diff --git a/src/transport/client.rs b/src/transport/client.rs deleted file mode 100644 index e69de29..0000000 --- a/src/transport/client.rs +++ /dev/null diff --git a/src/transport/mod.rs b/src/transport/mod.rs index b9babe5..3fa82df 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -1 +1,10 @@ -pub mod client; +use crate::encoding::{request::Request, response::Response}; +use anyhow::Result; + +pub mod tcp; + +#[allow(async_fn_in_trait)] +pub trait Transport { + async fn recv(&self) -> Result<Response>; + async fn send(&self, request: Request) -> Result<()>; +} diff --git a/src/transport/tcp.rs b/src/transport/tcp.rs new file mode 100644 index 0000000..e196db3 --- /dev/null +++ b/src/transport/tcp.rs @@ -0,0 +1,58 @@ +use crate::encoding::{request::Request, response::Response}; +use anyhow::{anyhow, Result}; +use log::debug; +use std::str::FromStr; +use tokio::{ + io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}, + net::{tcp::OwnedWriteHalf, TcpStream}, + sync::{ + mpsc::{channel, Receiver}, + Mutex, + }, +}; + +use super::Transport; + +pub struct TcpTransport { + write: Mutex<BufWriter<OwnedWriteHalf>>, + read: Mutex<Receiver<Response>>, +} + +impl TcpTransport { + pub async fn new(stream: TcpStream) -> Result<Self> { + let (read, write) = stream.into_split(); + + let (tx, rx) = channel(16); + + tokio::task::spawn(async move { + let mut sock = BufReader::new(read); + let mut message = String::new(); + loop { + while !message.ends_with("\r\n\r\n") { + sock.read_line(&mut message).await.unwrap(); + } + let mesg = Response::from_str(message.trim()).unwrap(); + debug!("<- {mesg:?}"); + tx.send(mesg).await.unwrap(); + message.clear(); + } + }); + + Ok(Self { + write: BufWriter::new(write).into(), + read: rx.into(), + }) + } +} +impl Transport for TcpTransport { + async fn recv(&self) -> Result<Response> { + self.read.lock().await.recv().await.ok_or(anyhow!("end")) + } + async fn send(&self, request: Request) -> Result<()> { + debug!("-> {request:?}"); + let mut g = self.write.lock().await; + g.write_all(format!("{request}").as_bytes()).await?; + g.flush().await?; + Ok(()) + } +} |