1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//! ## Summary
//!
//! A multiplexed p2p network framework based on yamux that supports mounting custom protocols.
//!
//! The crate is aimed at implementing a framework that light weight, simple, reliable, high performance, and friendly to users.
//!
//! ### Concept
//!
//! #### Multiaddr
//!
//! [Multiaddr](https://github.com/multiformats/multiaddr) aims to make network addresses future-proof, composable, and efficient.
//!
//! It can express almost all network protocols, such as:
//! - TCP/IP: `/ip4/127.0.0.1/tcp/1337`
//! - DNS/IP: `/dns4/localhost/tcp/1337`
//! - UDP: `/ip4/127.0.0.1/udp/1234`
//! - Websocket: `/ip4/127.0.0.1/tcp/1337/ws`
//!
//! #### Protocol
//!
//! In this library, the most important concept is protocol, so we need to clarify what is the protocol defined by tentacle.
//!
//! If you use the simplest way to understand, then it can be compared to the http protocol on the TCP protocol,
//! that is, each protocol can have its own behavior standards, analysis methods, etc.
//!
//! As a framework, a builder is provided to describe the standard protocol, and three traits
//! are provided to define the behavior of the protocol. Unfortunately, users also need an event trait to
//! perceive other events that occur in the service, such as session establishment, protocol exceptions, etc.
//!
//! builder: [`MetaBuilder`]
//!
//! traits: [`ServiceProtocol`]/[`SessionProtocol`]/[`ProtocolSpawn`]
//!
//! event trait: [`ServiceHandle`]
//!
//! The biggest question users may have is why there are three different traits defining protocol behavior.
//!
//! These three traits can be divided into two groups, representing two different design ideas:
//!
//! ##### Callback
//!
//! user can only do what it defines, tentacle has stronger constraints on it
//!
//! [`ServiceProtocol`] defines a globally unique single-protocol processing capability, and
//! all the protocol data and behaviors opened by the session will be summarized here for processing.
//! Its lifetime is from the start of the service to the end.
//!
//! [`SessionProtocol`] its lifetime is only in the phase when the corresponding session is opened,
//! the session is disconnected, and the handle drop, session open, handle open. That's all.
//!
//! Obviously, there is an abstraction of data interception
//!
//! ##### Stream
//!
//! user can combine stream with any existing future ecology, tentacle restricts it weaker
//!
//! [`ProtocolSpawn`] Users can get a perfect asynchronous environment to make complex control flow,
//! and even use this trait to implement data summary processing in callback mode again
//!
//! Compared with callback mode, it reduces a layer of abstraction
//!
//! ### Use tentacle
//!
//! 1. Define protocol behavior with [Callback](#callback) or [Stream](#stream) trait
//! 2. Define [`ServiceHandle`] to process other event output on tentacle service
//! 3. Build all need [`ProtocolMeta`] from [`MetaBuilder`]
//! 4. Register all [`ProtocolMeta`] into [`ServiceBuilder`], then build [`Service`]
//! 5. Setup an async runtime such as tokio
//! 6. Run [`Service`] just like other stream, maybe keep a [`Control`] on some place which want to
//!    communicate with background [`Service`]
//!
//! ### Feature flags
//! Tentacle uses the Feature flag to enable some optional functions and split the smallest dependencies
//! for use as much as possible. Users can choose the dependencies they want according to their needs.
//!
//! Related to runtime :
//! - `tokio-runtime`: Enable by default, use tokio runtime
//! - `tokio-timer`: Enable by default, use tokio inner timer
//!
//! - `async-timer`: use async-io timer
//! - `async-runtime`: use async-std runtime
//!
//! - `generic-timer`: use futures-timer
//! - `wasm-timer`: only use on Browser WebAssembly platform
//!
//! Function related:
//! - `ws`: Enable websocket protocol support
//! - `upnp`: Enable upnp protocol, automatically try to register the port to the gateway
//! - `unstable`: Enable the feature that has not yet decided to stabilize the API
//! - `parking_lot`: Enable priority channel use `parking_lot`
//!
//! [`MetaBuilder`]: crate::builder::MetaBuilder
//! [`ServiceBuilder`]: crate::builder::ServiceBuilder
//! [`ServiceProtocol`]: crate::traits::ServiceProtocol
//! [`SessionProtocol`]: crate::traits::SessionProtocol
//! [`ProtocolSpawn`]: crate::traits::ProtocolSpawn
//! [`ServiceHandle`]: crate::traits::ServiceHandle
//! [`ProtocolMeta`]: crate::service::config::ProtocolMeta
//! [`Control`]: crate::service::ServiceControl
//! [`Service`]: crate::service::Service
//!

