litep2p/yamux/mod.rs
1// Copyright (c) 2018-2019 Parity Technologies (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 or MIT license, at your option.
4//
5// A copy of the Apache License, Version 2.0 is included in the software as
6// LICENSE-APACHE and a copy of the MIT license is included in the software
7// as LICENSE-MIT. You may also obtain a copy of the Apache License, Version 2.0
8// at https://www.apache.org/licenses/LICENSE-2.0 and a copy of the MIT license
9// at https://opensource.org/licenses/MIT.
10
11//! This crate implements the [Yamux specification][1].
12//!
13//! It multiplexes independent I/O streams over reliable, ordered connections,
14//! such as TCP/IP.
15//!
16//! The three primary objects, clients of this crate interact with, are:
17//!
18//! - [`Connection`], which wraps the underlying I/O resource, e.g. a socket,
19//! - [`Stream`], which implements [`futures::io::AsyncRead`] and [`futures::io::AsyncWrite`], and
20//! - [`Control`], to asynchronously control the [`Connection`].
21//!
22//! [1]: https://github.com/hashicorp/yamux/blob/master/spec.md
23
24#![forbid(unsafe_code)]
25
26mod chunks;
27mod control;
28mod error;
29mod frame;
30
31pub(crate) mod connection;
32mod tagged_stream;
33
34pub use crate::yamux::{
35 connection::{Connection, Mode, Packet, Stream},
36 control::{Control, ControlledConnection},
37 error::ConnectionError,
38 frame::{
39 header::{HeaderDecodeError, StreamId},
40 FrameDecodeError,
41 },
42};
43
44pub const DEFAULT_CREDIT: u32 = 256 * 1024; // as per yamux specification
45
46pub type Result<T> = std::result::Result<T, ConnectionError>;
47
48/// The maximum number of streams we will open without an acknowledgement from the other peer.
49///
50/// This enables a very basic form of backpressure on the creation of streams.
51const MAX_ACK_BACKLOG: usize = 256;
52
53/// Default maximum number of bytes a Yamux data frame might carry as its
54/// payload when being send. Larger Payloads will be split.
55///
56/// The data frame payload size is not restricted by the yamux specification.
57/// Still, this implementation restricts the size to:
58///
59/// 1. Reduce delays sending time-sensitive frames, e.g. window updates.
60/// 2. Minimize head-of-line blocking across streams.
61/// 3. Enable better interleaving of send and receive operations, as each is carried out atomically
62/// instead of concurrently with its respective counterpart.
63///
64/// For details on why this concrete value was chosen, see
65/// <https://github.com/paritytech/yamux/issues/100>.
66const DEFAULT_SPLIT_SEND_SIZE: usize = 16 * 1024;
67
68/// Specifies when window update frames are sent.
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub enum WindowUpdateMode {
71 /// Send window updates as soon as a [`Stream`]'s receive window drops to 0.
72 ///
73 /// This ensures that the sender can resume sending more data as soon as possible
74 /// but a slow reader on the receiving side may be overwhelmed, i.e. it accumulates
75 /// data in its buffer which may reach its limit (see `set_max_buffer_size`).
76 /// In this mode, window updates merely prevent head of line blocking but do not
77 /// effectively exercise back pressure on senders.
78 OnReceive,
79
80 /// Send window updates only when data is read on the receiving end.
81 ///
82 /// This ensures that senders do not overwhelm receivers and keeps buffer usage
83 /// low. However, depending on the protocol, there is a risk of deadlock, namely
84 /// if both endpoints want to send data larger than the receivers window and they
85 /// do not read before finishing their writes. Use this mode only if you are sure
86 /// that this will never happen, i.e. if
87 ///
88 /// - Endpoints *A* and *B* never write at the same time, *or*
89 /// - Endpoints *A* and *B* write at most *n* frames concurrently such that the sum of the
90 /// frame lengths is less or equal to the available credit of *A* and *B* respectively.
91 OnRead,
92}
93
94/// Yamux configuration.
95///
96/// The default configuration values are as follows:
97///
98/// - receive window = 256 KiB
99/// - max. buffer size (per stream) = 1 MiB
100/// - max. number of streams = 8192
101/// - window update mode = on read
102/// - read after close = true
103/// - split send size = 16 KiB
104#[derive(Debug, Clone)]
105pub struct Config {
106 receive_window: u32,
107 max_buffer_size: usize,
108 max_num_streams: usize,
109 window_update_mode: WindowUpdateMode,
110 read_after_close: bool,
111 split_send_size: usize,
112}
113
114impl Default for Config {
115 fn default() -> Self {
116 Config {
117 receive_window: DEFAULT_CREDIT,
118 max_buffer_size: 1024 * 1024,
119 max_num_streams: 8192,
120 window_update_mode: WindowUpdateMode::OnRead,
121 read_after_close: true,
122 split_send_size: DEFAULT_SPLIT_SEND_SIZE,
123 }
124 }
125}
126
127impl Config {
128 /// Set the receive window per stream (must be >= 256 KiB).
129 ///
130 /// # Panics
131 ///
132 /// If the given receive window is < 256 KiB.
133 pub fn set_receive_window(&mut self, n: u32) -> &mut Self {
134 assert!(n >= DEFAULT_CREDIT);
135 self.receive_window = n;
136 self
137 }
138
139 /// Set the max. buffer size per stream.
140 pub fn set_max_buffer_size(&mut self, n: usize) -> &mut Self {
141 self.max_buffer_size = n;
142 self
143 }
144
145 /// Set the max. number of streams.
146 pub fn set_max_num_streams(&mut self, n: usize) -> &mut Self {
147 self.max_num_streams = n;
148 self
149 }
150
151 /// Set the window update mode to use.
152 pub fn set_window_update_mode(&mut self, m: WindowUpdateMode) -> &mut Self {
153 self.window_update_mode = m;
154 self
155 }
156
157 /// Allow or disallow streams to read from buffered data after
158 /// the connection has been closed.
159 pub fn set_read_after_close(&mut self, b: bool) -> &mut Self {
160 self.read_after_close = b;
161 self
162 }
163
164 /// Set the max. payload size used when sending data frames. Payloads larger
165 /// than the configured max. will be split.
166 pub fn set_split_send_size(&mut self, n: usize) -> &mut Self {
167 self.split_send_size = n;
168 self
169 }
170}
171
172// Check that we can safely cast a `usize` to a `u64`.
173static_assertions::const_assert! {
174 std::mem::size_of::<usize>() <= std::mem::size_of::<u64>()
175}
176
177// Check that we can safely cast a `u32` to a `usize`.
178static_assertions::const_assert! {
179 std::mem::size_of::<u32>() <= std::mem::size_of::<usize>()
180}