mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-07-01 10:41:33 +00:00
Add libp2p-request-response protocol. (#1596)
* Add the libp2p-request-response protocol. This crate provides a generic implementation for request/response protocols, whereby each request is sent on a new substream. * Fix OneShotHandler usage in floodsub. * Custom ProtocolsHandler and multiple protocols. 1. Implement a custom ProtocolsHandler instead of using the OneShotHandler for better control and error handling. In particular, all request/response sending/receiving is kept in the substreams upgrades and thus the background task of a connection. 2. Support multiple protocols (usually protocol versions) with a single `RequestResponse` instance, with configurable inbound/outbound support. * Small doc clarification. * Remove unnecessary Sync bounds. * Remove redundant Clone constraint. * Update protocols/request-response/Cargo.toml Co-authored-by: Toralf Wittner <tw@dtex.org> * Update dev-dependencies. * Update Cargo.tomls. * Add changelog. * Remove Sync bound from RequestResponseCodec::Protocol. Apparently the compiler just needs some help with the scope of borrows, which is unfortunate. * Try async-trait. * Allow checking whether a ResponseChannel is still open. Also expand the commentary on `send_response` to indicate that responses may be discard if they come in too late. * Add `RequestResponse::is_pending`. As an analogue of `ResponseChannel::is_open` for outbound requests. * Revert now unnecessary changes to the OneShotHandler. Since `libp2p-request-response` is no longer using it. * Update CHANGELOG for libp2p-swarm. Co-authored-by: Toralf Wittner <tw@dtex.org>
This commit is contained in:
165
protocols/request-response/src/handler/protocol.rs
Normal file
165
protocols/request-response/src/handler/protocol.rs
Normal file
@ -0,0 +1,165 @@
|
||||
// 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.
|
||||
|
||||
//! The definition of a request/response protocol via inbound
|
||||
//! and outbound substream upgrades. The inbound upgrade
|
||||
//! receives a request and sends a response, whereas the
|
||||
//! outbound upgrade send a request and receives a response.
|
||||
|
||||
use crate::RequestId;
|
||||
use crate::codec::RequestResponseCodec;
|
||||
|
||||
use futures::{
|
||||
channel::oneshot,
|
||||
future::BoxFuture,
|
||||
prelude::*,
|
||||
};
|
||||
use libp2p_core::{
|
||||
upgrade::{InboundUpgrade, OutboundUpgrade, UpgradeInfo},
|
||||
};
|
||||
use libp2p_swarm::{
|
||||
NegotiatedSubstream,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::io;
|
||||
|
||||
/// The level of support for a particular protocol.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ProtocolSupport {
|
||||
/// The protocol is only supported for inbound requests.
|
||||
Inbound,
|
||||
/// The protocol is only supported for outbound requests.
|
||||
Outbound,
|
||||
/// The protocol is supported for inbound and outbound requests.
|
||||
Full
|
||||
}
|
||||
|
||||
impl ProtocolSupport {
|
||||
/// Whether inbound requests are supported.
|
||||
pub fn inbound(&self) -> bool {
|
||||
match self {
|
||||
ProtocolSupport::Inbound | ProtocolSupport::Full => true,
|
||||
ProtocolSupport::Outbound => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether outbound requests are supported.
|
||||
pub fn outbound(&self) -> bool {
|
||||
match self {
|
||||
ProtocolSupport::Outbound | ProtocolSupport::Full => true,
|
||||
ProtocolSupport::Inbound => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Response substream upgrade protocol.
|
||||
///
|
||||
/// Receives a request and sends a response.
|
||||
#[derive(Debug)]
|
||||
pub struct ResponseProtocol<TCodec>
|
||||
where
|
||||
TCodec: RequestResponseCodec
|
||||
{
|
||||
pub(crate) codec: TCodec,
|
||||
pub(crate) protocols: SmallVec<[TCodec::Protocol; 2]>,
|
||||
pub(crate) request_sender: oneshot::Sender<TCodec::Request>,
|
||||
pub(crate) response_receiver: oneshot::Receiver<TCodec::Response>
|
||||
}
|
||||
|
||||
impl<TCodec> UpgradeInfo for ResponseProtocol<TCodec>
|
||||
where
|
||||
TCodec: RequestResponseCodec
|
||||
{
|
||||
type Info = TCodec::Protocol;
|
||||
type InfoIter = smallvec::IntoIter<[Self::Info; 2]>;
|
||||
|
||||
fn protocol_info(&self) -> Self::InfoIter {
|
||||
self.protocols.clone().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<TCodec> InboundUpgrade<NegotiatedSubstream> for ResponseProtocol<TCodec>
|
||||
where
|
||||
TCodec: RequestResponseCodec + Send + 'static,
|
||||
{
|
||||
type Output = ();
|
||||
type Error = io::Error;
|
||||
type Future = BoxFuture<'static, Result<Self::Output, Self::Error>>;
|
||||
|
||||
fn upgrade_inbound(mut self, mut io: NegotiatedSubstream, protocol: Self::Info) -> Self::Future {
|
||||
async move {
|
||||
let read = self.codec.read_request(&protocol, &mut io);
|
||||
let request = read.await?;
|
||||
if let Ok(()) = self.request_sender.send(request) {
|
||||
if let Ok(response) = self.response_receiver.await {
|
||||
let write = self.codec.write_response(&protocol, &mut io, response);
|
||||
write.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Request substream upgrade protocol.
|
||||
///
|
||||
/// Sends a request and receives a response.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RequestProtocol<TCodec>
|
||||
where
|
||||
TCodec: RequestResponseCodec
|
||||
{
|
||||
pub(crate) codec: TCodec,
|
||||
pub(crate) protocols: SmallVec<[TCodec::Protocol; 2]>,
|
||||
pub(crate) request_id: RequestId,
|
||||
pub(crate) request: TCodec::Request,
|
||||
}
|
||||
|
||||
impl<TCodec> UpgradeInfo for RequestProtocol<TCodec>
|
||||
where
|
||||
TCodec: RequestResponseCodec
|
||||
{
|
||||
type Info = TCodec::Protocol;
|
||||
type InfoIter = smallvec::IntoIter<[Self::Info; 2]>;
|
||||
|
||||
fn protocol_info(&self) -> Self::InfoIter {
|
||||
self.protocols.clone().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<TCodec> OutboundUpgrade<NegotiatedSubstream> for RequestProtocol<TCodec>
|
||||
where
|
||||
TCodec: RequestResponseCodec + Send + 'static,
|
||||
{
|
||||
type Output = TCodec::Response;
|
||||
type Error = io::Error;
|
||||
type Future = BoxFuture<'static, Result<Self::Output, Self::Error>>;
|
||||
|
||||
fn upgrade_outbound(mut self, mut io: NegotiatedSubstream, protocol: Self::Info) -> Self::Future {
|
||||
async move {
|
||||
let write = self.codec.write_request(&protocol, &mut io, self.request);
|
||||
write.await?;
|
||||
let read = self.codec.read_response(&protocol, &mut io);
|
||||
let response = read.await?;
|
||||
Ok(response)
|
||||
}.boxed()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user