#![deny(missing_docs)]
#![cfg_attr(
    target_arch = "wasm32",
    allow(dead_code, unused_variables, unused_imports)
)]

/// Re-pub bytes crate
pub use bytes;
/// Re-pub multiaddr crate
pub use multiaddr;
/// Re-pub secio crate
pub use secio;
/// Re-pub yamux crate
pub use yamux;

/// Buffer management in distribution mode
pub(crate) mod buffer;
/// Some gadgets that help create a service
pub mod builder;
/// Context for Session and Service
pub mod context;
/// Error
pub mod error;
pub(crate) mod lock;
/// Protocol handle callback stream
pub(crate) mod protocol_handle_stream;
/// Protocol select
pub mod protocol_select;
/// An abstraction of p2p service
pub mod service;
/// Wrapper for real data streams
pub(crate) mod session;
/// Each custom protocol in a session corresponds to a sub stream
pub(crate) mod substream;
/// Useful traits
pub mod traits;
/// Underlying transport protocols wrapper
pub(crate) mod transports;
/// Some useful functions
pub mod utils;

mod channel;
#[cfg_attr(not(feature = "unstable"), doc(hidden))]
#[allow(missing_docs)]
pub mod runtime;

#[cfg(all(not(target_arch = "wasm32"), feature = "upnp"))]
pub(crate) mod upnp;

use std::{fmt, ops::AddAssign};

pub use substream::SubstreamReadPart;

/// Index of sub/protocol stream
type StreamId = usize;
/// Protocol id
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, Default)]
pub struct ProtocolId(usize);

impl ProtocolId {
    /// New a protocol id
    pub const fn new(id: usize) -> Self {
        ProtocolId(id)
    }

    /// Get inner value
    pub const fn value(self) -> usize {
        self.0
    }
}

impl fmt::Display for ProtocolId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ProtocolId({})", self.0)
    }
}

impl From<usize> for ProtocolId {
    fn from(id: usize) -> Self {
        ProtocolId::new(id)
    }
}

impl std::hash::Hash for ProtocolId {
    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
        hasher.write_usize(self.0)
    }
}

impl PartialEq for ProtocolId {
    fn eq(&self, other: &ProtocolId) -> bool {
        self.0.eq(&other.0)
    }
}

impl nohash_hasher::IsEnabled for ProtocolId {}

/// Index of session
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, Default)]
pub struct SessionId(usize);

impl SessionId {
    /// New a session id
    pub const fn new(id: usize) -> Self {
        SessionId(id)
    }

    /// Get inner value
    pub const fn value(self) -> usize {
        self.0
    }

    pub(crate) const fn wrapping_add(self, rhs: usize) -> SessionId {
        SessionId(self.0.wrapping_add(rhs))
    }
}

impl fmt::Display for SessionId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SessionId({})", self.0)
    }
}

impl AddAssign<usize> for SessionId {
    fn add_assign(&mut self, rhs: usize) {
        self.0 += rhs
    }
}

impl From<usize> for SessionId {
    fn from(id: usize) -> Self {
        SessionId(id)
    }
}

impl std::hash::Hash for SessionId {
    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
        hasher.write_usize(self.0)
    }
}

impl PartialEq for SessionId {
    fn eq(&self, other: &SessionId) -> bool {
        self.0.eq(&other.0)
    }
}

impl nohash_hasher::IsEnabled for SessionId {}