From dd98771cff9650c8c0dc56c5c2064565d9a86ea0 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 16 May 2023 13:05:32 +0300 Subject: [PATCH 01/39] 3905 Serde support --- Cargo.lock | 13 + protocols/request-response/Cargo.toml | 3 + protocols/request-response/src/cbor.rs | 36 ++ protocols/request-response/src/json.rs | 36 ++ protocols/request-response/src/lib.rs | 3 + protocols/request-response/src/serde_codec.rs | 338 ++++++++++++++++++ .../request-response/tests/serde_codecs.rs | 161 +++++++++ 7 files changed, 590 insertions(+) create mode 100644 protocols/request-response/src/cbor.rs create mode 100644 protocols/request-response/src/json.rs create mode 100644 protocols/request-response/src/serde_codec.rs create mode 100644 protocols/request-response/tests/serde_codecs.rs diff --git a/Cargo.lock b/Cargo.lock index 2e850e031a0..e5ac8e6c0e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2964,6 +2964,9 @@ dependencies = [ "libp2p-tcp", "libp2p-yamux", "rand 0.8.5", + "serde", + "serde_cbor", + "serde_json", "smallvec", ] @@ -4617,6 +4620,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.162" diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index 3bde34e2cc1..40048b30ae2 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -18,6 +18,9 @@ libp2p-core = { workspace = true } libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } rand = "0.8" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.96" +serde_cbor = "0.11.2" smallvec = "1.6.1" [dev-dependencies] diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs new file mode 100644 index 00000000000..9117b9cf270 --- /dev/null +++ b/protocols/request-response/src/cbor.rs @@ -0,0 +1,36 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +pub use crate::serde_codec::{Behaviour}; + +use crate::{Codec, Config, ProtocolSupport}; +use crate::serde_codec::SerdeCodec; +use serde::{Serialize, de::DeserializeOwned}; + +pub fn new_behaviour(protocols: I, cfg: Config) -> Behaviour + where + I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + let codec: SerdeCodec = SerdeCodec::cbor(); + + Behaviour::new(codec, protocols, cfg) +} diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs new file mode 100644 index 00000000000..e495183b323 --- /dev/null +++ b/protocols/request-response/src/json.rs @@ -0,0 +1,36 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +pub use crate::serde_codec::{Behaviour}; + +use crate::{Codec, Config, ProtocolSupport}; +use crate::serde_codec::SerdeCodec; +use serde::{Serialize, de::DeserializeOwned}; + +pub fn new_behaviour(protocols: I, cfg: Config) -> Behaviour +where + I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + let codec: SerdeCodec = SerdeCodec::json(); + + Behaviour::new(codec, protocols, cfg) +} diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index 4267f83da8c..f8784043baf 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -60,6 +60,9 @@ mod codec; mod handler; +mod serde_codec; +pub mod json; +pub mod cbor; pub use codec::Codec; pub use handler::ProtocolSupport; diff --git a/protocols/request-response/src/serde_codec.rs b/protocols/request-response/src/serde_codec.rs new file mode 100644 index 00000000000..d6434d8b55d --- /dev/null +++ b/protocols/request-response/src/serde_codec.rs @@ -0,0 +1,338 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use async_trait::async_trait; +use futures::prelude::*; +use std::{ + io, marker::PhantomData, + task::{Context, Poll}, +}; +use serde::{Serialize, de::DeserializeOwned}; +use crate::{ + Behaviour as RequestResponseBehaviour, + Codec, Config, ProtocolSupport, Handler, + Event, RequestId, ResponseChannel, +}; +use libp2p_identity::PeerId; +use libp2p_core::{ + Endpoint, Multiaddr, + upgrade::{read_length_prefixed, write_length_prefixed}, +}; +use libp2p_swarm::{ + StreamProtocol, + behaviour::{FromSwarm}, + ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, THandler, + THandlerInEvent, THandlerOutEvent, ToSwarm, +}; + +const REQUEST_MAXIMUM: usize = 1_000_000; +const RESPONSE_MAXIMUM: usize = 500_000_000; + +#[derive(Debug, Clone)] +enum SerdeFormat { + JSON, + CBOR, +} + +#[derive(Debug, Clone)] +pub struct SerdeCodec { + format: SerdeFormat, + phantom: PhantomData<(Req, Resp)>, +} + +pub struct Behaviour + where + Req: Send + Clone + Serialize + DeserializeOwned + 'static, + Resp: Send + Clone + Serialize + DeserializeOwned + 'static, +{ + original: RequestResponseBehaviour>, + phantom: PhantomData<(Req, Resp)>, +} + +impl SerdeCodec + where + Req: Serialize + DeserializeOwned, + Resp: Serialize + DeserializeOwned, +{ + pub(crate) fn json() -> Self { + SerdeCodec { format: SerdeFormat::JSON, phantom: PhantomData } + } + + pub(crate) fn cbor() -> Self { + SerdeCodec { format: SerdeFormat::CBOR, phantom: PhantomData } + } + + fn val_to_vec(&self, val: &S) -> io::Result> { + match self.format { + SerdeFormat::JSON => { + serde_json::to_vec(val) + .map_err(|_| io::ErrorKind::Other.into()) + }, + SerdeFormat::CBOR => { + serde_cbor::to_vec(val) + .map_err(|_| io::ErrorKind::Other.into()) + }, + } + } + + fn val_from_slice(&self, vec: &[u8]) -> io::Result { + match self.format { + SerdeFormat::JSON => { + serde_json::from_slice(vec) + .map_err(|_| io::ErrorKind::Other.into()) + }, + SerdeFormat::CBOR => { + serde_cbor::from_slice(vec) + .map_err(|_| io::ErrorKind::Other.into()) + }, + } + } +} + +#[async_trait] +impl Codec for SerdeCodec + where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + type Protocol = StreamProtocol; + type Request = Req; + type Response = Resp; + + async fn read_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send + { + let vec = read_length_prefixed(io, REQUEST_MAXIMUM).await?; + + if vec.is_empty() { + return Err(io::ErrorKind::UnexpectedEof.into()); + } + + self.val_from_slice(vec.as_slice()) + } + + async fn read_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send + { + let vec = read_length_prefixed(io, RESPONSE_MAXIMUM).await?; + + if vec.is_empty() { + return Err(io::ErrorKind::UnexpectedEof.into()); + } + + self.val_from_slice(vec.as_slice()) + } + + async fn write_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send + { + let data = self.val_to_vec(&req)?; + write_length_prefixed(io, data).await?; + io.close().await?; + + Ok(()) + } + + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + res: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send + { + let data = self.val_to_vec(&res)?; + write_length_prefixed(io, data).await?; + io.close().await?; + + Ok(()) + } +} + +impl Behaviour + where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + pub(crate) fn new( + codec: SerdeCodec, + protocols: I, + cfg: Config) -> Behaviour + where + I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, + { + Behaviour { + original: RequestResponseBehaviour::new(codec, protocols, cfg), + phantom: PhantomData, + } + } + + + /// Initiates sending a request. + /// + /// If the targeted peer is currently not connected, a dialing + /// attempt is initiated and the request is sent as soon as a + /// connection is established. + /// + /// > **Note**: In order for such a dialing attempt to succeed, + /// > the `RequestResonse` protocol must either be embedded + /// > in another `NetworkBehaviour` that provides peer and + /// > address discovery, or known addresses of peers must be + /// > managed via [`Behaviour::add_address`] and + /// > [`Behaviour::remove_address`]. + pub fn send_request(&mut self, peer: &PeerId, request: Req) -> RequestId { + self.original.send_request(peer, request) + } + + /// Initiates sending a response to an inbound request. + /// + /// If the [`ResponseChannel`] is already closed due to a timeout or the + /// connection being closed, the response is returned as an `Err` for + /// further handling. Once the response has been successfully sent on the + /// corresponding connection, [`Event::ResponseSent`] is + /// emitted. In all other cases [`Event::InboundFailure`] + /// will be or has been emitted. + /// + /// The provided `ResponseChannel` is obtained from an inbound + /// [`Message::Request`]. + pub fn send_response( + &mut self, + ch: ResponseChannel, + rs: Resp, + ) -> Result<(), Resp> { + ch.sender.send(rs) + } + + /// Adds a known address for a peer that can be used for + /// dialing attempts by the `Swarm`, i.e. is returned + /// by [`NetworkBehaviour::handle_pending_outbound_connection`]. + /// + /// Addresses added in this way are only removed by `remove_address`. + pub fn add_address(&mut self, peer: &PeerId, address: Multiaddr) { + self.original.add_address(peer, address) + } + + /// Removes an address of a peer previously added via `add_address`. + pub fn remove_address(&mut self, peer: &PeerId, address: &Multiaddr) { + self.original.remove_address(peer, address) + } + + /// Checks whether a peer is currently connected. + pub fn is_connected(&self, peer: &PeerId) -> bool { + self.original.is_connected(peer) + } + + /// Checks whether an outbound request to the peer with the provided + /// [`PeerId`] initiated by [`Behaviour::send_request`] is still + /// pending, i.e. waiting for a response. + pub fn is_pending_outbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { + self.original.is_pending_outbound(peer, request_id) + } + + /// Checks whether an inbound request from the peer with the provided + /// [`PeerId`] is still pending, i.e. waiting for a response by the local + /// node through [`Behaviour::send_response`]. + pub fn is_pending_inbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { + self.original.is_pending_inbound(peer, request_id) + } +} + +impl NetworkBehaviour for Behaviour +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + type ConnectionHandler = Handler>; + type OutEvent = Event; + + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + self.original.handle_established_inbound_connection( + _connection_id, + peer, + local_addr, + remote_addr, + ) + } + + fn handle_established_outbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result, ConnectionDenied> { + self.original.handle_established_outbound_connection( + _connection_id, + peer, + addr, + role_override, + ) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + self.original.on_swarm_event(event); + } + + fn on_connection_handler_event( + &mut self, + _peer_id: PeerId, + _connection_id: ConnectionId, + _event: THandlerOutEvent, + ) { + self.original.on_connection_handler_event( + _peer_id, + _connection_id, + _event, + ); + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + params: &mut impl PollParameters, + ) -> Poll>> { + self.original.poll(cx, params) + } +} + + diff --git a/protocols/request-response/tests/serde_codecs.rs b/protocols/request-response/tests/serde_codecs.rs new file mode 100644 index 00000000000..eee58a49e0b --- /dev/null +++ b/protocols/request-response/tests/serde_codecs.rs @@ -0,0 +1,161 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + + +//! Integration tests for the `json::Behaviour`. + +use libp2p_request_response as request_response; +use libp2p_request_response::ProtocolSupport; +use libp2p_swarm::{StreamProtocol, Swarm}; +use libp2p_swarm_test::SwarmExt; +use rand::{self, Rng}; +use std::iter; +use serde::{Serialize, Deserialize}; +use libp2p_request_response::cbor::Behaviour; + +#[async_std::test] +async fn json() { + run_test(||{ + let protocols = iter::once( + (StreamProtocol::new("/test_json/1"), ProtocolSupport::Full) + ); + let cfg = request_response::Config::default(); + + let behaviour: Behaviour = + request_response::json::new_behaviour(protocols, cfg); + + Swarm::new_ephemeral(|_| behaviour) + }).await; +} + +#[async_std::test] +async fn cbor() { + run_test(||{ + let protocols = iter::once( + (StreamProtocol::new("/test_cbor/1"), ProtocolSupport::Full) + ); + let cfg = request_response::Config::default(); + + let behaviour: Behaviour = + request_response::cbor::new_behaviour(protocols, cfg); + + Swarm::new_ephemeral(|_| behaviour) + }).await; +} + +async fn run_test(mut supplier: F) +where + F: FnMut() -> Swarm>, +{ + let mut swarm1 = supplier(); + let peer1_id = *swarm1.local_peer_id(); + + let mut swarm2 = supplier(); + let peer2_id = *swarm2.local_peer_id(); + + swarm1.listen().await; + swarm2.connect(&mut swarm1).await; + + let test_req = TestRequest { payload: "test_request".to_string() }; + let test_resp = TestResponse { payload: "test_response".to_string() }; + + let expected_req = test_req.clone(); + let expected_resp = test_resp.clone(); + + let peer1 = async move { + loop { + match swarm1.next_swarm_event().await.try_into_behaviour_event() { + Ok(request_response::Event::Message { + peer, + message: + request_response::Message::Request { + request, channel, .. + }, + }) => { + assert_eq!(&request, &expected_req); + assert_eq!(&peer, &peer2_id); + swarm1 + .behaviour_mut() + .send_response(channel, test_resp.clone()) + .unwrap(); + } + Ok(request_response::Event::ResponseSent { peer, .. }) => { + assert_eq!(&peer, &peer2_id); + } + Ok(e) => { + panic!("Peer1: Unexpected event: {e:?}") + } + Err(..) => {} + } + } + }; + + let num_requests: u8 = rand::thread_rng().gen_range(1..100); + + let peer2 = async { + let mut count = 0; + + let mut req_id = swarm2.behaviour_mut().send_request(&peer1_id, test_req.clone()); + assert!(swarm2.behaviour().is_pending_outbound(&peer1_id, &req_id)); + + loop { + match swarm2 + .next_swarm_event() + .await + .try_into_behaviour_event() + .unwrap() + { + request_response::Event::Message { + peer, + message: + request_response::Message::Response { + request_id, + response, + }, + } => { + count += 1; + assert_eq!(&response, &expected_resp); + assert_eq!(&peer, &peer1_id); + assert_eq!(req_id, request_id); + if count >= num_requests { + return; + } else { + req_id = swarm2.behaviour_mut().send_request(&peer1_id, test_req.clone()); + } + } + e => panic!("Peer2: Unexpected event: {e:?}"), + } + } + }; + + async_std::task::spawn(Box::pin(peer1)); + peer2.await; +} + + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +struct TestRequest { + payload: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +struct TestResponse { + payload: String, +} From 55bc76a87bea239bbeda638d2603f4d06b371202 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 16 May 2023 13:40:21 +0300 Subject: [PATCH 02/39] fix failures --- protocols/request-response/src/serde_codec.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocols/request-response/src/serde_codec.rs b/protocols/request-response/src/serde_codec.rs index d6434d8b55d..83521ee18b1 100644 --- a/protocols/request-response/src/serde_codec.rs +++ b/protocols/request-response/src/serde_codec.rs @@ -277,7 +277,10 @@ where Resp: Send + Clone + Serialize + DeserializeOwned, { type ConnectionHandler = Handler>; - type OutEvent = Event; + type OutEvent = Event< + as Codec>::Request, + as Codec>::Response + >; fn handle_established_inbound_connection( &mut self, From a5f3ed94e4aca26e6036cbe0d243ddb99d09ab1c Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 16 May 2023 13:50:27 +0300 Subject: [PATCH 03/39] fix failures --- protocols/request-response/src/serde_codec.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/protocols/request-response/src/serde_codec.rs b/protocols/request-response/src/serde_codec.rs index 83521ee18b1..81e6e64a59e 100644 --- a/protocols/request-response/src/serde_codec.rs +++ b/protocols/request-response/src/serde_codec.rs @@ -273,14 +273,11 @@ impl Behaviour impl NetworkBehaviour for Behaviour where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, + Req: Send + Clone + Serialize + DeserializeOwned + 'static, + Resp: Send + Clone + Serialize + DeserializeOwned + 'static, { type ConnectionHandler = Handler>; - type OutEvent = Event< - as Codec>::Request, - as Codec>::Response - >; + type OutEvent = Event; fn handle_established_inbound_connection( &mut self, From 5304fb8535cf13910ad94ded2ae9a1442ec12214 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 16 May 2023 13:59:33 +0300 Subject: [PATCH 04/39] fix failures --- protocols/request-response/src/serde_codec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/request-response/src/serde_codec.rs b/protocols/request-response/src/serde_codec.rs index 81e6e64a59e..d6434d8b55d 100644 --- a/protocols/request-response/src/serde_codec.rs +++ b/protocols/request-response/src/serde_codec.rs @@ -273,8 +273,8 @@ impl Behaviour impl NetworkBehaviour for Behaviour where - Req: Send + Clone + Serialize + DeserializeOwned + 'static, - Resp: Send + Clone + Serialize + DeserializeOwned + 'static, + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, { type ConnectionHandler = Handler>; type OutEvent = Event; From e9628eef4fac553185e1b2f8020d6b1b256e66f3 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 16 May 2023 14:54:57 +0300 Subject: [PATCH 05/39] Fix format --- protocols/request-response/src/cbor.rs | 14 +- protocols/request-response/src/json.rs | 8 +- protocols/request-response/src/lib.rs | 4 +- protocols/request-response/src/serde_codec.rs | 129 ++++++++---------- .../request-response/tests/serde_codecs.rs | 60 ++++---- 5 files changed, 101 insertions(+), 114 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 9117b9cf270..b287e3c4ca8 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -18,17 +18,17 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -pub use crate::serde_codec::{Behaviour}; +pub use crate::serde_codec::Behaviour; -use crate::{Codec, Config, ProtocolSupport}; use crate::serde_codec::SerdeCodec; -use serde::{Serialize, de::DeserializeOwned}; +use crate::{Codec, Config, ProtocolSupport}; +use serde::{de::DeserializeOwned, Serialize}; pub fn new_behaviour(protocols: I, cfg: Config) -> Behaviour - where - I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, +where + I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, { let codec: SerdeCodec = SerdeCodec::cbor(); diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index e495183b323..f05165ef1b3 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -18,15 +18,15 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -pub use crate::serde_codec::{Behaviour}; +pub use crate::serde_codec::Behaviour; -use crate::{Codec, Config, ProtocolSupport}; use crate::serde_codec::SerdeCodec; -use serde::{Serialize, de::DeserializeOwned}; +use crate::{Codec, Config, ProtocolSupport}; +use serde::{de::DeserializeOwned, Serialize}; pub fn new_behaviour(protocols: I, cfg: Config) -> Behaviour where - I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, + I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, Req: Send + Clone + Serialize + DeserializeOwned, Resp: Send + Clone + Serialize + DeserializeOwned, { diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index f8784043baf..5208de7604c 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -58,11 +58,11 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +pub mod cbor; mod codec; mod handler; -mod serde_codec; pub mod json; -pub mod cbor; +mod serde_codec; pub use codec::Codec; pub use handler::ProtocolSupport; diff --git a/protocols/request-response/src/serde_codec.rs b/protocols/request-response/src/serde_codec.rs index d6434d8b55d..1a32cfed7de 100644 --- a/protocols/request-response/src/serde_codec.rs +++ b/protocols/request-response/src/serde_codec.rs @@ -18,28 +18,26 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use async_trait::async_trait; -use futures::prelude::*; -use std::{ - io, marker::PhantomData, - task::{Context, Poll}, -}; -use serde::{Serialize, de::DeserializeOwned}; use crate::{ - Behaviour as RequestResponseBehaviour, - Codec, Config, ProtocolSupport, Handler, - Event, RequestId, ResponseChannel, + Behaviour as RequestResponseBehaviour, Codec, Config, Event, Handler, ProtocolSupport, + RequestId, ResponseChannel, }; -use libp2p_identity::PeerId; +use async_trait::async_trait; +use futures::prelude::*; use libp2p_core::{ - Endpoint, Multiaddr, upgrade::{read_length_prefixed, write_length_prefixed}, + Endpoint, Multiaddr, }; +use libp2p_identity::PeerId; use libp2p_swarm::{ - StreamProtocol, - behaviour::{FromSwarm}, - ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, THandler, - THandlerInEvent, THandlerOutEvent, ToSwarm, + behaviour::FromSwarm, ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, + StreamProtocol, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, +}; +use serde::{de::DeserializeOwned, Serialize}; +use std::{ + io, + marker::PhantomData, + task::{Context, Poll}, }; const REQUEST_MAXIMUM: usize = 1_000_000; @@ -58,71 +56,65 @@ pub struct SerdeCodec { } pub struct Behaviour - where - Req: Send + Clone + Serialize + DeserializeOwned + 'static, - Resp: Send + Clone + Serialize + DeserializeOwned + 'static, +where + Req: Send + Clone + Serialize + DeserializeOwned + 'static, + Resp: Send + Clone + Serialize + DeserializeOwned + 'static, { original: RequestResponseBehaviour>, phantom: PhantomData<(Req, Resp)>, } impl SerdeCodec - where - Req: Serialize + DeserializeOwned, - Resp: Serialize + DeserializeOwned, +where + Req: Serialize + DeserializeOwned, + Resp: Serialize + DeserializeOwned, { pub(crate) fn json() -> Self { - SerdeCodec { format: SerdeFormat::JSON, phantom: PhantomData } + SerdeCodec { + format: SerdeFormat::JSON, + phantom: PhantomData, + } } pub(crate) fn cbor() -> Self { - SerdeCodec { format: SerdeFormat::CBOR, phantom: PhantomData } + SerdeCodec { + format: SerdeFormat::CBOR, + phantom: PhantomData, + } } fn val_to_vec(&self, val: &S) -> io::Result> { match self.format { - SerdeFormat::JSON => { - serde_json::to_vec(val) - .map_err(|_| io::ErrorKind::Other.into()) - }, - SerdeFormat::CBOR => { - serde_cbor::to_vec(val) - .map_err(|_| io::ErrorKind::Other.into()) - }, + SerdeFormat::JSON => serde_json::to_vec(val).map_err(|_| io::ErrorKind::Other.into()), + SerdeFormat::CBOR => serde_cbor::to_vec(val).map_err(|_| io::ErrorKind::Other.into()), } } fn val_from_slice(&self, vec: &[u8]) -> io::Result { match self.format { SerdeFormat::JSON => { - serde_json::from_slice(vec) - .map_err(|_| io::ErrorKind::Other.into()) - }, + serde_json::from_slice(vec).map_err(|_| io::ErrorKind::Other.into()) + } SerdeFormat::CBOR => { - serde_cbor::from_slice(vec) - .map_err(|_| io::ErrorKind::Other.into()) - }, + serde_cbor::from_slice(vec).map_err(|_| io::ErrorKind::Other.into()) + } } } } #[async_trait] impl Codec for SerdeCodec - where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, { type Protocol = StreamProtocol; type Request = Req; type Response = Resp; - async fn read_request( - &mut self, - _: &Self::Protocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Unpin + Send + async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, { let vec = read_length_prefixed(io, REQUEST_MAXIMUM).await?; @@ -138,8 +130,8 @@ impl Codec for SerdeCodec _: &Self::Protocol, io: &mut T, ) -> io::Result - where - T: AsyncRead + Unpin + Send + where + T: AsyncRead + Unpin + Send, { let vec = read_length_prefixed(io, RESPONSE_MAXIMUM).await?; @@ -156,8 +148,8 @@ impl Codec for SerdeCodec io: &mut T, req: Self::Request, ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send + where + T: AsyncWrite + Unpin + Send, { let data = self.val_to_vec(&req)?; write_length_prefixed(io, data).await?; @@ -172,8 +164,8 @@ impl Codec for SerdeCodec io: &mut T, res: Self::Response, ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send + where + T: AsyncWrite + Unpin + Send, { let data = self.val_to_vec(&res)?; write_length_prefixed(io, data).await?; @@ -184,16 +176,17 @@ impl Codec for SerdeCodec } impl Behaviour - where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, { pub(crate) fn new( codec: SerdeCodec, protocols: I, - cfg: Config) -> Behaviour - where - I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, + cfg: Config, + ) -> Behaviour + where + I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, { Behaviour { original: RequestResponseBehaviour::new(codec, protocols, cfg), @@ -201,7 +194,6 @@ impl Behaviour } } - /// Initiates sending a request. /// /// If the targeted peer is currently not connected, a dialing @@ -229,11 +221,7 @@ impl Behaviour /// /// The provided `ResponseChannel` is obtained from an inbound /// [`Message::Request`]. - pub fn send_response( - &mut self, - ch: ResponseChannel, - rs: Resp, - ) -> Result<(), Resp> { + pub fn send_response(&mut self, ch: ResponseChannel, rs: Resp) -> Result<(), Resp> { ch.sender.send(rs) } @@ -319,11 +307,8 @@ where _connection_id: ConnectionId, _event: THandlerOutEvent, ) { - self.original.on_connection_handler_event( - _peer_id, - _connection_id, - _event, - ); + self.original + .on_connection_handler_event(_peer_id, _connection_id, _event); } fn poll( @@ -334,5 +319,3 @@ where self.original.poll(cx, params) } } - - diff --git a/protocols/request-response/tests/serde_codecs.rs b/protocols/request-response/tests/serde_codecs.rs index eee58a49e0b..309a6164e8f 100644 --- a/protocols/request-response/tests/serde_codecs.rs +++ b/protocols/request-response/tests/serde_codecs.rs @@ -18,46 +18,43 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. - //! Integration tests for the `json::Behaviour`. use libp2p_request_response as request_response; +use libp2p_request_response::cbor::Behaviour; use libp2p_request_response::ProtocolSupport; use libp2p_swarm::{StreamProtocol, Swarm}; use libp2p_swarm_test::SwarmExt; use rand::{self, Rng}; +use serde::{Deserialize, Serialize}; use std::iter; -use serde::{Serialize, Deserialize}; -use libp2p_request_response::cbor::Behaviour; #[async_std::test] async fn json() { - run_test(||{ - let protocols = iter::once( - (StreamProtocol::new("/test_json/1"), ProtocolSupport::Full) - ); + run_test(|| { + let protocols = iter::once((StreamProtocol::new("/test_json/1"), ProtocolSupport::Full)); let cfg = request_response::Config::default(); let behaviour: Behaviour = request_response::json::new_behaviour(protocols, cfg); Swarm::new_ephemeral(|_| behaviour) - }).await; + }) + .await; } #[async_std::test] async fn cbor() { - run_test(||{ - let protocols = iter::once( - (StreamProtocol::new("/test_cbor/1"), ProtocolSupport::Full) - ); + run_test(|| { + let protocols = iter::once((StreamProtocol::new("/test_cbor/1"), ProtocolSupport::Full)); let cfg = request_response::Config::default(); let behaviour: Behaviour = request_response::cbor::new_behaviour(protocols, cfg); Swarm::new_ephemeral(|_| behaviour) - }).await; + }) + .await; } async fn run_test(mut supplier: F) @@ -73,8 +70,12 @@ where swarm1.listen().await; swarm2.connect(&mut swarm1).await; - let test_req = TestRequest { payload: "test_request".to_string() }; - let test_resp = TestResponse { payload: "test_response".to_string() }; + let test_req = TestRequest { + payload: "test_request".to_string(), + }; + let test_resp = TestResponse { + payload: "test_response".to_string(), + }; let expected_req = test_req.clone(); let expected_resp = test_resp.clone(); @@ -83,12 +84,12 @@ where loop { match swarm1.next_swarm_event().await.try_into_behaviour_event() { Ok(request_response::Event::Message { - peer, - message: - request_response::Message::Request { - request, channel, .. - }, - }) => { + peer, + message: + request_response::Message::Request { + request, channel, .. + }, + }) => { assert_eq!(&request, &expected_req); assert_eq!(&peer, &peer2_id); swarm1 @@ -112,7 +113,9 @@ where let peer2 = async { let mut count = 0; - let mut req_id = swarm2.behaviour_mut().send_request(&peer1_id, test_req.clone()); + let mut req_id = swarm2 + .behaviour_mut() + .send_request(&peer1_id, test_req.clone()); assert!(swarm2.behaviour().is_pending_outbound(&peer1_id, &req_id)); loop { @@ -125,10 +128,10 @@ where request_response::Event::Message { peer, message: - request_response::Message::Response { - request_id, - response, - }, + request_response::Message::Response { + request_id, + response, + }, } => { count += 1; assert_eq!(&response, &expected_resp); @@ -137,7 +140,9 @@ where if count >= num_requests { return; } else { - req_id = swarm2.behaviour_mut().send_request(&peer1_id, test_req.clone()); + req_id = swarm2 + .behaviour_mut() + .send_request(&peer1_id, test_req.clone()); } } e => panic!("Peer2: Unexpected event: {e:?}"), @@ -149,7 +154,6 @@ where peer2.await; } - #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] struct TestRequest { payload: String, From 23b7722445ff4e4394a122a178bfbe1153012eaa Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 16 May 2023 17:45:18 +0300 Subject: [PATCH 06/39] Fix failure --- protocols/request-response/src/serde_codec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/request-response/src/serde_codec.rs b/protocols/request-response/src/serde_codec.rs index 1a32cfed7de..5492962b751 100644 --- a/protocols/request-response/src/serde_codec.rs +++ b/protocols/request-response/src/serde_codec.rs @@ -265,7 +265,7 @@ where Resp: Send + Clone + Serialize + DeserializeOwned, { type ConnectionHandler = Handler>; - type OutEvent = Event; + type ToSwarm = Event; fn handle_established_inbound_connection( &mut self, @@ -315,7 +315,7 @@ where &mut self, cx: &mut Context<'_>, params: &mut impl PollParameters, - ) -> Poll>> { + ) -> Poll>> { self.original.poll(cx, params) } } From 11e078377e0620261f1dc221340e852f565fc4b7 Mon Sep 17 00:00:00 2001 From: dgarus Date: Thu, 18 May 2023 14:10:13 +0300 Subject: [PATCH 07/39] Cbor behavior --- protocols/request-response/src/cbor.rs | 132 ++++++++++++++++-- protocols/request-response/src/serde_codec.rs | 6 +- .../tests/{serde_codecs.rs => cbor.rs} | 79 +++-------- 3 files changed, 145 insertions(+), 72 deletions(-) rename protocols/request-response/tests/{serde_codecs.rs => cbor.rs} (69%) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index b287e3c4ca8..1bcffd1675f 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -18,19 +18,129 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -pub use crate::serde_codec::Behaviour; - -use crate::serde_codec::SerdeCodec; -use crate::{Codec, Config, ProtocolSupport}; +use std::{ + io, + marker::PhantomData, +}; +use futures::{AsyncRead, AsyncWrite}; +use async_trait::async_trait; +use libp2p_swarm::{NetworkBehaviour, StreamProtocol}; +use crate::{Config, ProtocolSupport}; use serde::{de::DeserializeOwned, Serialize}; +use futures::prelude::*; +use libp2p_core::upgrade::{ + read_length_prefixed, write_length_prefixed, +}; + +#[derive(Debug, Clone)] +pub struct Codec { + phantom: PhantomData<(Req, Resp)>, +} + +const REQUEST_SIZE_MAXIMUM: usize = 1_000_000; +const RESPONSE_SIZE_MAXIMUM: usize = 500_000_000; + +pub type OutEvent = crate::Event; -pub fn new_behaviour(protocols: I, cfg: Config) -> Behaviour -where - I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, +#[derive(NetworkBehaviour)] +#[behaviour( + to_swarm = "OutEvent", + prelude = "libp2p_swarm::derive_prelude" +)] +pub struct Behaviour + where + Req: Send + Clone + Serialize + DeserializeOwned + 'static, + Resp: Send + Clone + Serialize + DeserializeOwned + 'static, { - let codec: SerdeCodec = SerdeCodec::cbor(); + inner: crate::Behaviour>, +} + +#[async_trait] +impl crate::Codec for Codec + where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + type Protocol = StreamProtocol; + type Request = Req; + type Response = Resp; + + async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; + + if vec.is_empty() { + return Err(io::ErrorKind::UnexpectedEof.into()); + } - Behaviour::new(codec, protocols, cfg) + Ok(serde_json::from_slice(vec.as_slice())?) + } + + async fn read_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; + + if vec.is_empty() { + return Err(io::ErrorKind::UnexpectedEof.into()); + } + + Ok(serde_json::from_slice(vec.as_slice())?) + } + + async fn write_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let data = serde_json::to_vec(&req)?; + write_length_prefixed(io, data).await?; + io.close().await?; + + Ok(()) + } + + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + resp: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let data = serde_json::to_vec(&resp)?; + write_length_prefixed(io, data).await?; + io.close().await?; + + Ok(()) + } +} + +impl Behaviour + where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + pub fn new(protocols: I, cfg: Config) -> Self + where + I: IntoIterator as crate::Codec>::Protocol, ProtocolSupport)>, + { + Behaviour { + inner: crate::Behaviour::new( + Codec { phantom: PhantomData }, protocols, cfg, + ), + } + } } diff --git a/protocols/request-response/src/serde_codec.rs b/protocols/request-response/src/serde_codec.rs index 5492962b751..1d8b2244419 100644 --- a/protocols/request-response/src/serde_codec.rs +++ b/protocols/request-response/src/serde_codec.rs @@ -61,7 +61,6 @@ where Resp: Send + Clone + Serialize + DeserializeOwned + 'static, { original: RequestResponseBehaviour>, - phantom: PhantomData<(Req, Resp)>, } impl SerdeCodec @@ -76,12 +75,12 @@ where } } - pub(crate) fn cbor() -> Self { + /*pub(crate) fn cbor() -> Self { SerdeCodec { format: SerdeFormat::CBOR, phantom: PhantomData, } - } + }*/ fn val_to_vec(&self, val: &S) -> io::Result> { match self.format { @@ -190,7 +189,6 @@ where { Behaviour { original: RequestResponseBehaviour::new(codec, protocols, cfg), - phantom: PhantomData, } } diff --git a/protocols/request-response/tests/serde_codecs.rs b/protocols/request-response/tests/cbor.rs similarity index 69% rename from protocols/request-response/tests/serde_codecs.rs rename to protocols/request-response/tests/cbor.rs index 309a6164e8f..25b8cb57d65 100644 --- a/protocols/request-response/tests/serde_codecs.rs +++ b/protocols/request-response/tests/cbor.rs @@ -18,53 +18,28 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -//! Integration tests for the `json::Behaviour`. +//! Integration tests for the `cbor::Behaviour`. -use libp2p_request_response as request_response; -use libp2p_request_response::cbor::Behaviour; -use libp2p_request_response::ProtocolSupport; +use std::iter; use libp2p_swarm::{StreamProtocol, Swarm}; +use libp2p_request_response::ProtocolSupport; +use libp2p_request_response::cbor::Behaviour; use libp2p_swarm_test::SwarmExt; -use rand::{self, Rng}; -use serde::{Deserialize, Serialize}; -use std::iter; - -#[async_std::test] -async fn json() { - run_test(|| { - let protocols = iter::once((StreamProtocol::new("/test_json/1"), ProtocolSupport::Full)); - let cfg = request_response::Config::default(); - - let behaviour: Behaviour = - request_response::json::new_behaviour(protocols, cfg); - - Swarm::new_ephemeral(|_| behaviour) - }) - .await; -} #[async_std::test] async fn cbor() { - run_test(|| { - let protocols = iter::once((StreamProtocol::new("/test_cbor/1"), ProtocolSupport::Full)); - let cfg = request_response::Config::default(); - - let behaviour: Behaviour = - request_response::cbor::new_behaviour(protocols, cfg); + let protocols = iter::once((StreamProtocol::new("/test_cbor/1"), ProtocolSupport::Full)); + let cfg = request_response::Config::default(); - Swarm::new_ephemeral(|_| behaviour) - }) - .await; -} + let behaviour_1: Behaviour = + request_response::cbor::new_behaviour(protocols.clone(), cfg.clone()); -async fn run_test(mut supplier: F) -where - F: FnMut() -> Swarm>, -{ - let mut swarm1 = supplier(); + let mut swarm1 = Swarm::new_ephemeral(|_| behaviour_1); let peer1_id = *swarm1.local_peer_id(); - let mut swarm2 = supplier(); + let behaviour_2: Behaviour = + request_response::cbor::new_behaviour(protocols.clone(), cfg.clone()); + let mut swarm2 = Swarm::new_ephemeral(|_| behaviour_2); let peer2_id = *swarm2.local_peer_id(); swarm1.listen().await; @@ -84,12 +59,12 @@ where loop { match swarm1.next_swarm_event().await.try_into_behaviour_event() { Ok(request_response::Event::Message { - peer, - message: - request_response::Message::Request { - request, channel, .. - }, - }) => { + peer, + message: + request_response::Message::Request { + request, channel, .. + }, + }) => { assert_eq!(&request, &expected_req); assert_eq!(&peer, &peer2_id); swarm1 @@ -128,10 +103,10 @@ where request_response::Event::Message { peer, message: - request_response::Message::Response { - request_id, - response, - }, + request_response::Message::Response { + request_id, + response, + }, } => { count += 1; assert_eq!(&response, &expected_resp); @@ -153,13 +128,3 @@ where async_std::task::spawn(Box::pin(peer1)); peer2.await; } - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -struct TestRequest { - payload: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -struct TestResponse { - payload: String, -} From ade18429bcaf757ebda6cfc17cbdfa79041837c1 Mon Sep 17 00:00:00 2001 From: dgarus Date: Thu, 18 May 2023 14:17:33 +0300 Subject: [PATCH 08/39] Cbor behavior --- protocols/request-response/src/json.rs | 36 -- protocols/request-response/src/lib.rs | 2 - protocols/request-response/src/serde_codec.rs | 319 ------------------ 3 files changed, 357 deletions(-) delete mode 100644 protocols/request-response/src/json.rs delete mode 100644 protocols/request-response/src/serde_codec.rs diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs deleted file mode 100644 index f05165ef1b3..00000000000 --- a/protocols/request-response/src/json.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -pub use crate::serde_codec::Behaviour; - -use crate::serde_codec::SerdeCodec; -use crate::{Codec, Config, ProtocolSupport}; -use serde::{de::DeserializeOwned, Serialize}; - -pub fn new_behaviour(protocols: I, cfg: Config) -> Behaviour -where - I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - let codec: SerdeCodec = SerdeCodec::json(); - - Behaviour::new(codec, protocols, cfg) -} diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index 00fb68802f9..7e973c30c4e 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -61,8 +61,6 @@ pub mod cbor; mod codec; mod handler; -pub mod json; -mod serde_codec; pub use codec::Codec; pub use handler::ProtocolSupport; diff --git a/protocols/request-response/src/serde_codec.rs b/protocols/request-response/src/serde_codec.rs deleted file mode 100644 index 1d8b2244419..00000000000 --- a/protocols/request-response/src/serde_codec.rs +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use crate::{ - Behaviour as RequestResponseBehaviour, Codec, Config, Event, Handler, ProtocolSupport, - RequestId, ResponseChannel, -}; -use async_trait::async_trait; -use futures::prelude::*; -use libp2p_core::{ - upgrade::{read_length_prefixed, write_length_prefixed}, - Endpoint, Multiaddr, -}; -use libp2p_identity::PeerId; -use libp2p_swarm::{ - behaviour::FromSwarm, ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters, - StreamProtocol, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, -}; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - io, - marker::PhantomData, - task::{Context, Poll}, -}; - -const REQUEST_MAXIMUM: usize = 1_000_000; -const RESPONSE_MAXIMUM: usize = 500_000_000; - -#[derive(Debug, Clone)] -enum SerdeFormat { - JSON, - CBOR, -} - -#[derive(Debug, Clone)] -pub struct SerdeCodec { - format: SerdeFormat, - phantom: PhantomData<(Req, Resp)>, -} - -pub struct Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned + 'static, - Resp: Send + Clone + Serialize + DeserializeOwned + 'static, -{ - original: RequestResponseBehaviour>, -} - -impl SerdeCodec -where - Req: Serialize + DeserializeOwned, - Resp: Serialize + DeserializeOwned, -{ - pub(crate) fn json() -> Self { - SerdeCodec { - format: SerdeFormat::JSON, - phantom: PhantomData, - } - } - - /*pub(crate) fn cbor() -> Self { - SerdeCodec { - format: SerdeFormat::CBOR, - phantom: PhantomData, - } - }*/ - - fn val_to_vec(&self, val: &S) -> io::Result> { - match self.format { - SerdeFormat::JSON => serde_json::to_vec(val).map_err(|_| io::ErrorKind::Other.into()), - SerdeFormat::CBOR => serde_cbor::to_vec(val).map_err(|_| io::ErrorKind::Other.into()), - } - } - - fn val_from_slice(&self, vec: &[u8]) -> io::Result { - match self.format { - SerdeFormat::JSON => { - serde_json::from_slice(vec).map_err(|_| io::ErrorKind::Other.into()) - } - SerdeFormat::CBOR => { - serde_cbor::from_slice(vec).map_err(|_| io::ErrorKind::Other.into()) - } - } - } -} - -#[async_trait] -impl Codec for SerdeCodec -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - type Protocol = StreamProtocol; - type Request = Req; - type Response = Resp; - - async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let vec = read_length_prefixed(io, REQUEST_MAXIMUM).await?; - - if vec.is_empty() { - return Err(io::ErrorKind::UnexpectedEof.into()); - } - - self.val_from_slice(vec.as_slice()) - } - - async fn read_response( - &mut self, - _: &Self::Protocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let vec = read_length_prefixed(io, RESPONSE_MAXIMUM).await?; - - if vec.is_empty() { - return Err(io::ErrorKind::UnexpectedEof.into()); - } - - self.val_from_slice(vec.as_slice()) - } - - async fn write_request( - &mut self, - _: &Self::Protocol, - io: &mut T, - req: Self::Request, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - let data = self.val_to_vec(&req)?; - write_length_prefixed(io, data).await?; - io.close().await?; - - Ok(()) - } - - async fn write_response( - &mut self, - _: &Self::Protocol, - io: &mut T, - res: Self::Response, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - let data = self.val_to_vec(&res)?; - write_length_prefixed(io, data).await?; - io.close().await?; - - Ok(()) - } -} - -impl Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - pub(crate) fn new( - codec: SerdeCodec, - protocols: I, - cfg: Config, - ) -> Behaviour - where - I: IntoIterator as Codec>::Protocol, ProtocolSupport)>, - { - Behaviour { - original: RequestResponseBehaviour::new(codec, protocols, cfg), - } - } - - /// Initiates sending a request. - /// - /// If the targeted peer is currently not connected, a dialing - /// attempt is initiated and the request is sent as soon as a - /// connection is established. - /// - /// > **Note**: In order for such a dialing attempt to succeed, - /// > the `RequestResonse` protocol must either be embedded - /// > in another `NetworkBehaviour` that provides peer and - /// > address discovery, or known addresses of peers must be - /// > managed via [`Behaviour::add_address`] and - /// > [`Behaviour::remove_address`]. - pub fn send_request(&mut self, peer: &PeerId, request: Req) -> RequestId { - self.original.send_request(peer, request) - } - - /// Initiates sending a response to an inbound request. - /// - /// If the [`ResponseChannel`] is already closed due to a timeout or the - /// connection being closed, the response is returned as an `Err` for - /// further handling. Once the response has been successfully sent on the - /// corresponding connection, [`Event::ResponseSent`] is - /// emitted. In all other cases [`Event::InboundFailure`] - /// will be or has been emitted. - /// - /// The provided `ResponseChannel` is obtained from an inbound - /// [`Message::Request`]. - pub fn send_response(&mut self, ch: ResponseChannel, rs: Resp) -> Result<(), Resp> { - ch.sender.send(rs) - } - - /// Adds a known address for a peer that can be used for - /// dialing attempts by the `Swarm`, i.e. is returned - /// by [`NetworkBehaviour::handle_pending_outbound_connection`]. - /// - /// Addresses added in this way are only removed by `remove_address`. - pub fn add_address(&mut self, peer: &PeerId, address: Multiaddr) { - self.original.add_address(peer, address) - } - - /// Removes an address of a peer previously added via `add_address`. - pub fn remove_address(&mut self, peer: &PeerId, address: &Multiaddr) { - self.original.remove_address(peer, address) - } - - /// Checks whether a peer is currently connected. - pub fn is_connected(&self, peer: &PeerId) -> bool { - self.original.is_connected(peer) - } - - /// Checks whether an outbound request to the peer with the provided - /// [`PeerId`] initiated by [`Behaviour::send_request`] is still - /// pending, i.e. waiting for a response. - pub fn is_pending_outbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { - self.original.is_pending_outbound(peer, request_id) - } - - /// Checks whether an inbound request from the peer with the provided - /// [`PeerId`] is still pending, i.e. waiting for a response by the local - /// node through [`Behaviour::send_response`]. - pub fn is_pending_inbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { - self.original.is_pending_inbound(peer, request_id) - } -} - -impl NetworkBehaviour for Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - type ConnectionHandler = Handler>; - type ToSwarm = Event; - - fn handle_established_inbound_connection( - &mut self, - _connection_id: ConnectionId, - peer: PeerId, - local_addr: &Multiaddr, - remote_addr: &Multiaddr, - ) -> Result, ConnectionDenied> { - self.original.handle_established_inbound_connection( - _connection_id, - peer, - local_addr, - remote_addr, - ) - } - - fn handle_established_outbound_connection( - &mut self, - _connection_id: ConnectionId, - peer: PeerId, - addr: &Multiaddr, - role_override: Endpoint, - ) -> Result, ConnectionDenied> { - self.original.handle_established_outbound_connection( - _connection_id, - peer, - addr, - role_override, - ) - } - - fn on_swarm_event(&mut self, event: FromSwarm) { - self.original.on_swarm_event(event); - } - - fn on_connection_handler_event( - &mut self, - _peer_id: PeerId, - _connection_id: ConnectionId, - _event: THandlerOutEvent, - ) { - self.original - .on_connection_handler_event(_peer_id, _connection_id, _event); - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - params: &mut impl PollParameters, - ) -> Poll>> { - self.original.poll(cx, params) - } -} From 771560c8b8d68452439981f57104c20868c50894 Mon Sep 17 00:00:00 2001 From: dgarus Date: Thu, 18 May 2023 14:18:11 +0300 Subject: [PATCH 09/39] Format --- protocols/request-response/src/cbor.rs | 64 +++++++++++++----------- protocols/request-response/tests/cbor.rs | 26 +++++----- 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 1bcffd1675f..6537e2e63c3 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -18,19 +18,14 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use std::{ - io, - marker::PhantomData, -}; -use futures::{AsyncRead, AsyncWrite}; +use crate::{Config, ProtocolSupport}; use async_trait::async_trait; +use futures::prelude::*; +use futures::{AsyncRead, AsyncWrite}; +use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; use libp2p_swarm::{NetworkBehaviour, StreamProtocol}; -use crate::{Config, ProtocolSupport}; use serde::{de::DeserializeOwned, Serialize}; -use futures::prelude::*; -use libp2p_core::upgrade::{ - read_length_prefixed, write_length_prefixed, -}; +use std::{io, marker::PhantomData}; #[derive(Debug, Clone)] pub struct Codec { @@ -48,26 +43,26 @@ pub type OutEvent = crate::Event; prelude = "libp2p_swarm::derive_prelude" )] pub struct Behaviour - where - Req: Send + Clone + Serialize + DeserializeOwned + 'static, - Resp: Send + Clone + Serialize + DeserializeOwned + 'static, +where + Req: Send + Clone + Serialize + DeserializeOwned + 'static, + Resp: Send + Clone + Serialize + DeserializeOwned + 'static, { inner: crate::Behaviour>, } #[async_trait] impl crate::Codec for Codec - where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, { type Protocol = StreamProtocol; type Request = Req; type Response = Resp; async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result - where - T: AsyncRead + Unpin + Send, + where + T: AsyncRead + Unpin + Send, { let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; @@ -83,8 +78,8 @@ impl crate::Codec for Codec _: &Self::Protocol, io: &mut T, ) -> io::Result - where - T: AsyncRead + Unpin + Send, + where + T: AsyncRead + Unpin + Send, { let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; @@ -101,8 +96,8 @@ impl crate::Codec for Codec io: &mut T, req: Self::Request, ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, + where + T: AsyncWrite + Unpin + Send, { let data = serde_json::to_vec(&req)?; write_length_prefixed(io, data).await?; @@ -117,8 +112,8 @@ impl crate::Codec for Codec io: &mut T, resp: Self::Response, ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, + where + T: AsyncWrite + Unpin + Send, { let data = serde_json::to_vec(&resp)?; write_length_prefixed(io, data).await?; @@ -129,17 +124,26 @@ impl crate::Codec for Codec } impl Behaviour - where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, { pub fn new(protocols: I, cfg: Config) -> Self - where - I: IntoIterator as crate::Codec>::Protocol, ProtocolSupport)>, + where + I: IntoIterator< + Item = ( + as crate::Codec>::Protocol, + ProtocolSupport, + ), + >, { Behaviour { inner: crate::Behaviour::new( - Codec { phantom: PhantomData }, protocols, cfg, + Codec { + phantom: PhantomData, + }, + protocols, + cfg, ), } } diff --git a/protocols/request-response/tests/cbor.rs b/protocols/request-response/tests/cbor.rs index 25b8cb57d65..216056d0774 100644 --- a/protocols/request-response/tests/cbor.rs +++ b/protocols/request-response/tests/cbor.rs @@ -20,11 +20,11 @@ //! Integration tests for the `cbor::Behaviour`. -use std::iter; -use libp2p_swarm::{StreamProtocol, Swarm}; -use libp2p_request_response::ProtocolSupport; use libp2p_request_response::cbor::Behaviour; +use libp2p_request_response::ProtocolSupport; +use libp2p_swarm::{StreamProtocol, Swarm}; use libp2p_swarm_test::SwarmExt; +use std::iter; #[async_std::test] async fn cbor() { @@ -59,12 +59,12 @@ async fn cbor() { loop { match swarm1.next_swarm_event().await.try_into_behaviour_event() { Ok(request_response::Event::Message { - peer, - message: - request_response::Message::Request { - request, channel, .. - }, - }) => { + peer, + message: + request_response::Message::Request { + request, channel, .. + }, + }) => { assert_eq!(&request, &expected_req); assert_eq!(&peer, &peer2_id); swarm1 @@ -103,10 +103,10 @@ async fn cbor() { request_response::Event::Message { peer, message: - request_response::Message::Response { - request_id, - response, - }, + request_response::Message::Response { + request_id, + response, + }, } => { count += 1; assert_eq!(&response, &expected_resp); From 2a6693e8e34134d63a687667537869756731da16 Mon Sep 17 00:00:00 2001 From: dgarus Date: Thu, 18 May 2023 15:06:51 +0300 Subject: [PATCH 10/39] Fix test --- protocols/request-response/src/cbor.rs | 32 +++++++++++++++++++++++- protocols/request-response/tests/cbor.rs | 21 +++++++++++++--- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 6537e2e63c3..470331d37fc 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -18,11 +18,13 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::{Config, ProtocolSupport}; +use crate::{Config, ProtocolSupport, RequestId, ResponseChannel}; use async_trait::async_trait; use futures::prelude::*; use futures::{AsyncRead, AsyncWrite}; use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; +use libp2p_core::Multiaddr; +use libp2p_identity::PeerId; use libp2p_swarm::{NetworkBehaviour, StreamProtocol}; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; @@ -147,4 +149,32 @@ where ), } } + + pub fn send_request(&mut self, peer: &PeerId, request: Req) -> RequestId { + self.inner.send_request(peer, request) + } + + pub fn send_response(&mut self, ch: ResponseChannel, rs: Resp) -> Result<(), Resp> { + self.inner.send_response(ch, rs) + } + + pub fn add_address(&mut self, peer: &PeerId, address: Multiaddr) { + self.inner.add_address(peer, address) + } + + pub fn remove_address(&mut self, peer: &PeerId, address: &Multiaddr) { + self.inner.remove_address(peer, address) + } + + pub fn is_connected(&self, peer: &PeerId) -> bool { + self.inner.is_connected(peer) + } + + pub fn is_pending_outbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { + self.inner.is_pending_outbound(peer, request_id) + } + + pub fn is_pending_inbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { + self.inner.is_pending_inbound(peer, request_id) + } } diff --git a/protocols/request-response/tests/cbor.rs b/protocols/request-response/tests/cbor.rs index 216056d0774..fd4d795aa91 100644 --- a/protocols/request-response/tests/cbor.rs +++ b/protocols/request-response/tests/cbor.rs @@ -20,25 +20,28 @@ //! Integration tests for the `cbor::Behaviour`. +use libp2p_request_response as request_response; use libp2p_request_response::cbor::Behaviour; -use libp2p_request_response::ProtocolSupport; +use libp2p_request_response::{Config, ProtocolSupport}; use libp2p_swarm::{StreamProtocol, Swarm}; use libp2p_swarm_test::SwarmExt; +use rand::{self, Rng}; +use serde::{Deserialize, Serialize}; use std::iter; #[async_std::test] async fn cbor() { let protocols = iter::once((StreamProtocol::new("/test_cbor/1"), ProtocolSupport::Full)); - let cfg = request_response::Config::default(); + let cfg = Config::default(); let behaviour_1: Behaviour = - request_response::cbor::new_behaviour(protocols.clone(), cfg.clone()); + Behaviour::new(protocols.clone(), cfg.clone()); let mut swarm1 = Swarm::new_ephemeral(|_| behaviour_1); let peer1_id = *swarm1.local_peer_id(); let behaviour_2: Behaviour = - request_response::cbor::new_behaviour(protocols.clone(), cfg.clone()); + Behaviour::new(protocols.clone(), cfg.clone()); let mut swarm2 = Swarm::new_ephemeral(|_| behaviour_2); let peer2_id = *swarm2.local_peer_id(); @@ -128,3 +131,13 @@ async fn cbor() { async_std::task::spawn(Box::pin(peer1)); peer2.await; } + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TestRequest { + payload: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TestResponse { + payload: String, +} From d4bff4cb6e688a4d8b480b7c6b185e0a6edd02d8 Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 19 May 2023 15:28:39 +0300 Subject: [PATCH 11/39] Fix review comments --- Cargo.lock | 2 + protocols/request-response/Cargo.toml | 6 +- protocols/request-response/src/cbor.rs | 165 +++++++++++-- protocols/request-response/src/json.rs | 287 +++++++++++++++++++++++ protocols/request-response/src/lib.rs | 5 +- protocols/request-response/tests/cbor.rs | 143 ----------- 6 files changed, 441 insertions(+), 167 deletions(-) create mode 100644 protocols/request-response/src/json.rs delete mode 100644 protocols/request-response/tests/cbor.rs diff --git a/Cargo.lock b/Cargo.lock index 55155fe384c..b328ed4b4e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2969,11 +2969,13 @@ dependencies = [ "async-trait", "env_logger 0.10.0", "futures", + "futures_ringbuf", "instant", "libp2p-core", "libp2p-identity", "libp2p-noise", "libp2p-swarm", + "libp2p-swarm-derive", "libp2p-swarm-test", "libp2p-tcp", "libp2p-yamux", diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index 1cd9ea00817..17df937252a 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -16,11 +16,12 @@ futures = "0.3.28" instant = "0.1.11" libp2p-core = { workspace = true } libp2p-swarm = { workspace = true } +libp2p-swarm-derive = { workspace = true } libp2p-identity = { workspace = true } rand = "0.8" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.96" -serde_cbor = "0.11.2" +serde_json = { version = "1.0.96", optional = true } +serde_cbor = { version = "0.11.2", optional = true } smallvec = "1.6.1" void = "1.0.2" log = "0.4.17" @@ -33,6 +34,7 @@ libp2p-tcp = { workspace = true, features = ["async-io"] } libp2p-yamux = { workspace = true } rand = "0.8" libp2p-swarm-test = { workspace = true } +futures_ringbuf = "0.3.1" # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 470331d37fc..f3dc287c8cb 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -1,4 +1,4 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. +// Copyright 2023 Protocol Labs // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -25,10 +25,13 @@ use futures::{AsyncRead, AsyncWrite}; use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; -use libp2p_swarm::{NetworkBehaviour, StreamProtocol}; +use libp2p_swarm::StreamProtocol; +use libp2p_swarm_derive::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; +#[cfg(feature = "serde_cbor")] + #[derive(Debug, Clone)] pub struct Codec { phantom: PhantomData<(Req, Resp)>, @@ -52,6 +55,16 @@ where inner: crate::Behaviour>, } +fn convert_error(err: serde_cbor::Error) -> io::Error { + if err.is_syntax() || err.is_data() { + io::Error::new(io::ErrorKind::InvalidData, err) + } else if err.is_eof() { + io::Error::new(io::ErrorKind::UnexpectedEof, err) + } else { + io::Error::new(io::ErrorKind::Other, err) + } +} + #[async_trait] impl crate::Codec for Codec where @@ -68,28 +81,16 @@ where { let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; - if vec.is_empty() { - return Err(io::ErrorKind::UnexpectedEof.into()); - } - - Ok(serde_json::from_slice(vec.as_slice())?) + serde_cbor::from_slice(vec.as_slice()).map_err(|e| convert_error(e)) } - async fn read_response( - &mut self, - _: &Self::Protocol, - io: &mut T, - ) -> io::Result + async fn read_response(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result where T: AsyncRead + Unpin + Send, { let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; - if vec.is_empty() { - return Err(io::ErrorKind::UnexpectedEof.into()); - } - - Ok(serde_json::from_slice(vec.as_slice())?) + serde_cbor::from_slice(vec.as_slice()).map_err(|e| convert_error(e)) } async fn write_request( @@ -101,8 +102,10 @@ where where T: AsyncWrite + Unpin + Send, { - let data = serde_json::to_vec(&req)?; - write_length_prefixed(io, data).await?; + let data: Vec = serde_cbor::to_vec(&req) + .map_err(|e| convert_error(e)) + .unwrap(); + write_length_prefixed(io, data.as_slice()).await?; io.close().await?; Ok(()) @@ -117,8 +120,10 @@ where where T: AsyncWrite + Unpin + Send, { - let data = serde_json::to_vec(&resp)?; - write_length_prefixed(io, data).await?; + let data: Vec = serde_cbor::to_vec(&resp) + .map_err(|e| convert_error(e)) + .unwrap(); + write_length_prefixed(io, data.as_slice()).await?; io.close().await?; Ok(()) @@ -178,3 +183,121 @@ where self.inner.is_pending_inbound(peer, request_id) } } + +#[cfg(test)] +mod tests { + use crate::Codec; + use futures_ringbuf::Endpoint; + use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; + use libp2p_swarm::StreamProtocol; + use serde::{Deserialize, Serialize}; + use std::marker::PhantomData; + + #[async_std::test] + async fn test_read_request() { + let (mut a, mut b) = Endpoint::pair(124, 124); + let expected = TestRequest { + payload: "test_payload".to_string(), + }; + let data = serde_cbor::to_vec(&expected).expect("Should serialize"); + + write_length_prefixed(&mut a, data.as_slice()) + .await + .expect("Should write"); + + let protocol = StreamProtocol::new("/test_json/1"); + let mut codec: super::Codec = super::Codec { + phantom: PhantomData, + }; + let actual = codec + .read_request(&protocol, &mut b) + .await + .expect("Should read"); + + assert_eq!(actual, expected); + } + + #[async_std::test] + async fn test_read_response() { + let (mut a, mut b) = Endpoint::pair(124, 124); + let expected = TestResponse { + payload: "test_payload".to_string(), + }; + let data = serde_cbor::to_vec(&expected).expect("Should serialize"); + + write_length_prefixed(&mut a, data.as_slice()) + .await + .expect("Should write"); + + let protocol = StreamProtocol::new("/test_json/1"); + let mut codec: super::Codec = super::Codec { + phantom: PhantomData, + }; + let actual = codec + .read_response(&protocol, &mut b) + .await + .expect("Should read"); + + assert_eq!(actual, expected); + } + + #[async_std::test] + async fn test_write_request() { + let (mut a, mut b) = Endpoint::pair(124, 124); + let expected = TestRequest { + payload: "test_payload".to_string(), + }; + let protocol = StreamProtocol::new("/test_json/1"); + let mut codec: super::Codec = super::Codec { + phantom: PhantomData, + }; + + codec + .write_request(&protocol, &mut a, expected.clone()) + .await + .expect("Should write"); + + let data = read_length_prefixed(&mut b, 1024) + .await + .expect("Should read"); + let actual: TestRequest = + serde_cbor::from_slice(data.as_slice()).expect("Should deserialize"); + + assert_eq!(actual, expected); + } + + #[async_std::test] + async fn test_write_response() { + let (mut a, mut b) = Endpoint::pair(124, 124); + let expected = TestResponse { + payload: "test_payload".to_string(), + }; + let protocol = StreamProtocol::new("/test_json/1"); + let mut codec: super::Codec = super::Codec { + phantom: PhantomData, + }; + + codec + .write_response(&protocol, &mut a, expected.clone()) + .await + .expect("Should write"); + + let data = read_length_prefixed(&mut b, 1024) + .await + .expect("Should read"); + let actual: TestResponse = + serde_cbor::from_slice(data.as_slice()).expect("Should deserialize"); + + assert_eq!(actual, expected); + } + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct TestRequest { + payload: String, + } + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct TestResponse { + payload: String, + } +} diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs new file mode 100644 index 00000000000..d06198db5fe --- /dev/null +++ b/protocols/request-response/src/json.rs @@ -0,0 +1,287 @@ +// Copyright 2023 Protocol Labs +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::{Config, ProtocolSupport, RequestId, ResponseChannel}; +use async_trait::async_trait; +use futures::prelude::*; +use futures::{AsyncRead, AsyncWrite}; +use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; +use libp2p_core::Multiaddr; +use libp2p_identity::PeerId; +use libp2p_swarm::StreamProtocol; +use libp2p_swarm_derive::NetworkBehaviour; +use serde::{de::DeserializeOwned, Serialize}; +use std::{io, marker::PhantomData}; + +#[derive(Debug, Clone)] +pub struct Codec { + phantom: PhantomData<(Req, Resp)>, +} + +const REQUEST_SIZE_MAXIMUM: usize = 1_000_000; +const RESPONSE_SIZE_MAXIMUM: usize = 500_000_000; + +pub type OutEvent = crate::Event; + +#[derive(NetworkBehaviour)] +#[behaviour( + to_swarm = "OutEvent", + prelude = "libp2p_swarm::derive_prelude" +)] +pub struct Behaviour +where + Req: Send + Clone + Serialize + DeserializeOwned + 'static, + Resp: Send + Clone + Serialize + DeserializeOwned + 'static, +{ + inner: crate::Behaviour>, +} + +#[async_trait] +impl crate::Codec for Codec +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + type Protocol = StreamProtocol; + type Request = Req; + type Response = Resp; + + async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; + + Ok(serde_json::from_slice(vec.as_slice())?) + } + + async fn read_response(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; + + Ok(serde_json::from_slice(vec.as_slice())?) + } + + async fn write_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let data = serde_json::to_vec(&req)?; + write_length_prefixed(io, data.as_slice()).await?; + io.close().await?; + + Ok(()) + } + + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + resp: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let data = serde_json::to_vec(&resp)?; + write_length_prefixed(io, data.as_slice()).await?; + io.close().await?; + + Ok(()) + } +} + +impl Behaviour +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + pub fn new(protocols: I, cfg: Config) -> Self + where + I: IntoIterator< + Item = ( + as crate::Codec>::Protocol, + ProtocolSupport, + ), + >, + { + Behaviour { + inner: crate::Behaviour::new( + Codec { + phantom: PhantomData, + }, + protocols, + cfg, + ), + } + } + + pub fn send_request(&mut self, peer: &PeerId, request: Req) -> RequestId { + self.inner.send_request(peer, request) + } + + pub fn send_response(&mut self, ch: ResponseChannel, rs: Resp) -> Result<(), Resp> { + self.inner.send_response(ch, rs) + } + + pub fn add_address(&mut self, peer: &PeerId, address: Multiaddr) { + self.inner.add_address(peer, address) + } + + pub fn remove_address(&mut self, peer: &PeerId, address: &Multiaddr) { + self.inner.remove_address(peer, address) + } + + pub fn is_connected(&self, peer: &PeerId) -> bool { + self.inner.is_connected(peer) + } + + pub fn is_pending_outbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { + self.inner.is_pending_outbound(peer, request_id) + } + + pub fn is_pending_inbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { + self.inner.is_pending_inbound(peer, request_id) + } +} + +#[cfg(test)] +mod tests { + use crate::Codec; + use futures_ringbuf::Endpoint; + use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; + use libp2p_swarm::StreamProtocol; + use serde::{Deserialize, Serialize}; + use std::marker::PhantomData; + + #[async_std::test] + async fn test_read_request() { + let (mut a, mut b) = Endpoint::pair(124, 124); + let expected = TestRequest { + payload: "test_payload".to_string(), + }; + let data = serde_json::to_vec(&expected).expect("Should serialize"); + + write_length_prefixed(&mut a, data.as_slice()) + .await + .expect("Should write"); + + let protocol = StreamProtocol::new("/test_json/1"); + let mut codec: super::Codec = super::Codec { + phantom: PhantomData, + }; + let actual = codec + .read_request(&protocol, &mut b) + .await + .expect("Should read"); + + assert_eq!(actual, expected); + } + + #[async_std::test] + async fn test_read_response() { + let (mut a, mut b) = Endpoint::pair(124, 124); + let expected = TestResponse { + payload: "test_payload".to_string(), + }; + let data = serde_json::to_vec(&expected).expect("Should serialize"); + + write_length_prefixed(&mut a, data.as_slice()) + .await + .expect("Should write"); + + let protocol = StreamProtocol::new("/test_json/1"); + let mut codec: super::Codec = super::Codec { + phantom: PhantomData, + }; + let actual = codec + .read_response(&protocol, &mut b) + .await + .expect("Should read"); + + assert_eq!(actual, expected); + } + + #[async_std::test] + async fn test_write_request() { + let (mut a, mut b) = Endpoint::pair(124, 124); + let expected = TestRequest { + payload: "test_payload".to_string(), + }; + let protocol = StreamProtocol::new("/test_json/1"); + let mut codec: super::Codec = super::Codec { + phantom: PhantomData, + }; + + codec + .write_request(&protocol, &mut a, expected.clone()) + .await + .expect("Should write"); + + let data = read_length_prefixed(&mut b, 1024) + .await + .expect("Should read"); + let actual: TestRequest = + serde_json::from_slice(data.as_slice()).expect("Should deserialize"); + + assert_eq!(actual, expected); + } + + #[async_std::test] + async fn test_write_response() { + let (mut a, mut b) = Endpoint::pair(124, 124); + let expected = TestResponse { + payload: "test_payload".to_string(), + }; + let protocol = StreamProtocol::new("/test_json/1"); + let mut codec: super::Codec = super::Codec { + phantom: PhantomData, + }; + + codec + .write_response(&protocol, &mut a, expected.clone()) + .await + .expect("Should write"); + + let data = read_length_prefixed(&mut b, 1024) + .await + .expect("Should read"); + let actual: TestResponse = + serde_json::from_slice(data.as_slice()).expect("Should deserialize"); + + assert_eq!(actual, expected); + } + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct TestRequest { + payload: String, + } + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct TestResponse { + payload: String, + } +} diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index 7e973c30c4e..31d2683582c 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. +// Copyright 2023 Protocol Labs // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -58,9 +58,12 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#[cfg(feature = "serde_cbor")] pub mod cbor; mod codec; mod handler; +#[cfg(feature = "serde_json")] +pub mod json; pub use codec::Codec; pub use handler::ProtocolSupport; diff --git a/protocols/request-response/tests/cbor.rs b/protocols/request-response/tests/cbor.rs deleted file mode 100644 index fd4d795aa91..00000000000 --- a/protocols/request-response/tests/cbor.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -//! Integration tests for the `cbor::Behaviour`. - -use libp2p_request_response as request_response; -use libp2p_request_response::cbor::Behaviour; -use libp2p_request_response::{Config, ProtocolSupport}; -use libp2p_swarm::{StreamProtocol, Swarm}; -use libp2p_swarm_test::SwarmExt; -use rand::{self, Rng}; -use serde::{Deserialize, Serialize}; -use std::iter; - -#[async_std::test] -async fn cbor() { - let protocols = iter::once((StreamProtocol::new("/test_cbor/1"), ProtocolSupport::Full)); - let cfg = Config::default(); - - let behaviour_1: Behaviour = - Behaviour::new(protocols.clone(), cfg.clone()); - - let mut swarm1 = Swarm::new_ephemeral(|_| behaviour_1); - let peer1_id = *swarm1.local_peer_id(); - - let behaviour_2: Behaviour = - Behaviour::new(protocols.clone(), cfg.clone()); - let mut swarm2 = Swarm::new_ephemeral(|_| behaviour_2); - let peer2_id = *swarm2.local_peer_id(); - - swarm1.listen().await; - swarm2.connect(&mut swarm1).await; - - let test_req = TestRequest { - payload: "test_request".to_string(), - }; - let test_resp = TestResponse { - payload: "test_response".to_string(), - }; - - let expected_req = test_req.clone(); - let expected_resp = test_resp.clone(); - - let peer1 = async move { - loop { - match swarm1.next_swarm_event().await.try_into_behaviour_event() { - Ok(request_response::Event::Message { - peer, - message: - request_response::Message::Request { - request, channel, .. - }, - }) => { - assert_eq!(&request, &expected_req); - assert_eq!(&peer, &peer2_id); - swarm1 - .behaviour_mut() - .send_response(channel, test_resp.clone()) - .unwrap(); - } - Ok(request_response::Event::ResponseSent { peer, .. }) => { - assert_eq!(&peer, &peer2_id); - } - Ok(e) => { - panic!("Peer1: Unexpected event: {e:?}") - } - Err(..) => {} - } - } - }; - - let num_requests: u8 = rand::thread_rng().gen_range(1..100); - - let peer2 = async { - let mut count = 0; - - let mut req_id = swarm2 - .behaviour_mut() - .send_request(&peer1_id, test_req.clone()); - assert!(swarm2.behaviour().is_pending_outbound(&peer1_id, &req_id)); - - loop { - match swarm2 - .next_swarm_event() - .await - .try_into_behaviour_event() - .unwrap() - { - request_response::Event::Message { - peer, - message: - request_response::Message::Response { - request_id, - response, - }, - } => { - count += 1; - assert_eq!(&response, &expected_resp); - assert_eq!(&peer, &peer1_id); - assert_eq!(req_id, request_id); - if count >= num_requests { - return; - } else { - req_id = swarm2 - .behaviour_mut() - .send_request(&peer1_id, test_req.clone()); - } - } - e => panic!("Peer2: Unexpected event: {e:?}"), - } - } - }; - - async_std::task::spawn(Box::pin(peer1)); - peer2.await; -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TestRequest { - payload: String, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct TestResponse { - payload: String, -} From 92d9862a2a0e43e1f8f5f1a5225b56e27acdeaec Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 19 May 2023 15:54:45 +0300 Subject: [PATCH 12/39] Fix tests running --- protocols/request-response/Cargo.toml | 4 ++-- protocols/request-response/src/cbor.rs | 2 -- protocols/request-response/src/lib.rs | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index 17df937252a..5808f92e529 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -20,8 +20,8 @@ libp2p-swarm-derive = { workspace = true } libp2p-identity = { workspace = true } rand = "0.8" serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0.96", optional = true } -serde_cbor = { version = "0.11.2", optional = true } +serde_json = { version = "1.0.96" } +serde_cbor = { version = "0.11.2" } smallvec = "1.6.1" void = "1.0.2" log = "0.4.17" diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index f3dc287c8cb..093bfd91045 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -30,8 +30,6 @@ use libp2p_swarm_derive::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; -#[cfg(feature = "serde_cbor")] - #[derive(Debug, Clone)] pub struct Codec { phantom: PhantomData<(Req, Resp)>, diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index 31d2683582c..c66c195a5eb 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -58,11 +58,9 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#[cfg(feature = "serde_cbor")] pub mod cbor; mod codec; mod handler; -#[cfg(feature = "serde_json")] pub mod json; pub use codec::Codec; From a0b8ee5effb96325363cc25e90ab870bc39a57b8 Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 19 May 2023 16:27:57 +0300 Subject: [PATCH 13/39] Fix tests running --- protocols/request-response/src/cbor.rs | 12 ++++++------ protocols/request-response/src/json.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 093bfd91045..3d0e224bb48 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -203,7 +203,7 @@ mod tests { .await .expect("Should write"); - let protocol = StreamProtocol::new("/test_json/1"); + let protocol = StreamProtocol::new("/test_cbor/1"); let mut codec: super::Codec = super::Codec { phantom: PhantomData, }; @@ -227,7 +227,7 @@ mod tests { .await .expect("Should write"); - let protocol = StreamProtocol::new("/test_json/1"); + let protocol = StreamProtocol::new("/test_cbor/1"); let mut codec: super::Codec = super::Codec { phantom: PhantomData, }; @@ -245,7 +245,7 @@ mod tests { let expected = TestRequest { payload: "test_payload".to_string(), }; - let protocol = StreamProtocol::new("/test_json/1"); + let protocol = StreamProtocol::new("/test_cbor/1"); let mut codec: super::Codec = super::Codec { phantom: PhantomData, }; @@ -255,7 +255,7 @@ mod tests { .await .expect("Should write"); - let data = read_length_prefixed(&mut b, 1024) + let data = read_length_prefixed(&mut b, 124) .await .expect("Should read"); let actual: TestRequest = @@ -270,7 +270,7 @@ mod tests { let expected = TestResponse { payload: "test_payload".to_string(), }; - let protocol = StreamProtocol::new("/test_json/1"); + let protocol = StreamProtocol::new("/test_cbor/1"); let mut codec: super::Codec = super::Codec { phantom: PhantomData, }; @@ -280,7 +280,7 @@ mod tests { .await .expect("Should write"); - let data = read_length_prefixed(&mut b, 1024) + let data = read_length_prefixed(&mut b, 124) .await .expect("Should read"); let actual: TestResponse = diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index d06198db5fe..07932fddf98 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -241,7 +241,7 @@ mod tests { .await .expect("Should write"); - let data = read_length_prefixed(&mut b, 1024) + let data = read_length_prefixed(&mut b, 124) .await .expect("Should read"); let actual: TestRequest = @@ -266,7 +266,7 @@ mod tests { .await .expect("Should write"); - let data = read_length_prefixed(&mut b, 1024) + let data = read_length_prefixed(&mut b, 124) .await .expect("Should read"); let actual: TestResponse = From 1129dee443dc0d4a0f01c23316a94fed519cfaf6 Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 19 May 2023 17:10:22 +0300 Subject: [PATCH 14/39] Added features `json` and `cbor` --- protocols/request-response/Cargo.toml | 11 ++++++++--- protocols/request-response/src/lib.rs | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index 5808f92e529..e6ba38f8b97 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -19,13 +19,18 @@ libp2p-swarm = { workspace = true } libp2p-swarm-derive = { workspace = true } libp2p-identity = { workspace = true } rand = "0.8" -serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0.96" } -serde_cbor = { version = "0.11.2" } +serde = { version = "1.0", optional = true, features = ["derive"] } +serde_json = { version = "1.0.96", optional = true } +serde_cbor = { version = "0.11.2", optional = true } smallvec = "1.6.1" void = "1.0.2" log = "0.4.17" +[features] +default = ["json"] +json = ["dep:serde", "dep:serde_json"] +cbor = ["dep:serde", "dep:serde_cbor"] + [dev-dependencies] async-std = { version = "1.6.2", features = ["attributes"] } env_logger = "0.10.0" diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index c66c195a5eb..f4481716805 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -58,9 +58,11 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#[cfg(feature = "cbor")] pub mod cbor; mod codec; mod handler; +#[cfg(feature = "json")] pub mod json; pub use codec::Codec; From a8f9ee914b546236a4725f0212c5e96b51476934 Mon Sep 17 00:00:00 2001 From: dgarus Date: Fri, 19 May 2023 17:26:07 +0300 Subject: [PATCH 15/39] CHANGELOG --- protocols/request-response/CHANGELOG.md | 6 +++++- protocols/request-response/src/cbor.rs | 8 +++++--- protocols/request-response/src/json.rs | 8 +++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/protocols/request-response/CHANGELOG.md b/protocols/request-response/CHANGELOG.md index 442906f4621..4cfadde7ed5 100644 --- a/protocols/request-response/CHANGELOG.md +++ b/protocols/request-response/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.25.0 - unreleased +- Implemented `request_response::json::Behaviour` and `request_response::cbor::Behaviour` which + use `json` and `cbor` representing of request/response, added according features. + See [PR 3952]. + - Raise MSRV to 1.65. See [PR 3715]. - Remove deprecated `RequestResponse` prefixed items. See [PR 3702]. @@ -8,7 +12,7 @@ These variants are no longer constructed. See [PR 3605]. -- Don't close connections if individual streams fail. +- Don't close connections if individual streams fail. Log the error instead. See [PR 3913]. diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 3d0e224bb48..17d20677a5a 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -30,14 +30,16 @@ use libp2p_swarm_derive::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; +/// Max request size in bytes +const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; +/// Max response size in bytes +const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; + #[derive(Debug, Clone)] pub struct Codec { phantom: PhantomData<(Req, Resp)>, } -const REQUEST_SIZE_MAXIMUM: usize = 1_000_000; -const RESPONSE_SIZE_MAXIMUM: usize = 500_000_000; - pub type OutEvent = crate::Event; #[derive(NetworkBehaviour)] diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 07932fddf98..2f09f042be2 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -30,14 +30,16 @@ use libp2p_swarm_derive::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; +/// Max request size in bytes +const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; +/// Max response size in bytes +const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; + #[derive(Debug, Clone)] pub struct Codec { phantom: PhantomData<(Req, Resp)>, } -const REQUEST_SIZE_MAXIMUM: usize = 1_000_000; -const RESPONSE_SIZE_MAXIMUM: usize = 500_000_000; - pub type OutEvent = crate::Event; #[derive(NetworkBehaviour)] From 36eaf029a223a4657c59de160da36d5f7bdea8e2 Mon Sep 17 00:00:00 2001 From: Denis Garus Date: Sat, 20 May 2023 14:49:09 +0300 Subject: [PATCH 16/39] Update protocols/request-response/CHANGELOG.md Co-authored-by: Thomas Eizinger --- protocols/request-response/CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/request-response/CHANGELOG.md b/protocols/request-response/CHANGELOG.md index 4cfadde7ed5..e2cc6551045 100644 --- a/protocols/request-response/CHANGELOG.md +++ b/protocols/request-response/CHANGELOG.md @@ -1,7 +1,6 @@ ## 0.25.0 - unreleased -- Implemented `request_response::json::Behaviour` and `request_response::cbor::Behaviour` which - use `json` and `cbor` representing of request/response, added according features. +- Add `request_response::json::Behaviour` and `request_response::cbor::Behaviour` building on top of the `serde` traits. See [PR 3952]. - Raise MSRV to 1.65. From 61f7d48255a0779442b131d647ee2b1e2a404b5e Mon Sep 17 00:00:00 2001 From: Denis Garus Date: Sat, 20 May 2023 14:49:39 +0300 Subject: [PATCH 17/39] Update protocols/request-response/src/cbor.rs Co-authored-by: Thomas Eizinger --- protocols/request-response/src/cbor.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 17d20677a5a..66d578e1a12 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -57,12 +57,14 @@ where fn convert_error(err: serde_cbor::Error) -> io::Error { if err.is_syntax() || err.is_data() { - io::Error::new(io::ErrorKind::InvalidData, err) - } else if err.is_eof() { - io::Error::new(io::ErrorKind::UnexpectedEof, err) - } else { - io::Error::new(io::ErrorKind::Other, err) + return io::Error::new(io::ErrorKind::InvalidData, err) } + + if err.is_eof() { + return io::Error::new(io::ErrorKind::UnexpectedEof, err) + } + + io::Error::new(io::ErrorKind::Other, err) } #[async_trait] From d78c17b5f0341f994ab9805c6ff793a5bc56d9d9 Mon Sep 17 00:00:00 2001 From: Denis Garus Date: Sat, 20 May 2023 14:50:03 +0300 Subject: [PATCH 18/39] Update protocols/request-response/src/cbor.rs Co-authored-by: Thomas Eizinger --- protocols/request-response/src/cbor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 66d578e1a12..abc30dcb180 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -92,7 +92,7 @@ where { let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; - serde_cbor::from_slice(vec.as_slice()).map_err(|e| convert_error(e)) + serde_cbor::from_slice(vec.as_slice()).map_err(convert_error) } async fn write_request( From 0ee31bd04f339f7106ddfbf6bdd3440329b82123 Mon Sep 17 00:00:00 2001 From: Denis Garus Date: Sat, 20 May 2023 14:50:20 +0300 Subject: [PATCH 19/39] Update protocols/request-response/src/cbor.rs Co-authored-by: Thomas Eizinger --- protocols/request-response/src/cbor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index abc30dcb180..7d0e619a48c 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -55,7 +55,7 @@ where inner: crate::Behaviour>, } -fn convert_error(err: serde_cbor::Error) -> io::Error { +fn into_io_error(err: serde_cbor::Error) -> io::Error { if err.is_syntax() || err.is_data() { return io::Error::new(io::ErrorKind::InvalidData, err) } From d4044a58fe32eaa8b4190a085784cd073544c034 Mon Sep 17 00:00:00 2001 From: Denis Garus Date: Sat, 20 May 2023 14:50:56 +0300 Subject: [PATCH 20/39] Update protocols/request-response/src/cbor.rs Co-authored-by: Thomas Eizinger --- protocols/request-response/src/cbor.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 7d0e619a48c..0ab94bd25b8 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -105,8 +105,7 @@ where T: AsyncWrite + Unpin + Send, { let data: Vec = serde_cbor::to_vec(&req) - .map_err(|e| convert_error(e)) - .unwrap(); + .map_err(|e| convert_error(e))?; write_length_prefixed(io, data.as_slice()).await?; io.close().await?; From 416f271c99e440da046088f3fcd1d14a5e803fc2 Mon Sep 17 00:00:00 2001 From: dgarus Date: Sat, 20 May 2023 19:44:32 +0300 Subject: [PATCH 21/39] fix review comments --- protocols/request-response/CHANGELOG.md | 1 + protocols/request-response/src/cbor.rs | 110 +++++------------------- protocols/request-response/src/json.rs | 93 ++++---------------- protocols/request-response/src/lib.rs | 2 +- 4 files changed, 41 insertions(+), 165 deletions(-) diff --git a/protocols/request-response/CHANGELOG.md b/protocols/request-response/CHANGELOG.md index e2cc6551045..a4973e602ce 100644 --- a/protocols/request-response/CHANGELOG.md +++ b/protocols/request-response/CHANGELOG.md @@ -15,6 +15,7 @@ Log the error instead. See [PR 3913]. +[PR 3952]: https://github.com/libp2p/rust-libp2p/pull/3952 [PR 3605]: https://github.com/libp2p/rust-libp2p/pull/3605 [PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715 [PR 3702]: https://github.com/libp2p/rust-libp2p/pull/3702 diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 0ab94bd25b8..683679ecf25 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -57,13 +57,13 @@ where fn into_io_error(err: serde_cbor::Error) -> io::Error { if err.is_syntax() || err.is_data() { - return io::Error::new(io::ErrorKind::InvalidData, err) + return io::Error::new(io::ErrorKind::InvalidData, err); } - + if err.is_eof() { - return io::Error::new(io::ErrorKind::UnexpectedEof, err) + return io::Error::new(io::ErrorKind::UnexpectedEof, err); } - + io::Error::new(io::ErrorKind::Other, err) } @@ -83,7 +83,7 @@ where { let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; - serde_cbor::from_slice(vec.as_slice()).map_err(|e| convert_error(e)) + serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) } async fn read_response(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result @@ -92,7 +92,7 @@ where { let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; - serde_cbor::from_slice(vec.as_slice()).map_err(convert_error) + serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) } async fn write_request( @@ -104,8 +104,7 @@ where where T: AsyncWrite + Unpin + Send, { - let data: Vec = serde_cbor::to_vec(&req) - .map_err(|e| convert_error(e))?; + let data: Vec = serde_cbor::to_vec(&req).map_err(into_io_error)?; write_length_prefixed(io, data.as_slice()).await?; io.close().await?; @@ -121,9 +120,7 @@ where where T: AsyncWrite + Unpin + Send, { - let data: Vec = serde_cbor::to_vec(&resp) - .map_err(|e| convert_error(e)) - .unwrap(); + let data: Vec = serde_cbor::to_vec(&resp).map_err(into_io_error).unwrap(); write_length_prefixed(io, data.as_slice()).await?; io.close().await?; @@ -189,107 +186,46 @@ where mod tests { use crate::Codec; use futures_ringbuf::Endpoint; - use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; use libp2p_swarm::StreamProtocol; use serde::{Deserialize, Serialize}; use std::marker::PhantomData; #[async_std::test] - async fn test_read_request() { - let (mut a, mut b) = Endpoint::pair(124, 124); - let expected = TestRequest { + async fn test_codec() { + let expected_request = TestRequest { payload: "test_payload".to_string(), }; - let data = serde_cbor::to_vec(&expected).expect("Should serialize"); - - write_length_prefixed(&mut a, data.as_slice()) - .await - .expect("Should write"); - - let protocol = StreamProtocol::new("/test_cbor/1"); - let mut codec: super::Codec = super::Codec { - phantom: PhantomData, - }; - let actual = codec - .read_request(&protocol, &mut b) - .await - .expect("Should read"); - - assert_eq!(actual, expected); - } - - #[async_std::test] - async fn test_read_response() { - let (mut a, mut b) = Endpoint::pair(124, 124); - let expected = TestResponse { + let expected_response = TestResponse { payload: "test_payload".to_string(), }; - let data = serde_cbor::to_vec(&expected).expect("Should serialize"); - - write_length_prefixed(&mut a, data.as_slice()) - .await - .expect("Should write"); - let protocol = StreamProtocol::new("/test_cbor/1"); let mut codec: super::Codec = super::Codec { phantom: PhantomData, }; - let actual = codec - .read_response(&protocol, &mut b) - .await - .expect("Should read"); - assert_eq!(actual, expected); - } - - #[async_std::test] - async fn test_write_request() { let (mut a, mut b) = Endpoint::pair(124, 124); - let expected = TestRequest { - payload: "test_payload".to_string(), - }; - let protocol = StreamProtocol::new("/test_cbor/1"); - let mut codec: super::Codec = super::Codec { - phantom: PhantomData, - }; - codec - .write_request(&protocol, &mut a, expected.clone()) + .write_request(&protocol, &mut a, expected_request.clone()) .await - .expect("Should write"); - - let data = read_length_prefixed(&mut b, 124) + .expect("Should write request"); + let actual_request = codec + .read_request(&protocol, &mut b) .await - .expect("Should read"); - let actual: TestRequest = - serde_cbor::from_slice(data.as_slice()).expect("Should deserialize"); + .expect("Should read request"); - assert_eq!(actual, expected); - } + assert_eq!(actual_request, expected_request); - #[async_std::test] - async fn test_write_response() { let (mut a, mut b) = Endpoint::pair(124, 124); - let expected = TestResponse { - payload: "test_payload".to_string(), - }; - let protocol = StreamProtocol::new("/test_cbor/1"); - let mut codec: super::Codec = super::Codec { - phantom: PhantomData, - }; - codec - .write_response(&protocol, &mut a, expected.clone()) + .write_response(&protocol, &mut a, expected_response.clone()) .await - .expect("Should write"); - - let data = read_length_prefixed(&mut b, 124) + .expect("Should write response"); + let actual_response = codec + .read_response(&protocol, &mut b) .await - .expect("Should read"); - let actual: TestResponse = - serde_cbor::from_slice(data.as_slice()).expect("Should deserialize"); + .expect("Should read response"); - assert_eq!(actual, expected); + assert_eq!(actual_response, expected_response); } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 2f09f042be2..0253daa79d0 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -174,107 +174,46 @@ where mod tests { use crate::Codec; use futures_ringbuf::Endpoint; - use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; use libp2p_swarm::StreamProtocol; use serde::{Deserialize, Serialize}; use std::marker::PhantomData; #[async_std::test] - async fn test_read_request() { - let (mut a, mut b) = Endpoint::pair(124, 124); - let expected = TestRequest { + async fn test_codec() { + let expected_request = TestRequest { payload: "test_payload".to_string(), }; - let data = serde_json::to_vec(&expected).expect("Should serialize"); - - write_length_prefixed(&mut a, data.as_slice()) - .await - .expect("Should write"); - - let protocol = StreamProtocol::new("/test_json/1"); - let mut codec: super::Codec = super::Codec { - phantom: PhantomData, - }; - let actual = codec - .read_request(&protocol, &mut b) - .await - .expect("Should read"); - - assert_eq!(actual, expected); - } - - #[async_std::test] - async fn test_read_response() { - let (mut a, mut b) = Endpoint::pair(124, 124); - let expected = TestResponse { + let expected_response = TestResponse { payload: "test_payload".to_string(), }; - let data = serde_json::to_vec(&expected).expect("Should serialize"); - - write_length_prefixed(&mut a, data.as_slice()) - .await - .expect("Should write"); - - let protocol = StreamProtocol::new("/test_json/1"); + let protocol = StreamProtocol::new("/test_cbor/1"); let mut codec: super::Codec = super::Codec { phantom: PhantomData, }; - let actual = codec - .read_response(&protocol, &mut b) - .await - .expect("Should read"); - - assert_eq!(actual, expected); - } - #[async_std::test] - async fn test_write_request() { let (mut a, mut b) = Endpoint::pair(124, 124); - let expected = TestRequest { - payload: "test_payload".to_string(), - }; - let protocol = StreamProtocol::new("/test_json/1"); - let mut codec: super::Codec = super::Codec { - phantom: PhantomData, - }; - codec - .write_request(&protocol, &mut a, expected.clone()) + .write_request(&protocol, &mut a, expected_request.clone()) .await - .expect("Should write"); - - let data = read_length_prefixed(&mut b, 124) + .expect("Should write request"); + let actual_request = codec + .read_request(&protocol, &mut b) .await - .expect("Should read"); - let actual: TestRequest = - serde_json::from_slice(data.as_slice()).expect("Should deserialize"); + .expect("Should read request"); - assert_eq!(actual, expected); - } + assert_eq!(actual_request, expected_request); - #[async_std::test] - async fn test_write_response() { let (mut a, mut b) = Endpoint::pair(124, 124); - let expected = TestResponse { - payload: "test_payload".to_string(), - }; - let protocol = StreamProtocol::new("/test_json/1"); - let mut codec: super::Codec = super::Codec { - phantom: PhantomData, - }; - codec - .write_response(&protocol, &mut a, expected.clone()) + .write_response(&protocol, &mut a, expected_response.clone()) .await - .expect("Should write"); - - let data = read_length_prefixed(&mut b, 124) + .expect("Should write response"); + let actual_response = codec + .read_response(&protocol, &mut b) .await - .expect("Should read"); - let actual: TestResponse = - serde_json::from_slice(data.as_slice()).expect("Should deserialize"); + .expect("Should read response"); - assert_eq!(actual, expected); + assert_eq!(actual_response, expected_response); } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index f4481716805..6d395a6495e 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2023 Protocol Labs +// Copyright 2020 Parity Technologies (UK) Ltd. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), From 9d20aaa7e7d85aadc9e88d0ea30f74ff86ff7c91 Mon Sep 17 00:00:00 2001 From: dgarus Date: Mon, 22 May 2023 13:22:41 +0300 Subject: [PATCH 22/39] fix serde feature should be in the test scope --- protocols/request-response/Cargo.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index e6ba38f8b97..c39114c91db 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -19,7 +19,7 @@ libp2p-swarm = { workspace = true } libp2p-swarm-derive = { workspace = true } libp2p-identity = { workspace = true } rand = "0.8" -serde = { version = "1.0", optional = true, features = ["derive"] } +serde = { version = "1.0", optional = true} serde_json = { version = "1.0.96", optional = true } serde_cbor = { version = "0.11.2", optional = true } smallvec = "1.6.1" @@ -27,9 +27,9 @@ void = "1.0.2" log = "0.4.17" [features] -default = ["json"] -json = ["dep:serde", "dep:serde_json"] -cbor = ["dep:serde", "dep:serde_cbor"] +default = ["json", "cbor"] +json = ["dep:serde", "dep:serde_json", "libp2p-swarm/macros"] +cbor = ["dep:serde", "dep:serde_cbor", "libp2p-swarm/macros"] [dev-dependencies] async-std = { version = "1.6.2", features = ["attributes"] } @@ -40,6 +40,7 @@ libp2p-yamux = { workspace = true } rand = "0.8" libp2p-swarm-test = { workspace = true } futures_ringbuf = "0.3.1" +serde = { version = "1.0", features = ["derive"]} # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling From ed5da99fd1de4b02a9523bfd781e091c4e1ca544 Mon Sep 17 00:00:00 2001 From: dgarus Date: Mon, 22 May 2023 13:23:19 +0300 Subject: [PATCH 23/39] No default features --- protocols/request-response/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index c39114c91db..38b3004aa0c 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -27,7 +27,6 @@ void = "1.0.2" log = "0.4.17" [features] -default = ["json", "cbor"] json = ["dep:serde", "dep:serde_json", "libp2p-swarm/macros"] cbor = ["dep:serde", "dep:serde_cbor", "libp2p-swarm/macros"] From 2d70235e12b64ff9e7885506c6fa8ff8b8b6f6bf Mon Sep 17 00:00:00 2001 From: dgarus Date: Mon, 22 May 2023 13:26:36 +0300 Subject: [PATCH 24/39] swarm-derive should be optional --- protocols/request-response/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index 38b3004aa0c..a84c07ad533 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -16,7 +16,7 @@ futures = "0.3.28" instant = "0.1.11" libp2p-core = { workspace = true } libp2p-swarm = { workspace = true } -libp2p-swarm-derive = { workspace = true } +libp2p-swarm-derive = { workspace = true, optional = true } libp2p-identity = { workspace = true } rand = "0.8" serde = { version = "1.0", optional = true} @@ -27,8 +27,8 @@ void = "1.0.2" log = "0.4.17" [features] -json = ["dep:serde", "dep:serde_json", "libp2p-swarm/macros"] -cbor = ["dep:serde", "dep:serde_cbor", "libp2p-swarm/macros"] +json = ["dep:serde", "dep:serde_json", "dep:libp2p-swarm-derive"] +cbor = ["dep:serde", "dep:serde_cbor", "dep:libp2p-swarm-derive"] [dev-dependencies] async-std = { version = "1.6.2", features = ["attributes"] } From 9c7d967f2eeaa6b3ee59b507c6c948070b74eecf Mon Sep 17 00:00:00 2001 From: dgarus Date: Mon, 22 May 2023 15:33:13 +0300 Subject: [PATCH 25/39] Codecs are `pub(crate)` --- protocols/request-response/src/cbor.rs | 2 +- protocols/request-response/src/json.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 683679ecf25..15831b9e99e 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -36,7 +36,7 @@ const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; #[derive(Debug, Clone)] -pub struct Codec { +pub(crate) struct Codec { phantom: PhantomData<(Req, Resp)>, } diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 0253daa79d0..59a6b8c4ec7 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -36,7 +36,7 @@ const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; #[derive(Debug, Clone)] -pub struct Codec { +pub(crate) struct Codec { phantom: PhantomData<(Req, Resp)>, } From 38395992554b7c9b0464f9fa54c9d3f486eb30f5 Mon Sep 17 00:00:00 2001 From: dgarus Date: Mon, 22 May 2023 17:16:28 +0300 Subject: [PATCH 26/39] revert --- protocols/request-response/src/cbor.rs | 2 +- protocols/request-response/src/json.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 15831b9e99e..683679ecf25 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -36,7 +36,7 @@ const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; #[derive(Debug, Clone)] -pub(crate) struct Codec { +pub struct Codec { phantom: PhantomData<(Req, Resp)>, } diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 59a6b8c4ec7..0253daa79d0 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -36,7 +36,7 @@ const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; #[derive(Debug, Clone)] -pub(crate) struct Codec { +pub struct Codec { phantom: PhantomData<(Req, Resp)>, } From 15b0cb53ecab9be3bcf483133068feddc61bf651 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 22 May 2023 18:27:35 +0200 Subject: [PATCH 27/39] Hide codec implementation from public API --- protocols/request-response/src/cbor.rs | 214 +++++++++++++------------ protocols/request-response/src/json.rs | 4 +- 2 files changed, 111 insertions(+), 107 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 683679ecf25..a89a1308ef2 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -19,26 +19,10 @@ // DEALINGS IN THE SOFTWARE. use crate::{Config, ProtocolSupport, RequestId, ResponseChannel}; -use async_trait::async_trait; -use futures::prelude::*; -use futures::{AsyncRead, AsyncWrite}; -use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; -use libp2p_swarm::StreamProtocol; use libp2p_swarm_derive::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; -use std::{io, marker::PhantomData}; - -/// Max request size in bytes -const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; -/// Max response size in bytes -const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; - -#[derive(Debug, Clone)] -pub struct Codec { - phantom: PhantomData<(Req, Resp)>, -} pub type OutEvent = crate::Event; @@ -52,80 +36,7 @@ where Req: Send + Clone + Serialize + DeserializeOwned + 'static, Resp: Send + Clone + Serialize + DeserializeOwned + 'static, { - inner: crate::Behaviour>, -} - -fn into_io_error(err: serde_cbor::Error) -> io::Error { - if err.is_syntax() || err.is_data() { - return io::Error::new(io::ErrorKind::InvalidData, err); - } - - if err.is_eof() { - return io::Error::new(io::ErrorKind::UnexpectedEof, err); - } - - io::Error::new(io::ErrorKind::Other, err) -} - -#[async_trait] -impl crate::Codec for Codec -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - type Protocol = StreamProtocol; - type Request = Req; - type Response = Resp; - - async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; - - serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) - } - - async fn read_response(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; - - serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) - } - - async fn write_request( - &mut self, - _: &Self::Protocol, - io: &mut T, - req: Self::Request, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - let data: Vec = serde_cbor::to_vec(&req).map_err(into_io_error)?; - write_length_prefixed(io, data.as_slice()).await?; - io.close().await?; - - Ok(()) - } - - async fn write_response( - &mut self, - _: &Self::Protocol, - io: &mut T, - resp: Self::Response, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - let data: Vec = serde_cbor::to_vec(&resp).map_err(into_io_error).unwrap(); - write_length_prefixed(io, data.as_slice()).await?; - io.close().await?; - - Ok(()) - } + inner: crate::Behaviour>, } impl Behaviour @@ -137,19 +48,13 @@ where where I: IntoIterator< Item = ( - as crate::Codec>::Protocol, + as crate::Codec>::Protocol, ProtocolSupport, ), >, { Behaviour { - inner: crate::Behaviour::new( - Codec { - phantom: PhantomData, - }, - protocols, - cfg, - ), + inner: crate::Behaviour::new(codec::Codec::default(), protocols, cfg), } } @@ -182,13 +87,114 @@ where } } +mod codec { + use async_trait::async_trait; + use futures::prelude::*; + use futures::{AsyncRead, AsyncWrite}; + use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; + use libp2p_swarm::StreamProtocol; + use serde::{de::DeserializeOwned, Serialize}; + use std::{io, marker::PhantomData}; + + /// Max request size in bytes + const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; + /// Max response size in bytes + const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; + + #[derive(Debug, Clone)] + pub struct Codec { + phantom: PhantomData<(Req, Resp)>, + } + + impl Default for Codec { + fn default() -> Self { + Codec { + phantom: PhantomData, + } + } + } + + #[async_trait] + impl crate::Codec for Codec + where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, + { + type Protocol = StreamProtocol; + type Request = Req; + type Response = Resp; + + async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; + + serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) + } + + async fn read_response(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; + + serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) + } + + async fn write_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let data: Vec = serde_cbor::to_vec(&req).map_err(into_io_error)?; + write_length_prefixed(io, data.as_slice()).await?; + io.close().await?; + + Ok(()) + } + + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + resp: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let data: Vec = serde_cbor::to_vec(&resp).map_err(into_io_error).unwrap(); + write_length_prefixed(io, data.as_slice()).await?; + io.close().await?; + + Ok(()) + } + } + + fn into_io_error(err: serde_cbor::Error) -> io::Error { + if err.is_syntax() || err.is_data() { + return io::Error::new(io::ErrorKind::InvalidData, err); + } + + if err.is_eof() { + return io::Error::new(io::ErrorKind::UnexpectedEof, err); + } + + io::Error::new(io::ErrorKind::Other, err) + } +} + #[cfg(test)] mod tests { - use crate::Codec; + use crate::cbor::codec::Codec; + use crate::Codec as _; use futures_ringbuf::Endpoint; use libp2p_swarm::StreamProtocol; use serde::{Deserialize, Serialize}; - use std::marker::PhantomData; #[async_std::test] async fn test_codec() { @@ -199,9 +205,7 @@ mod tests { payload: "test_payload".to_string(), }; let protocol = StreamProtocol::new("/test_cbor/1"); - let mut codec: super::Codec = super::Codec { - phantom: PhantomData, - }; + let mut codec = Codec::default(); let (mut a, mut b) = Endpoint::pair(124, 124); codec @@ -229,12 +233,12 @@ mod tests { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct TestRequest { + struct TestRequest { payload: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct TestResponse { + struct TestResponse { payload: String, } } diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 0253daa79d0..644e1437aab 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -217,12 +217,12 @@ mod tests { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct TestRequest { + struct TestRequest { payload: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct TestResponse { + struct TestResponse { payload: String, } } From c6a03272babafab60a7c55e5cb33e57b5d5ee27e Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 11:16:25 +0300 Subject: [PATCH 28/39] Hide json::Codec --- protocols/request-response/src/json.rs | 184 +++++++++++++------------ 1 file changed, 94 insertions(+), 90 deletions(-) diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 644e1437aab..76bc7dcfc37 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -19,26 +19,10 @@ // DEALINGS IN THE SOFTWARE. use crate::{Config, ProtocolSupport, RequestId, ResponseChannel}; -use async_trait::async_trait; -use futures::prelude::*; -use futures::{AsyncRead, AsyncWrite}; -use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; -use libp2p_swarm::StreamProtocol; use libp2p_swarm_derive::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; -use std::{io, marker::PhantomData}; - -/// Max request size in bytes -const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; -/// Max response size in bytes -const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; - -#[derive(Debug, Clone)] -pub struct Codec { - phantom: PhantomData<(Req, Resp)>, -} pub type OutEvent = crate::Event; @@ -52,68 +36,7 @@ where Req: Send + Clone + Serialize + DeserializeOwned + 'static, Resp: Send + Clone + Serialize + DeserializeOwned + 'static, { - inner: crate::Behaviour>, -} - -#[async_trait] -impl crate::Codec for Codec -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - type Protocol = StreamProtocol; - type Request = Req; - type Response = Resp; - - async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; - - Ok(serde_json::from_slice(vec.as_slice())?) - } - - async fn read_response(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; - - Ok(serde_json::from_slice(vec.as_slice())?) - } - - async fn write_request( - &mut self, - _: &Self::Protocol, - io: &mut T, - req: Self::Request, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - let data = serde_json::to_vec(&req)?; - write_length_prefixed(io, data.as_slice()).await?; - io.close().await?; - - Ok(()) - } - - async fn write_response( - &mut self, - _: &Self::Protocol, - io: &mut T, - resp: Self::Response, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - let data = serde_json::to_vec(&resp)?; - write_length_prefixed(io, data.as_slice()).await?; - io.close().await?; - - Ok(()) - } + inner: crate::Behaviour>, } impl Behaviour @@ -125,19 +48,13 @@ where where I: IntoIterator< Item = ( - as crate::Codec>::Protocol, + as crate::Codec>::Protocol, ProtocolSupport, ), >, { Behaviour { - inner: crate::Behaviour::new( - Codec { - phantom: PhantomData, - }, - protocols, - cfg, - ), + inner: crate::Behaviour::new(codec::Codec::default(), protocols, cfg), } } @@ -170,13 +87,101 @@ where } } +mod codec { + use async_trait::async_trait; + use futures::prelude::*; + use futures::{AsyncRead, AsyncWrite}; + use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; + use libp2p_swarm::StreamProtocol; + use serde::{de::DeserializeOwned, Serialize}; + use std::{io, marker::PhantomData}; + + /// Max request size in bytes + const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; + /// Max response size in bytes + const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; + + #[derive(Debug, Clone)] + pub struct Codec { + phantom: PhantomData<(Req, Resp)>, + } + + impl Default for Codec { + fn default() -> Self { + Codec { + phantom: PhantomData, + } + } + } + + #[async_trait] + impl crate::Codec for Codec + where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, + { + type Protocol = StreamProtocol; + type Request = Req; + type Response = Resp; + + async fn read_request(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; + + Ok(serde_json::from_slice(vec.as_slice())?) + } + + async fn read_response(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; + + Ok(serde_json::from_slice(vec.as_slice())?) + } + + async fn write_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let data = serde_json::to_vec(&req)?; + write_length_prefixed(io, data.as_slice()).await?; + io.close().await?; + + Ok(()) + } + + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + resp: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let data = serde_json::to_vec(&resp)?; + write_length_prefixed(io, data.as_slice()).await?; + io.close().await?; + + Ok(()) + } + } +} + #[cfg(test)] mod tests { use crate::Codec; use futures_ringbuf::Endpoint; use libp2p_swarm::StreamProtocol; use serde::{Deserialize, Serialize}; - use std::marker::PhantomData; #[async_std::test] async fn test_codec() { @@ -187,9 +192,8 @@ mod tests { payload: "test_payload".to_string(), }; let protocol = StreamProtocol::new("/test_cbor/1"); - let mut codec: super::Codec = super::Codec { - phantom: PhantomData, - }; + let mut codec: super::codec::Codec = + super::codec::Codec::default(); let (mut a, mut b) = Endpoint::pair(124, 124); codec From 9d6d608f74faf612f186ae3db748f49a8ad53e24 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 11:40:24 +0300 Subject: [PATCH 29/39] Remove `libp2p-swarm-derive` dep --- Cargo.lock | 1 - protocols/request-response/Cargo.toml | 5 ++--- protocols/request-response/src/cbor.rs | 2 +- protocols/request-response/src/json.rs | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43598d54cd6..27f94fdbc36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2986,7 +2986,6 @@ dependencies = [ "libp2p-identity", "libp2p-noise", "libp2p-swarm", - "libp2p-swarm-derive", "libp2p-swarm-test", "libp2p-tcp", "libp2p-yamux", diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index a84c07ad533..9e733c8a50b 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -16,7 +16,6 @@ futures = "0.3.28" instant = "0.1.11" libp2p-core = { workspace = true } libp2p-swarm = { workspace = true } -libp2p-swarm-derive = { workspace = true, optional = true } libp2p-identity = { workspace = true } rand = "0.8" serde = { version = "1.0", optional = true} @@ -27,8 +26,8 @@ void = "1.0.2" log = "0.4.17" [features] -json = ["dep:serde", "dep:serde_json", "dep:libp2p-swarm-derive"] -cbor = ["dep:serde", "dep:serde_cbor", "dep:libp2p-swarm-derive"] +json = ["dep:serde", "dep:serde_json", "libp2p-swarm/macros"] +cbor = ["dep:serde", "dep:serde_cbor", "libp2p-swarm/macros"] [dev-dependencies] async-std = { version = "1.6.2", features = ["attributes"] } diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index a89a1308ef2..3f4b75a8a32 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -21,7 +21,7 @@ use crate::{Config, ProtocolSupport, RequestId, ResponseChannel}; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; -use libp2p_swarm_derive::NetworkBehaviour; +use libp2p_swarm::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; pub type OutEvent = crate::Event; diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 76bc7dcfc37..8c5e21d2dc0 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -21,7 +21,7 @@ use crate::{Config, ProtocolSupport, RequestId, ResponseChannel}; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; -use libp2p_swarm_derive::NetworkBehaviour; +use libp2p_swarm::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; pub type OutEvent = crate::Event; From 5cdb636c157aa289358a05eaa5a62999887fbc88 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 13:25:37 +0300 Subject: [PATCH 30/39] Read/write without prefixed size --- core/src/upgrade.rs | 2 +- core/src/upgrade/transfer.rs | 33 ++++++++++++++++++++++++++ protocols/request-response/src/cbor.rs | 12 ++++++---- protocols/request-response/src/json.rs | 13 ++++++---- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/core/src/upgrade.rs b/core/src/upgrade.rs index 8219ac03a73..e8693e4182d 100644 --- a/core/src/upgrade.rs +++ b/core/src/upgrade.rs @@ -77,7 +77,7 @@ pub use self::{ pending::PendingUpgrade, ready::ReadyUpgrade, select::SelectUpgrade, - transfer::{read_length_prefixed, read_varint, write_length_prefixed, write_varint}, + transfer::{read_to_end, read_length_prefixed, read_varint, write_length_prefixed, write_varint}, }; pub use crate::Negotiated; pub use multistream_select::{NegotiatedComplete, NegotiationError, ProtocolError, Version}; diff --git a/core/src/upgrade/transfer.rs b/core/src/upgrade/transfer.rs index 93aeb987c8a..88e6fbc143e 100644 --- a/core/src/upgrade/transfer.rs +++ b/core/src/upgrade/transfer.rs @@ -20,6 +20,7 @@ //! Contains some helper futures for creating upgrades. +use std::cmp::min; use futures::prelude::*; use std::io; @@ -123,6 +124,38 @@ pub async fn read_length_prefixed( Ok(buf) } +const DEFAULT_READ_STEP_SIZE: u64 = 1024; + +/// Reads a message from the given socket. +/// +/// The `max_size` parameter is the maximum size in bytes of the message that we accept. This is +/// necessary in order to avoid DoS attacks where the remote sends us a message of several +/// gigabytes. +pub async fn read_to_end( + socket: &mut (impl AsyncRead + Unpin), + max_size: usize, +) -> io::Result> { + let step_size = min(DEFAULT_READ_STEP_SIZE, u64::try_from(max_size).unwrap()); + let mut res = Vec::with_capacity(usize::try_from(step_size).unwrap()); + + loop { + let len = res.len(); + if len > max_size { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("Received data size ({len} bytes) exceeds maximum ({max_size} bytes)"), + )); + } + + let num_bytes = socket.take(step_size).read_to_end(&mut res).await?; + if num_bytes == 0 || num_bytes == 1024 { + break; + } + } + + Ok(res) +} + #[cfg(test)] mod tests { use super::*; diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 3f4b75a8a32..6899634430b 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -91,7 +91,7 @@ mod codec { use async_trait::async_trait; use futures::prelude::*; use futures::{AsyncRead, AsyncWrite}; - use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; + use libp2p_core::upgrade::read_to_end; use libp2p_swarm::StreamProtocol; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; @@ -128,7 +128,7 @@ mod codec { where T: AsyncRead + Unpin + Send, { - let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; + let vec = read_to_end(io, REQUEST_SIZE_MAXIMUM).await?; serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) } @@ -137,7 +137,7 @@ mod codec { where T: AsyncRead + Unpin + Send, { - let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; + let vec = read_to_end(io, RESPONSE_SIZE_MAXIMUM).await?; serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) } @@ -152,7 +152,8 @@ mod codec { T: AsyncWrite + Unpin + Send, { let data: Vec = serde_cbor::to_vec(&req).map_err(into_io_error)?; - write_length_prefixed(io, data.as_slice()).await?; + io.write_all(data.as_ref()).await?; + io.flush().await?; io.close().await?; Ok(()) @@ -168,7 +169,8 @@ mod codec { T: AsyncWrite + Unpin + Send, { let data: Vec = serde_cbor::to_vec(&resp).map_err(into_io_error).unwrap(); - write_length_prefixed(io, data.as_slice()).await?; + io.write_all(data.as_ref()).await?; + io.flush().await?; io.close().await?; Ok(()) diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 8c5e21d2dc0..96f00eec474 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -91,7 +91,7 @@ mod codec { use async_trait::async_trait; use futures::prelude::*; use futures::{AsyncRead, AsyncWrite}; - use libp2p_core::upgrade::{read_length_prefixed, write_length_prefixed}; + use libp2p_core::upgrade::read_to_end; use libp2p_swarm::StreamProtocol; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; @@ -128,7 +128,7 @@ mod codec { where T: AsyncRead + Unpin + Send, { - let vec = read_length_prefixed(io, REQUEST_SIZE_MAXIMUM).await?; + let vec = read_to_end(io, REQUEST_SIZE_MAXIMUM).await?; Ok(serde_json::from_slice(vec.as_slice())?) } @@ -137,7 +137,7 @@ mod codec { where T: AsyncRead + Unpin + Send, { - let vec = read_length_prefixed(io, RESPONSE_SIZE_MAXIMUM).await?; + let vec = read_to_end(io, RESPONSE_SIZE_MAXIMUM).await?; Ok(serde_json::from_slice(vec.as_slice())?) } @@ -152,7 +152,9 @@ mod codec { T: AsyncWrite + Unpin + Send, { let data = serde_json::to_vec(&req)?; - write_length_prefixed(io, data.as_slice()).await?; + + io.write_all(data.as_ref()).await?; + io.flush().await?; io.close().await?; Ok(()) @@ -168,7 +170,8 @@ mod codec { T: AsyncWrite + Unpin + Send, { let data = serde_json::to_vec(&resp)?; - write_length_prefixed(io, data.as_slice()).await?; + io.write_all(data.as_ref()).await?; + io.flush().await?; io.close().await?; Ok(()) From c17757057de7c5809d5c941401d7e0f2eb2b7c54 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 13:28:12 +0300 Subject: [PATCH 31/39] fix format --- core/src/upgrade/transfer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/upgrade/transfer.rs b/core/src/upgrade/transfer.rs index 88e6fbc143e..d516340132d 100644 --- a/core/src/upgrade/transfer.rs +++ b/core/src/upgrade/transfer.rs @@ -20,8 +20,8 @@ //! Contains some helper futures for creating upgrades. -use std::cmp::min; use futures::prelude::*; +use std::cmp::min; use std::io; // TODO: these methods could be on an Ext trait to AsyncWrite From 60f029c9b189a7ae59fb70a022400c058b12d411 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 13:29:02 +0300 Subject: [PATCH 32/39] fix format --- core/src/upgrade.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/upgrade.rs b/core/src/upgrade.rs index e8693e4182d..80df980414d 100644 --- a/core/src/upgrade.rs +++ b/core/src/upgrade.rs @@ -77,7 +77,9 @@ pub use self::{ pending::PendingUpgrade, ready::ReadyUpgrade, select::SelectUpgrade, - transfer::{read_to_end, read_length_prefixed, read_varint, write_length_prefixed, write_varint}, + transfer::{ + read_length_prefixed, read_to_end, read_varint, write_length_prefixed, write_varint, + }, }; pub use crate::Negotiated; pub use multistream_select::{NegotiatedComplete, NegotiationError, ProtocolError, Version}; From 0fb3e7837c973cc65f9672825b9790f2ab294845 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 14:13:11 +0300 Subject: [PATCH 33/39] fix bug --- core/src/upgrade/transfer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/upgrade/transfer.rs b/core/src/upgrade/transfer.rs index d516340132d..38775453e6d 100644 --- a/core/src/upgrade/transfer.rs +++ b/core/src/upgrade/transfer.rs @@ -148,7 +148,7 @@ pub async fn read_to_end( } let num_bytes = socket.take(step_size).read_to_end(&mut res).await?; - if num_bytes == 0 || num_bytes == 1024 { + if num_bytes == 0 { break; } } From 12a1322d4742312e255f3b2ffcfe5941153d5797 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 17:05:57 +0300 Subject: [PATCH 34/39] Deref for behaviors --- protocols/request-response/src/cbor.rs | 39 ++++++++------------------ protocols/request-response/src/json.rs | 39 ++++++++------------------ 2 files changed, 22 insertions(+), 56 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 6899634430b..1d6d9cdf94b 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -18,9 +18,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::{Config, ProtocolSupport, RequestId, ResponseChannel}; -use libp2p_core::Multiaddr; -use libp2p_identity::PeerId; +use std::ops::Deref; +use crate::{Config, ProtocolSupport}; use libp2p_swarm::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; @@ -57,33 +56,17 @@ where inner: crate::Behaviour::new(codec::Codec::default(), protocols, cfg), } } +} - pub fn send_request(&mut self, peer: &PeerId, request: Req) -> RequestId { - self.inner.send_request(peer, request) - } - - pub fn send_response(&mut self, ch: ResponseChannel, rs: Resp) -> Result<(), Resp> { - self.inner.send_response(ch, rs) - } - - pub fn add_address(&mut self, peer: &PeerId, address: Multiaddr) { - self.inner.add_address(peer, address) - } - - pub fn remove_address(&mut self, peer: &PeerId, address: &Multiaddr) { - self.inner.remove_address(peer, address) - } - - pub fn is_connected(&self, peer: &PeerId) -> bool { - self.inner.is_connected(peer) - } - - pub fn is_pending_outbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { - self.inner.is_pending_outbound(peer, request_id) - } +impl Deref for Behaviour + where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + type Target = crate::Behaviour>; - pub fn is_pending_inbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { - self.inner.is_pending_inbound(peer, request_id) + fn deref(&self) -> &Self::Target { + &self.inner } } diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 96f00eec474..e888f873580 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -18,9 +18,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::{Config, ProtocolSupport, RequestId, ResponseChannel}; -use libp2p_core::Multiaddr; -use libp2p_identity::PeerId; +use std::ops::Deref; +use crate::{Config, ProtocolSupport}; use libp2p_swarm::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; @@ -57,33 +56,17 @@ where inner: crate::Behaviour::new(codec::Codec::default(), protocols, cfg), } } +} - pub fn send_request(&mut self, peer: &PeerId, request: Req) -> RequestId { - self.inner.send_request(peer, request) - } - - pub fn send_response(&mut self, ch: ResponseChannel, rs: Resp) -> Result<(), Resp> { - self.inner.send_response(ch, rs) - } - - pub fn add_address(&mut self, peer: &PeerId, address: Multiaddr) { - self.inner.add_address(peer, address) - } - - pub fn remove_address(&mut self, peer: &PeerId, address: &Multiaddr) { - self.inner.remove_address(peer, address) - } - - pub fn is_connected(&self, peer: &PeerId) -> bool { - self.inner.is_connected(peer) - } - - pub fn is_pending_outbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { - self.inner.is_pending_outbound(peer, request_id) - } +impl Deref for Behaviour + where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, +{ + type Target = crate::Behaviour>; - pub fn is_pending_inbound(&self, peer: &PeerId, request_id: &RequestId) -> bool { - self.inner.is_pending_inbound(peer, request_id) + fn deref(&self) -> &Self::Target { + &self.inner } } From 4122c4e7d68ed977fae71e9134ab05a69dee8fa7 Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 17:10:10 +0300 Subject: [PATCH 35/39] fix format --- protocols/request-response/src/cbor.rs | 8 ++++---- protocols/request-response/src/json.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 1d6d9cdf94b..01bf1f9a5f8 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -18,10 +18,10 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use std::ops::Deref; use crate::{Config, ProtocolSupport}; use libp2p_swarm::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; +use std::ops::Deref; pub type OutEvent = crate::Event; @@ -59,9 +59,9 @@ where } impl Deref for Behaviour - where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, { type Target = crate::Behaviour>; diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index e888f873580..d8f93a8fd6a 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -18,10 +18,10 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use std::ops::Deref; use crate::{Config, ProtocolSupport}; use libp2p_swarm::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; +use std::ops::Deref; pub type OutEvent = crate::Event; @@ -59,9 +59,9 @@ where } impl Deref for Behaviour - where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, +where + Req: Send + Clone + Serialize + DeserializeOwned, + Resp: Send + Clone + Serialize + DeserializeOwned, { type Target = crate::Behaviour>; From 74669a3b8a36a397d81765880cdc488fcefba96c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 17:56:48 +0200 Subject: [PATCH 36/39] Rename constructors to better support type aliases --- examples/file-sharing/src/network.rs | 2 +- protocols/autonat/src/behaviour.rs | 2 +- protocols/request-response/CHANGELOG.md | 1 + protocols/request-response/src/cbor.rs | 80 +++++++++--------------- protocols/request-response/src/json.rs | 80 +++++++++--------------- protocols/request-response/src/lib.rs | 25 +++++++- protocols/request-response/tests/ping.rs | 26 ++++---- 7 files changed, 103 insertions(+), 113 deletions(-) diff --git a/examples/file-sharing/src/network.rs b/examples/file-sharing/src/network.rs index 49479bf81ed..f497d0ce299 100644 --- a/examples/file-sharing/src/network.rs +++ b/examples/file-sharing/src/network.rs @@ -60,7 +60,7 @@ pub(crate) async fn new( transport, ComposedBehaviour { kademlia: Kademlia::new(peer_id, MemoryStore::new(peer_id)), - request_response: request_response::Behaviour::new( + request_response: request_response::Behaviour::with_codec( FileExchangeCodec(), iter::once(( StreamProtocol::new("/file-exchange/1"), diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs index 17681341489..3ff7f1cd3ec 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -222,7 +222,7 @@ impl Behaviour { let protocols = iter::once((DEFAULT_PROTOCOL_NAME, ProtocolSupport::Full)); let mut cfg = request_response::Config::default(); cfg.set_request_timeout(config.timeout); - let inner = request_response::Behaviour::new(AutoNatCodec, protocols, cfg); + let inner = request_response::Behaviour::with_codec(AutoNatCodec, protocols, cfg); Self { local_peer_id, inner, diff --git a/protocols/request-response/CHANGELOG.md b/protocols/request-response/CHANGELOG.md index a4973e602ce..222cf59c347 100644 --- a/protocols/request-response/CHANGELOG.md +++ b/protocols/request-response/CHANGELOG.md @@ -1,6 +1,7 @@ ## 0.25.0 - unreleased - Add `request_response::json::Behaviour` and `request_response::cbor::Behaviour` building on top of the `serde` traits. + To conveniently construct these, we remove the `Codec` parameter from `Behaviour::new` and add `Behaviour::with_codec`. See [PR 3952]. - Raise MSRV to 1.65. diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 01bf1f9a5f8..a3653f765ae 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -23,52 +23,29 @@ use libp2p_swarm::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; use std::ops::Deref; -pub type OutEvent = crate::Event; - -#[derive(NetworkBehaviour)] -#[behaviour( - to_swarm = "OutEvent", - prelude = "libp2p_swarm::derive_prelude" -)] -pub struct Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned + 'static, - Resp: Send + Clone + Serialize + DeserializeOwned + 'static, -{ - inner: crate::Behaviour>, -} - -impl Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - pub fn new(protocols: I, cfg: Config) -> Self - where - I: IntoIterator< - Item = ( - as crate::Codec>::Protocol, - ProtocolSupport, - ), - >, - { - Behaviour { - inner: crate::Behaviour::new(codec::Codec::default(), protocols, cfg), - } - } -} - -impl Deref for Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - type Target = crate::Behaviour>; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} +/// A request-response behaviour using [`serde_cbor`] for serializing and deserializing the messages. +/// +/// # Example +/// +/// ``` +/// # use libp2p_request_response::{cbor, ProtocolSupport, self as request_response}; +/// # use libp2p_swarm::{StreamProtocol, SwarmBuilder}; +/// #[derive(Debug, serde::Serialize, serde::Deserialize)] +/// struct GreetRequest { +/// name: String, +/// } +/// +/// #[derive(Debug, serde::Serialize, serde::Deserialize)] +/// struct GreetResponse { +/// message: String, +/// } +/// +/// let behaviour = cbor::Behaviour::::new( +/// [(StreamProtocol::new("/my-cbor-protocol"), ProtocolSupport::Full)], +/// request_response::Config::default() +/// ); +/// ``` +pub type Behaviour = crate::Behaviour>; mod codec { use async_trait::async_trait; @@ -84,7 +61,6 @@ mod codec { /// Max response size in bytes const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; - #[derive(Debug, Clone)] pub struct Codec { phantom: PhantomData<(Req, Resp)>, } @@ -97,11 +73,17 @@ mod codec { } } + impl Clone for Codec { + fn clone(&self) -> Self { + Self::default() + } + } + #[async_trait] impl crate::Codec for Codec where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, + Req: Send + Serialize + DeserializeOwned, + Resp: Send + Serialize + DeserializeOwned, { type Protocol = StreamProtocol; type Request = Req; diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index d8f93a8fd6a..f32255f4314 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -23,52 +23,29 @@ use libp2p_swarm::NetworkBehaviour; use serde::{de::DeserializeOwned, Serialize}; use std::ops::Deref; -pub type OutEvent = crate::Event; - -#[derive(NetworkBehaviour)] -#[behaviour( - to_swarm = "OutEvent", - prelude = "libp2p_swarm::derive_prelude" -)] -pub struct Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned + 'static, - Resp: Send + Clone + Serialize + DeserializeOwned + 'static, -{ - inner: crate::Behaviour>, -} - -impl Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - pub fn new(protocols: I, cfg: Config) -> Self - where - I: IntoIterator< - Item = ( - as crate::Codec>::Protocol, - ProtocolSupport, - ), - >, - { - Behaviour { - inner: crate::Behaviour::new(codec::Codec::default(), protocols, cfg), - } - } -} - -impl Deref for Behaviour -where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, -{ - type Target = crate::Behaviour>; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} +/// A request-response behaviour using [`serde_json`] for serializing and deserializing the messages. +/// +/// # Example +/// +/// ``` +/// # use libp2p_request_response::{json, ProtocolSupport, self as request_response}; +/// # use libp2p_swarm::{StreamProtocol}; +/// #[derive(Debug, serde::Serialize, serde::Deserialize)] +/// struct GreetRequest { +/// name: String, +/// } +/// +/// #[derive(Debug, serde::Serialize, serde::Deserialize)] +/// struct GreetResponse { +/// message: String, +/// } +/// +/// let behaviour = json::Behaviour::::new( +/// [(StreamProtocol::new("/my-json-protocol"), ProtocolSupport::Full)], +/// request_response::Config::default() +/// ); +/// ``` +pub type Behaviour = crate::Behaviour>; mod codec { use async_trait::async_trait; @@ -84,7 +61,6 @@ mod codec { /// Max response size in bytes const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; - #[derive(Debug, Clone)] pub struct Codec { phantom: PhantomData<(Req, Resp)>, } @@ -97,11 +73,17 @@ mod codec { } } + impl Clone for Codec { + fn clone(&self) -> Self { + Self::default() + } + } + #[async_trait] impl crate::Codec for Codec where - Req: Send + Clone + Serialize + DeserializeOwned, - Resp: Send + Clone + Serialize + DeserializeOwned, + Req: Send + Serialize + DeserializeOwned, + Resp: Send + Serialize + DeserializeOwned, { type Protocol = StreamProtocol; type Request = Req; diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index 6d395a6495e..ef672ad6e7d 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -28,7 +28,7 @@ //! over the actual messages being sent, which are defined in terms of a //! [`Codec`]. Creating a request/response protocol thus amounts //! to providing an implementation of this trait which can then be -//! given to [`Behaviour::new`]. Further configuration options are +//! given to [`Behaviour::with_codec`]. Further configuration options are //! available via the [`Config`]. //! //! Requests are sent using [`Behaviour::send_request`] and the @@ -39,6 +39,14 @@ //! receiving a [`Message::Request`] via //! [`Event::Message`]. //! +//! ## Predefined codecs +//! +//! In case your message types implement [`serde::Serialize`] and [`serde::Deserialize`], +//! you can use two predefined behaviours: +//! +//! - [`cbor::Behaviour`] for CBOR-encoded messages +//! - [`json::Behaviour`] for JSON-encoded messages +//! //! ## Protocol Families //! //! A single [`Behaviour`] instance can be used with an entire @@ -332,13 +340,26 @@ where pending_outbound_requests: HashMap; 10]>>, } +impl Behaviour +where + TCodec: Codec + Default + Clone + Send + 'static, +{ + /// Creates a new `Behaviour` for the given protocols and configuration, using [`Default`] to construct the codec. + pub fn new(protocols: I, cfg: Config) -> Self + where + I: IntoIterator, + { + Self::with_codec(TCodec::default(), protocols, cfg) + } +} + impl Behaviour where TCodec: Codec + Clone + Send + 'static, { /// Creates a new `Behaviour` for the given /// protocols, codec and configuration. - pub fn new(codec: TCodec, protocols: I, cfg: Config) -> Self + pub fn with_codec(codec: TCodec, protocols: I, cfg: Config) -> Self where I: IntoIterator, { diff --git a/protocols/request-response/tests/ping.rs b/protocols/request-response/tests/ping.rs index 48860b5887f..42baf0cf773 100644 --- a/protocols/request-response/tests/ping.rs +++ b/protocols/request-response/tests/ping.rs @@ -43,8 +43,9 @@ async fn is_response_outbound() { )); let cfg = request_response::Config::default(); - let mut swarm1 = - Swarm::new_ephemeral(|_| request_response::Behaviour::new(PingCodec(), protocols, cfg)); + let mut swarm1 = Swarm::new_ephemeral(|_| { + request_response::Behaviour::with_codec(PingCodec(), protocols, cfg) + }); let request_id1 = swarm1 .behaviour_mut() @@ -87,11 +88,12 @@ async fn ping_protocol() { let cfg = request_response::Config::default(); let mut swarm1 = Swarm::new_ephemeral(|_| { - request_response::Behaviour::new(PingCodec(), protocols.clone(), cfg.clone()) + request_response::Behaviour::with_codec(PingCodec(), protocols.clone(), cfg.clone()) }); let peer1_id = *swarm1.local_peer_id(); - let mut swarm2 = - Swarm::new_ephemeral(|_| request_response::Behaviour::new(PingCodec(), protocols, cfg)); + let mut swarm2 = Swarm::new_ephemeral(|_| { + request_response::Behaviour::with_codec(PingCodec(), protocols, cfg) + }); let peer2_id = *swarm2.local_peer_id(); swarm1.listen().await; @@ -178,11 +180,12 @@ async fn emits_inbound_connection_closed_failure() { let cfg = request_response::Config::default(); let mut swarm1 = Swarm::new_ephemeral(|_| { - request_response::Behaviour::new(PingCodec(), protocols.clone(), cfg.clone()) + request_response::Behaviour::with_codec(PingCodec(), protocols.clone(), cfg.clone()) }); let peer1_id = *swarm1.local_peer_id(); - let mut swarm2 = - Swarm::new_ephemeral(|_| request_response::Behaviour::new(PingCodec(), protocols, cfg)); + let mut swarm2 = Swarm::new_ephemeral(|_| { + request_response::Behaviour::with_codec(PingCodec(), protocols, cfg) + }); let peer2_id = *swarm2.local_peer_id(); swarm1.listen().await; @@ -241,11 +244,12 @@ async fn emits_inbound_connection_closed_if_channel_is_dropped() { let cfg = request_response::Config::default(); let mut swarm1 = Swarm::new_ephemeral(|_| { - request_response::Behaviour::new(PingCodec(), protocols.clone(), cfg.clone()) + request_response::Behaviour::with_codec(PingCodec(), protocols.clone(), cfg.clone()) }); let peer1_id = *swarm1.local_peer_id(); - let mut swarm2 = - Swarm::new_ephemeral(|_| request_response::Behaviour::new(PingCodec(), protocols, cfg)); + let mut swarm2 = Swarm::new_ephemeral(|_| { + request_response::Behaviour::with_codec(PingCodec(), protocols, cfg) + }); let peer2_id = *swarm2.local_peer_id(); swarm1.listen().await; From 2da4b43f72179bf50bf8d8808f2bb40d5baaed8b Mon Sep 17 00:00:00 2001 From: dgarus Date: Tue, 23 May 2023 19:41:42 +0300 Subject: [PATCH 37/39] move fn read_to_end to request_response crate --- core/src/upgrade.rs | 4 +-- core/src/upgrade/transfer.rs | 33 ------------------------- protocols/request-response/src/cbor.rs | 7 +----- protocols/request-response/src/codec.rs | 33 +++++++++++++++++++++++++ protocols/request-response/src/json.rs | 7 +----- 5 files changed, 36 insertions(+), 48 deletions(-) diff --git a/core/src/upgrade.rs b/core/src/upgrade.rs index 80df980414d..8219ac03a73 100644 --- a/core/src/upgrade.rs +++ b/core/src/upgrade.rs @@ -77,9 +77,7 @@ pub use self::{ pending::PendingUpgrade, ready::ReadyUpgrade, select::SelectUpgrade, - transfer::{ - read_length_prefixed, read_to_end, read_varint, write_length_prefixed, write_varint, - }, + transfer::{read_length_prefixed, read_varint, write_length_prefixed, write_varint}, }; pub use crate::Negotiated; pub use multistream_select::{NegotiatedComplete, NegotiationError, ProtocolError, Version}; diff --git a/core/src/upgrade/transfer.rs b/core/src/upgrade/transfer.rs index 38775453e6d..93aeb987c8a 100644 --- a/core/src/upgrade/transfer.rs +++ b/core/src/upgrade/transfer.rs @@ -21,7 +21,6 @@ //! Contains some helper futures for creating upgrades. use futures::prelude::*; -use std::cmp::min; use std::io; // TODO: these methods could be on an Ext trait to AsyncWrite @@ -124,38 +123,6 @@ pub async fn read_length_prefixed( Ok(buf) } -const DEFAULT_READ_STEP_SIZE: u64 = 1024; - -/// Reads a message from the given socket. -/// -/// The `max_size` parameter is the maximum size in bytes of the message that we accept. This is -/// necessary in order to avoid DoS attacks where the remote sends us a message of several -/// gigabytes. -pub async fn read_to_end( - socket: &mut (impl AsyncRead + Unpin), - max_size: usize, -) -> io::Result> { - let step_size = min(DEFAULT_READ_STEP_SIZE, u64::try_from(max_size).unwrap()); - let mut res = Vec::with_capacity(usize::try_from(step_size).unwrap()); - - loop { - let len = res.len(); - if len > max_size { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - format!("Received data size ({len} bytes) exceeds maximum ({max_size} bytes)"), - )); - } - - let num_bytes = socket.take(step_size).read_to_end(&mut res).await?; - if num_bytes == 0 { - break; - } - } - - Ok(res) -} - #[cfg(test)] mod tests { use super::*; diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index a3653f765ae..9797287d384 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -18,11 +18,6 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::{Config, ProtocolSupport}; -use libp2p_swarm::NetworkBehaviour; -use serde::{de::DeserializeOwned, Serialize}; -use std::ops::Deref; - /// A request-response behaviour using [`serde_cbor`] for serializing and deserializing the messages. /// /// # Example @@ -48,10 +43,10 @@ use std::ops::Deref; pub type Behaviour = crate::Behaviour>; mod codec { + use crate::codec::read_to_end; use async_trait::async_trait; use futures::prelude::*; use futures::{AsyncRead, AsyncWrite}; - use libp2p_core::upgrade::read_to_end; use libp2p_swarm::StreamProtocol; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; diff --git a/protocols/request-response/src/codec.rs b/protocols/request-response/src/codec.rs index d26b729acae..ac98830fa41 100644 --- a/protocols/request-response/src/codec.rs +++ b/protocols/request-response/src/codec.rs @@ -20,6 +20,7 @@ use async_trait::async_trait; use futures::prelude::*; +use std::cmp::min; use std::io; /// A `Codec` defines the request and response types @@ -76,3 +77,35 @@ pub trait Codec { where T: AsyncWrite + Unpin + Send; } + +const DEFAULT_READ_STEP_SIZE: u64 = 1024; + +/// Reads a message from the given socket. +/// +/// The `max_size` parameter is the maximum size in bytes of the message that we accept. This is +/// necessary in order to avoid DoS attacks where the remote sends us a message of several +/// gigabytes. +pub(crate) async fn read_to_end( + socket: &mut (impl AsyncRead + Unpin), + max_size: usize, +) -> io::Result> { + let step_size = min(DEFAULT_READ_STEP_SIZE, u64::try_from(max_size).unwrap()); + let mut res = Vec::with_capacity(usize::try_from(step_size).unwrap()); + + loop { + let len = res.len(); + if len > max_size { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("Received data size ({len} bytes) exceeds maximum ({max_size} bytes)"), + )); + } + + let num_bytes = socket.take(step_size).read_to_end(&mut res).await?; + if num_bytes == 0 { + break; + } + } + + Ok(res) +} diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index f32255f4314..12a4b400aca 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -18,11 +18,6 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::{Config, ProtocolSupport}; -use libp2p_swarm::NetworkBehaviour; -use serde::{de::DeserializeOwned, Serialize}; -use std::ops::Deref; - /// A request-response behaviour using [`serde_json`] for serializing and deserializing the messages. /// /// # Example @@ -48,10 +43,10 @@ use std::ops::Deref; pub type Behaviour = crate::Behaviour>; mod codec { + use crate::codec::read_to_end; use async_trait::async_trait; use futures::prelude::*; use futures::{AsyncRead, AsyncWrite}; - use libp2p_core::upgrade::read_to_end; use libp2p_swarm::StreamProtocol; use serde::{de::DeserializeOwned, Serialize}; use std::{io, marker::PhantomData}; From 207f15ae7dee16b19b59ba17f575f8c7ecdc1485 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 24 May 2023 10:51:51 +0300 Subject: [PATCH 38/39] fix review comments --- protocols/request-response/src/cbor.rs | 15 +++++------ protocols/request-response/src/codec.rs | 33 ------------------------- protocols/request-response/src/json.rs | 17 +++++++------ 3 files changed, 17 insertions(+), 48 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 9797287d384..323c750d6c7 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -43,7 +43,6 @@ pub type Behaviour = crate::Behaviour>; mod codec { - use crate::codec::read_to_end; use async_trait::async_trait; use futures::prelude::*; use futures::{AsyncRead, AsyncWrite}; @@ -52,9 +51,9 @@ mod codec { use std::{io, marker::PhantomData}; /// Max request size in bytes - const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; + const REQUEST_SIZE_MAXIMUM: u64 = 1024 * 1024; /// Max response size in bytes - const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; + const RESPONSE_SIZE_MAXIMUM: u64 = 10 * 1024 * 1024; pub struct Codec { phantom: PhantomData<(Req, Resp)>, @@ -88,7 +87,9 @@ mod codec { where T: AsyncRead + Unpin + Send, { - let vec = read_to_end(io, REQUEST_SIZE_MAXIMUM).await?; + let mut vec = Vec::new(); + + io.take(REQUEST_SIZE_MAXIMUM).read_to_end(&mut vec).await?; serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) } @@ -97,7 +98,9 @@ mod codec { where T: AsyncRead + Unpin + Send, { - let vec = read_to_end(io, RESPONSE_SIZE_MAXIMUM).await?; + let mut vec = Vec::new(); + + io.take(RESPONSE_SIZE_MAXIMUM).read_to_end(&mut vec).await?; serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) } @@ -113,7 +116,6 @@ mod codec { { let data: Vec = serde_cbor::to_vec(&req).map_err(into_io_error)?; io.write_all(data.as_ref()).await?; - io.flush().await?; io.close().await?; Ok(()) @@ -130,7 +132,6 @@ mod codec { { let data: Vec = serde_cbor::to_vec(&resp).map_err(into_io_error).unwrap(); io.write_all(data.as_ref()).await?; - io.flush().await?; io.close().await?; Ok(()) diff --git a/protocols/request-response/src/codec.rs b/protocols/request-response/src/codec.rs index ac98830fa41..d26b729acae 100644 --- a/protocols/request-response/src/codec.rs +++ b/protocols/request-response/src/codec.rs @@ -20,7 +20,6 @@ use async_trait::async_trait; use futures::prelude::*; -use std::cmp::min; use std::io; /// A `Codec` defines the request and response types @@ -77,35 +76,3 @@ pub trait Codec { where T: AsyncWrite + Unpin + Send; } - -const DEFAULT_READ_STEP_SIZE: u64 = 1024; - -/// Reads a message from the given socket. -/// -/// The `max_size` parameter is the maximum size in bytes of the message that we accept. This is -/// necessary in order to avoid DoS attacks where the remote sends us a message of several -/// gigabytes. -pub(crate) async fn read_to_end( - socket: &mut (impl AsyncRead + Unpin), - max_size: usize, -) -> io::Result> { - let step_size = min(DEFAULT_READ_STEP_SIZE, u64::try_from(max_size).unwrap()); - let mut res = Vec::with_capacity(usize::try_from(step_size).unwrap()); - - loop { - let len = res.len(); - if len > max_size { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - format!("Received data size ({len} bytes) exceeds maximum ({max_size} bytes)"), - )); - } - - let num_bytes = socket.take(step_size).read_to_end(&mut res).await?; - if num_bytes == 0 { - break; - } - } - - Ok(res) -} diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index 12a4b400aca..dbbb1a3df32 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -43,7 +43,6 @@ pub type Behaviour = crate::Behaviour>; mod codec { - use crate::codec::read_to_end; use async_trait::async_trait; use futures::prelude::*; use futures::{AsyncRead, AsyncWrite}; @@ -52,9 +51,9 @@ mod codec { use std::{io, marker::PhantomData}; /// Max request size in bytes - const REQUEST_SIZE_MAXIMUM: usize = 1024 * 1024; + const REQUEST_SIZE_MAXIMUM: u64 = 1024 * 1024; /// Max response size in bytes - const RESPONSE_SIZE_MAXIMUM: usize = 10 * 1024 * 1024; + const RESPONSE_SIZE_MAXIMUM: u64 = 10 * 1024 * 1024; pub struct Codec { phantom: PhantomData<(Req, Resp)>, @@ -88,7 +87,9 @@ mod codec { where T: AsyncRead + Unpin + Send, { - let vec = read_to_end(io, REQUEST_SIZE_MAXIMUM).await?; + let mut vec = Vec::new(); + + io.take(REQUEST_SIZE_MAXIMUM).read_to_end(&mut vec).await?; Ok(serde_json::from_slice(vec.as_slice())?) } @@ -97,7 +98,9 @@ mod codec { where T: AsyncRead + Unpin + Send, { - let vec = read_to_end(io, RESPONSE_SIZE_MAXIMUM).await?; + let mut vec = Vec::new(); + + io.take(RESPONSE_SIZE_MAXIMUM).read_to_end(&mut vec).await?; Ok(serde_json::from_slice(vec.as_slice())?) } @@ -114,7 +117,6 @@ mod codec { let data = serde_json::to_vec(&req)?; io.write_all(data.as_ref()).await?; - io.flush().await?; io.close().await?; Ok(()) @@ -131,7 +133,6 @@ mod codec { { let data = serde_json::to_vec(&resp)?; io.write_all(data.as_ref()).await?; - io.flush().await?; io.close().await?; Ok(()) @@ -154,7 +155,7 @@ mod tests { let expected_response = TestResponse { payload: "test_payload".to_string(), }; - let protocol = StreamProtocol::new("/test_cbor/1"); + let protocol = StreamProtocol::new("/test_json/1"); let mut codec: super::codec::Codec = super::codec::Codec::default(); From 81972786a144c4eb1f5db1fa37cf1e8117ce1640 Mon Sep 17 00:00:00 2001 From: dgarus Date: Wed, 24 May 2023 12:40:26 +0300 Subject: [PATCH 39/39] fix review comments --- protocols/request-response/src/cbor.rs | 11 +++++++++-- protocols/request-response/src/json.rs | 10 ++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/protocols/request-response/src/cbor.rs b/protocols/request-response/src/cbor.rs index 323c750d6c7..b7c520c72b9 100644 --- a/protocols/request-response/src/cbor.rs +++ b/protocols/request-response/src/cbor.rs @@ -115,8 +115,8 @@ mod codec { T: AsyncWrite + Unpin + Send, { let data: Vec = serde_cbor::to_vec(&req).map_err(into_io_error)?; + io.write_all(data.as_ref()).await?; - io.close().await?; Ok(()) } @@ -131,8 +131,8 @@ mod codec { T: AsyncWrite + Unpin + Send, { let data: Vec = serde_cbor::to_vec(&resp).map_err(into_io_error).unwrap(); + io.write_all(data.as_ref()).await?; - io.close().await?; Ok(()) } @@ -155,6 +155,7 @@ mod codec { mod tests { use crate::cbor::codec::Codec; use crate::Codec as _; + use futures::AsyncWriteExt; use futures_ringbuf::Endpoint; use libp2p_swarm::StreamProtocol; use serde::{Deserialize, Serialize}; @@ -175,10 +176,13 @@ mod tests { .write_request(&protocol, &mut a, expected_request.clone()) .await .expect("Should write request"); + a.close().await.unwrap(); + let actual_request = codec .read_request(&protocol, &mut b) .await .expect("Should read request"); + b.close().await.unwrap(); assert_eq!(actual_request, expected_request); @@ -187,10 +191,13 @@ mod tests { .write_response(&protocol, &mut a, expected_response.clone()) .await .expect("Should write response"); + a.close().await.unwrap(); + let actual_response = codec .read_response(&protocol, &mut b) .await .expect("Should read response"); + b.close().await.unwrap(); assert_eq!(actual_response, expected_response); } diff --git a/protocols/request-response/src/json.rs b/protocols/request-response/src/json.rs index dbbb1a3df32..0b3d634573b 100644 --- a/protocols/request-response/src/json.rs +++ b/protocols/request-response/src/json.rs @@ -117,7 +117,6 @@ mod codec { let data = serde_json::to_vec(&req)?; io.write_all(data.as_ref()).await?; - io.close().await?; Ok(()) } @@ -132,8 +131,8 @@ mod codec { T: AsyncWrite + Unpin + Send, { let data = serde_json::to_vec(&resp)?; + io.write_all(data.as_ref()).await?; - io.close().await?; Ok(()) } @@ -143,6 +142,7 @@ mod codec { #[cfg(test)] mod tests { use crate::Codec; + use futures::AsyncWriteExt; use futures_ringbuf::Endpoint; use libp2p_swarm::StreamProtocol; use serde::{Deserialize, Serialize}; @@ -164,10 +164,13 @@ mod tests { .write_request(&protocol, &mut a, expected_request.clone()) .await .expect("Should write request"); + a.close().await.unwrap(); + let actual_request = codec .read_request(&protocol, &mut b) .await .expect("Should read request"); + b.close().await.unwrap(); assert_eq!(actual_request, expected_request); @@ -176,10 +179,13 @@ mod tests { .write_response(&protocol, &mut a, expected_response.clone()) .await .expect("Should write response"); + a.close().await.unwrap(); + let actual_response = codec .read_response(&protocol, &mut b) .await .expect("Should read response"); + b.close().await.unwrap(); assert_eq!(actual_response, expected_response); }