libp2p_core/
upgrade.rs

1// Copyright 2018 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21//! Contains everything related to upgrading a connection or a substream to use a protocol.
22//!
23//! After a connection with a remote has been successfully established or a substream successfully
24//! opened, the next step is to *upgrade* this connection or substream to use a protocol.
25//!
26//! This is where the `UpgradeInfo`, `InboundUpgrade` and `OutboundUpgrade` traits come into play.
27//! The `InboundUpgrade` and `OutboundUpgrade` traits are implemented on types that represent a
28//! collection of one or more possible protocols for respectively an ingoing or outgoing
29//! connection or substream.
30//!
31//! > **Note**: Multiple versions of the same protocol are treated as different protocols.
32//! >           For example, `/foo/1.0.0` and `/foo/1.1.0` are totally unrelated as far as
33//! >           upgrading is concerned.
34//!
35//! # Upgrade process
36//!
37//! An upgrade is performed in two steps:
38//!
39//! - A protocol negotiation step. The `UpgradeInfo::protocol_info` method is called to determine
40//!   which protocols are supported by the trait implementation. The `multistream-select` protocol
41//!   is used in order to agree on which protocol to use amongst the ones supported.
42//!
43//! - A handshake. After a successful negotiation, the `InboundUpgrade::upgrade_inbound` or
44//!   `OutboundUpgrade::upgrade_outbound` method is called. This method will return a `Future` that
45//!   performs a handshake. This handshake is considered mandatory, however in practice it is
46//!   possible for the trait implementation to return a dummy `Future` that doesn't perform any
47//!   action and immediately succeeds.
48//!
49//! After an upgrade is successful, an object of type `InboundUpgrade::Output` or
50//! `OutboundUpgrade::Output` is returned. The actual object depends on the implementation and
51//! there is no constraint on the traits that it should implement, however it is expected that it
52//! can be used by the user to control the behaviour of the protocol.
53//!
54//! > **Note**: You can use the `apply_inbound` or `apply_outbound` methods to try upgrade a
55//!             connection or substream. However if you use the recommended `Swarm` or
56//!             `ConnectionHandler` APIs, the upgrade is automatically handled for you and you don't
57//!             need to use these methods.
58//!
59
60mod apply;
61mod denied;
62mod either;
63mod error;
64mod pending;
65mod ready;
66mod select;
67mod transfer;
68
69pub(crate) use apply::{
70    apply, apply_inbound, apply_outbound, InboundUpgradeApply, OutboundUpgradeApply,
71};
72pub(crate) use error::UpgradeError;
73use futures::future::Future;
74
75pub use self::{
76    denied::DeniedUpgrade,
77    pending::PendingUpgrade,
78    ready::ReadyUpgrade,
79    select::SelectUpgrade,
80    transfer::{read_length_prefixed, read_varint, write_length_prefixed, write_varint},
81};
82pub use crate::Negotiated;
83pub use multistream_select::{NegotiatedComplete, NegotiationError, ProtocolError, Version};
84
85/// Common trait for upgrades that can be applied on inbound substreams, outbound substreams,
86/// or both.
87pub trait UpgradeInfo {
88    /// Opaque type representing a negotiable protocol.
89    type Info: AsRef<str> + Clone;
90    /// Iterator returned by `protocol_info`.
91    type InfoIter: IntoIterator<Item = Self::Info>;
92
93    /// Returns the list of protocols that are supported. Used during the negotiation process.
94    fn protocol_info(&self) -> Self::InfoIter;
95}
96
97/// Possible upgrade on an inbound connection or substream.
98pub trait InboundUpgrade<C>: UpgradeInfo {
99    /// Output after the upgrade has been successfully negotiated and the handshake performed.
100    type Output;
101    /// Possible error during the handshake.
102    type Error;
103    /// Future that performs the handshake with the remote.
104    type Future: Future<Output = Result<Self::Output, Self::Error>>;
105
106    /// After we have determined that the remote supports one of the protocols we support, this
107    /// method is called to start the handshake.
108    ///
109    /// The `info` is the identifier of the protocol, as produced by `protocol_info`.
110    fn upgrade_inbound(self, socket: C, info: Self::Info) -> Self::Future;
111}
112
113/// Possible upgrade on an outbound connection or substream.
114pub trait OutboundUpgrade<C>: UpgradeInfo {
115    /// Output after the upgrade has been successfully negotiated and the handshake performed.
116    type Output;
117    /// Possible error during the handshake.
118    type Error;
119    /// Future that performs the handshake with the remote.
120    type Future: Future<Output = Result<Self::Output, Self::Error>>;
121
122    /// After we have determined that the remote supports one of the protocols we support, this
123    /// method is called to start the handshake.
124    ///
125    /// The `info` is the identifier of the protocol, as produced by `protocol_info`.
126    fn upgrade_outbound(self, socket: C, info: Self::Info) -> Self::Future;
127}
128
129/// Possible upgrade on an inbound connection
130pub trait InboundConnectionUpgrade<T>: UpgradeInfo {
131    /// Output after the upgrade has been successfully negotiated and the handshake performed.
132    type Output;
133    /// Possible error during the handshake.
134    type Error;
135    /// Future that performs the handshake with the remote.
136    type Future: Future<Output = Result<Self::Output, Self::Error>>;
137
138    /// After we have determined that the remote supports one of the protocols we support, this
139    /// method is called to start the handshake.
140    ///
141    /// The `info` is the identifier of the protocol, as produced by `protocol_info`.
142    fn upgrade_inbound(self, socket: T, info: Self::Info) -> Self::Future;
143}
144
145/// Possible upgrade on an outbound connection
146pub trait OutboundConnectionUpgrade<T>: UpgradeInfo {
147    /// Output after the upgrade has been successfully negotiated and the handshake performed.
148    type Output;
149    /// Possible error during the handshake.
150    type Error;
151    /// Future that performs the handshake with the remote.
152    type Future: Future<Output = Result<Self::Output, Self::Error>>;
153
154    /// After we have determined that the remote supports one of the protocols we support, this
155    /// method is called to start the handshake.
156    ///
157    /// The `info` is the identifier of the protocol, as produced by `protocol_info`.
158    fn upgrade_outbound(self, socket: T, info: Self::Info) -> Self::Future;
159}
160
161// Blanket implementation for InboundConnectionUpgrade based on InboundUpgrade for backwards compatibility
162impl<U, T> InboundConnectionUpgrade<T> for U
163where
164    U: InboundUpgrade<T>,
165{
166    type Output = <U as InboundUpgrade<T>>::Output;
167    type Error = <U as InboundUpgrade<T>>::Error;
168    type Future = <U as InboundUpgrade<T>>::Future;
169
170    fn upgrade_inbound(self, socket: T, info: Self::Info) -> Self::Future {
171        self.upgrade_inbound(socket, info)
172    }
173}
174
175// Blanket implementation for OutboundConnectionUpgrade based on OutboundUpgrade for backwards compatibility
176impl<U, T> OutboundConnectionUpgrade<T> for U
177where
178    U: OutboundUpgrade<T>,
179{
180    type Output = <U as OutboundUpgrade<T>>::Output;
181    type Error = <U as OutboundUpgrade<T>>::Error;
182    type Future = <U as OutboundUpgrade<T>>::Future;
183
184    fn upgrade_outbound(self, socket: T, info: Self::Info) -> Self::Future {
185        self.upgrade_outbound(socket, info)
186    }
187}