libp2prs_core/
upgrade.rs

1// Copyright 2018 Parity Technologies (UK) Ltd.
2// Copyright 2020 Netwarps Ltd.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22//! Contains everything related to upgrading a connection to use a protocol.
23//!
24//! After a connection with a remote has been successfully established. The next step
25//! is to *upgrade* this connection to use a protocol.
26//!
27//! This is where the `Upgrader` traits come into play.
28//! The trait is implemented on types that represent a collection of one or more possible
29//! protocols for respectively an ingoing or outgoing connection.
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 `Upgrader::upgrade_inbound` or
44//!   `Upgrader::upgrade_outbound` method is called. This method will return a upgraded
45//!   'Connection'. This handshake is considered mandatory, however in practice it is
46//!   possible for the trait implementation to return a dummy `Connection` that doesn't perform any
47//!   action and immediately succeeds.
48//!
49//! After an upgrade is successful, an object of type `Upgrader::Output` is returned.
50//! The actual object depends on the implementation and there is no constraint on the traits that
51//! it should implement, however it is expected that it can be used by the user to control the
52//! behaviour of the protocol.
53//!
54
55use std::fmt;
56
57use async_trait::async_trait;
58
59use crate::transport::TransportError;
60
61pub use self::{dummy::DummyUpgrader, select::Selector};
62pub(crate) mod dummy;
63pub(crate) mod multistream;
64pub(crate) mod select;
65
66/// Types serving as protocol names.
67///
68/// # Context
69///
70/// In situations where we provide a list of protocols that we support,
71/// the elements of that list are required to implement the [`ProtocolName`] trait.
72///
73/// Libp2p will call [`ProtocolName::protocol_name`] on each element of that list, and transmit the
74/// returned value on the network. If the remote accepts a given protocol, the element
75/// serves as the return value of the function that performed the negotiation.
76///
77/// # Example
78///
79/// ```
80/// use libp2prs_core::upgrade::ProtocolName;
81///
82/// enum MyProtocolName {
83///     Version1,
84///     Version2,
85///     Version3,
86/// }
87///
88/// impl ProtocolName for MyProtocolName {
89///     fn protocol_name(&self) -> &[u8] {
90///         match *self {
91///             MyProtocolName::Version1 => b"/myproto/1.0",
92///             MyProtocolName::Version2 => b"/myproto/2.0",
93///             MyProtocolName::Version3 => b"/myproto/3.0",
94///         }
95///     }
96/// }
97/// ```
98///
99pub trait ProtocolName {
100    /// The protocol name as bytes. Transmitted on the network.
101    ///
102    /// **Note:** Valid protocol names must start with `/` and
103    /// not exceed 140 bytes in length.
104    fn protocol_name(&self) -> &[u8];
105}
106
107impl<T: AsRef<[u8]>> ProtocolName for T {
108    fn protocol_name(&self) -> &[u8] {
109        self.as_ref()
110    }
111}
112
113/// A general new type implementation of ProtocolName trait.
114///
115/// It is used to simplify the usage of ProtocolName. Furthermore, it
116/// provides more friendly Debug and Display, which can output a
117/// readable string instead of an array [...]
118#[derive(Clone, PartialOrd, PartialEq, Eq, Hash)]
119pub struct ProtocolId(&'static [u8]);
120
121impl fmt::Debug for ProtocolId {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        write!(f, "ProtocolId({})", String::from_utf8_lossy(&self.0))
124    }
125}
126
127impl fmt::Display for ProtocolId {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        write!(f, "{}", String::from_utf8_lossy(&self.0))
130    }
131}
132
133// impl ProtocolName for ProtocolId {
134//     fn protocol_name(&self) -> &[u8] {
135//         self.0.as_ref()
136//     }
137// }
138
139impl AsRef<[u8]> for ProtocolId {
140    fn as_ref(&self) -> &[u8] {
141        self.0
142    }
143}
144
145// impl From<Bytes> for ProtocolId {
146//     fn from(value: Bytes) -> Self {
147//         ProtocolId(value)
148//     }
149// }
150
151impl From<&'static [u8]> for ProtocolId {
152    fn from(value: &'static [u8]) -> Self {
153        ProtocolId(value)
154    }
155}
156
157pub trait UpgradeInfo: Send {
158    /// Opaque type representing a negotiable protocol.
159    type Info: ProtocolName + Clone + Send + Sync + fmt::Debug;
160
161    /// Returns the list of protocols that are supported. Used during the negotiation process.
162    fn protocol_info(&self) -> Vec<Self::Info>;
163}
164
165/// Common trait for upgrades that can be applied on a connection.
166#[async_trait]
167pub trait Upgrader<C>: UpgradeInfo + Clone {
168    /// Output after the upgrade has been successfully negotiated and the handshake performed.
169    type Output: Send;
170
171    /// After we have determined that the remote supports one of the protocols we support, this
172    /// method is called to start the handshake.
173    ///
174    /// The `info` is the identifier of the protocol, as produced by `protocol_info`.
175    async fn upgrade_inbound(self, socket: C, info: <Self as UpgradeInfo>::Info) -> Result<Self::Output, TransportError>;
176
177    /// After we have determined that the remote supports one of the protocols we support, this
178    /// method is called to start the handshake.
179    ///
180    /// The `info` is the identifier of the protocol, as produced by `protocol_info`.
181    async fn upgrade_outbound(self, socket: C, info: <Self as UpgradeInfo>::Info) -> Result<Self::Output, TransportError>;
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187
188    #[test]
189    fn upgrade_info_multi_versions() {
190        #[derive(PartialEq, Debug, Clone)]
191        enum MyProtocolName {
192            Version1,
193            Version2,
194            Version3,
195        }
196
197        impl ProtocolName for MyProtocolName {
198            fn protocol_name(&self) -> &[u8] {
199                match *self {
200                    MyProtocolName::Version1 => b"/myproto/1.0",
201                    MyProtocolName::Version2 => b"/myproto/2.0",
202                    MyProtocolName::Version3 => b"/myproto/3.0",
203                }
204            }
205        }
206
207        struct P;
208
209        impl UpgradeInfo for P {
210            type Info = MyProtocolName;
211            fn protocol_info(&self) -> Vec<Self::Info> {
212                vec![MyProtocolName::Version1, MyProtocolName::Version2, MyProtocolName::Version3]
213            }
214        }
215
216        let p = P {};
217
218        assert_eq!(p.protocol_info().get(0).unwrap(), &MyProtocolName::Version1);
219        assert_eq!(p.protocol_info().get(1).unwrap(), &MyProtocolName::Version2);
220        assert_eq!(p.protocol_info().get(2).unwrap(), &MyProtocolName::Version3);
221    }
222
223    #[test]
224    fn protocol_id() {
225        let p = ProtocolId::from(b"protocol" as &[u8]);
226
227        assert_eq!(p.to_string(), "protocol");
228    }
229}