-
Notifications
You must be signed in to change notification settings - Fork 930
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(request-response): add modules for json
and cbor
messages
#3952
Merged
Merged
Changes from 42 commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
dd98771
3905 Serde support
dgarus 55bc76a
fix failures
dgarus a5f3ed9
fix failures
dgarus 5304fb8
fix failures
dgarus e9628ee
Fix format
dgarus 4f8150d
Merge branch 'master' into 3905-serde_support
dgarus 23b7722
Fix failure
dgarus 11e0783
Cbor behavior
dgarus ade1842
Cbor behavior
dgarus 771560c
Format
dgarus 2a6693e
Fix test
dgarus d4bff4c
Fix review comments
dgarus 92d9862
Fix tests running
dgarus a0b8ee5
Fix tests running
dgarus 1129dee
Added features `json` and `cbor`
dgarus a8f9ee9
CHANGELOG
dgarus 36eaf02
Update protocols/request-response/CHANGELOG.md
dgarus 61f7d48
Update protocols/request-response/src/cbor.rs
dgarus d78c17b
Update protocols/request-response/src/cbor.rs
dgarus 0ee31bd
Update protocols/request-response/src/cbor.rs
dgarus d4044a5
Update protocols/request-response/src/cbor.rs
dgarus 416f271
fix review comments
dgarus 9d20aaa
fix serde feature should be in the test scope
dgarus ed5da99
No default features
dgarus 2d70235
swarm-derive should be optional
dgarus 9c7d967
Codecs are `pub(crate)`
dgarus 3d4b12b
Merge branch 'master' into 3905-serde_support
dgarus 3839599
revert
dgarus 4e95f78
Merge remote-tracking branch 'origin/3905-serde_support' into 3905-se…
dgarus 15b0cb5
Hide codec implementation from public API
thomaseizinger 30c9420
Merge branch 'master' into 3905-serde_support
dgarus c6a0327
Hide json::Codec
dgarus 9d6d608
Remove `libp2p-swarm-derive` dep
dgarus 5cdb636
Read/write without prefixed size
dgarus c177570
fix format
dgarus 60f029c
fix format
dgarus 0fb3e78
fix bug
dgarus 12a1322
Deref for behaviors
dgarus 4122c4e
fix format
dgarus 74669a3
Rename constructors to better support type aliases
thomaseizinger 2da4b43
move fn read_to_end to request_response crate
dgarus 20b2c07
Merge branch 'master' into 3905-serde_support
dgarus 207f15a
fix review comments
dgarus 9047c7a
Merge branch 'master' into 3905-serde_support
dgarus 8197278
fix review comments
dgarus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
// 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. | ||
|
||
/// 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::<GreetRequest, GreetResponse>::new( | ||
/// [(StreamProtocol::new("/my-cbor-protocol"), ProtocolSupport::Full)], | ||
/// request_response::Config::default() | ||
/// ); | ||
/// ``` | ||
pub type Behaviour<Req, Resp> = crate::Behaviour<codec::Codec<Req, Resp>>; | ||
|
||
mod codec { | ||
use crate::codec::read_to_end; | ||
use async_trait::async_trait; | ||
use futures::prelude::*; | ||
use futures::{AsyncRead, AsyncWrite}; | ||
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; | ||
|
||
pub struct Codec<Req, Resp> { | ||
phantom: PhantomData<(Req, Resp)>, | ||
} | ||
|
||
impl<Req, Resp> Default for Codec<Req, Resp> { | ||
fn default() -> Self { | ||
Codec { | ||
phantom: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<Req, Resp> Clone for Codec<Req, Resp> { | ||
fn clone(&self) -> Self { | ||
Self::default() | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl<Req, Resp> crate::Codec for Codec<Req, Resp> | ||
where | ||
Req: Send + Serialize + DeserializeOwned, | ||
Resp: Send + Serialize + DeserializeOwned, | ||
{ | ||
type Protocol = StreamProtocol; | ||
type Request = Req; | ||
type Response = Resp; | ||
|
||
async fn read_request<T>(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result<Req> | ||
where | ||
T: AsyncRead + Unpin + Send, | ||
{ | ||
let vec = read_to_end(io, REQUEST_SIZE_MAXIMUM).await?; | ||
|
||
serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) | ||
} | ||
|
||
async fn read_response<T>(&mut self, _: &Self::Protocol, io: &mut T) -> io::Result<Resp> | ||
where | ||
T: AsyncRead + Unpin + Send, | ||
{ | ||
let vec = read_to_end(io, RESPONSE_SIZE_MAXIMUM).await?; | ||
|
||
serde_cbor::from_slice(vec.as_slice()).map_err(into_io_error) | ||
} | ||
|
||
async fn write_request<T>( | ||
&mut self, | ||
_: &Self::Protocol, | ||
io: &mut T, | ||
req: Self::Request, | ||
) -> io::Result<()> | ||
where | ||
T: AsyncWrite + Unpin + Send, | ||
{ | ||
let data: Vec<u8> = serde_cbor::to_vec(&req).map_err(into_io_error)?; | ||
io.write_all(data.as_ref()).await?; | ||
io.flush().await?; | ||
io.close().await?; | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn write_response<T>( | ||
&mut self, | ||
_: &Self::Protocol, | ||
io: &mut T, | ||
resp: Self::Response, | ||
) -> io::Result<()> | ||
where | ||
T: AsyncWrite + Unpin + Send, | ||
{ | ||
let data: Vec<u8> = 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(()) | ||
} | ||
} | ||
|
||
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::cbor::codec::Codec; | ||
use crate::Codec as _; | ||
use futures_ringbuf::Endpoint; | ||
use libp2p_swarm::StreamProtocol; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[async_std::test] | ||
async fn test_codec() { | ||
let expected_request = TestRequest { | ||
payload: "test_payload".to_string(), | ||
}; | ||
let expected_response = TestResponse { | ||
payload: "test_payload".to_string(), | ||
}; | ||
let protocol = StreamProtocol::new("/test_cbor/1"); | ||
let mut codec = Codec::default(); | ||
|
||
let (mut a, mut b) = Endpoint::pair(124, 124); | ||
codec | ||
.write_request(&protocol, &mut a, expected_request.clone()) | ||
.await | ||
.expect("Should write request"); | ||
let actual_request = codec | ||
.read_request(&protocol, &mut b) | ||
.await | ||
.expect("Should read request"); | ||
|
||
assert_eq!(actual_request, expected_request); | ||
|
||
let (mut a, mut b) = Endpoint::pair(124, 124); | ||
codec | ||
.write_response(&protocol, &mut a, expected_response.clone()) | ||
.await | ||
.expect("Should write response"); | ||
let actual_response = codec | ||
.read_response(&protocol, &mut b) | ||
.await | ||
.expect("Should read response"); | ||
|
||
assert_eq!(actual_response, expected_response); | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
struct TestRequest { | ||
payload: String, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
struct TestResponse { | ||
payload: String, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonderful how simple this turned out to be. Thanks @dgarus and @thomaseizinger!