Expand Shadowsocks runtime compatibility

This commit is contained in:
JettChenT 2026-06-05 10:33:41 -07:00
parent d1638726ca
commit 4d1f589280
167 changed files with 57173 additions and 1640 deletions

523
third_party/tokio-rustls-fork-shadow-tls/Cargo.lock generated vendored Normal file
View file

@ -0,0 +1,523 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mio"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.45.0",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys 0.45.0",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "proc-macro2"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rustls-fork-shadow-tls"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "227b89ca93267855a168e5293f66c74d43afabcdf2239947d3b5904928388161"
dependencies = [
"log",
"ring",
"sct",
"webpki",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "socket2"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.42.0",
]
[[package]]
name = "tokio-macros"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-rustls-fork-shadow-tls"
version = "0.0.8"
dependencies = [
"bytes",
"rustls-fork-shadow-tls",
"thiserror",
"tokio",
"webpki-roots",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "web-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
dependencies = [
"webpki",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]]
name = "windows_i686_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"

View file

@ -0,0 +1,57 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "tokio-rustls-fork-shadow-tls"
version = "0.0.8"
authors = ["ChiHai <ihciah@gmail.com>"]
description = "Asynchronous TLS streams wrapper for Tokio based on Rustls."
homepage = "https://github.com/compdzwio/tokio-tls"
readme = "README.md"
categories = [
"asynchronous",
"cryptography",
"network-programming",
]
license = "MIT/Apache-2.0"
repository = "https://github.com/compdzwio/tokio-tls"
resolver = "1"
[dependencies.bytes]
version = "1"
[dependencies.rustls-fork-shadow-tls]
version = "0.20.8"
default-features = false
[dependencies.thiserror]
version = "1"
[dependencies.tokio]
version = "1.25.0"
features = ["full"]
[dev-dependencies.tokio]
version = "1.25.0"
[dev-dependencies.webpki-roots]
version = "0.22"
[features]
dangerous_configuration = ["rustls-fork-shadow-tls/dangerous_configuration"]
default = [
"logging",
"tls12",
]
logging = ["rustls-fork-shadow-tls/logging"]
tls12 = ["rustls-fork-shadow-tls/tls12"]
unsafe_io = []

View file

@ -0,0 +1,30 @@
[package]
authors = ["ChiHai <ihciah@gmail.com>"]
categories = ["asynchronous", "cryptography", "network-programming"]
description = "Asynchronous TLS streams wrapper for Tokio based on Rustls."
edition = "2021"
homepage = "https://github.com/compdzwio/tokio-tls"
license = "MIT/Apache-2.0"
name = "tokio-rustls-fork-shadow-tls"
readme = "README.md"
repository = "https://github.com/compdzwio/tokio-tls"
version = "0.0.8"
[dependencies]
bytes = {version = "1"}
tokio = {version = "1.25.0", features = ["full"]}
rustls-fork-shadow-tls = {version = "0.20.8", default-features = false}
thiserror = {version = "1"}
[features]
dangerous_configuration = ["rustls-fork-shadow-tls/dangerous_configuration"]
default = ["logging", "tls12"]
logging = ["rustls-fork-shadow-tls/logging"]
tls12 = ["rustls-fork-shadow-tls/tls12"]
# Once unsafe_io is enabled, you may not drop the future before it returns ready.
# It saves one buffer copy than disabled.
unsafe_io = []
[dev-dependencies]
tokio = {version = "1.25.0"}
webpki-roots = "0.22"

View file

@ -0,0 +1 @@
# Tokio-rustls

View file

@ -0,0 +1,42 @@
use std::sync::Arc;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
};
use tokio_rustls_fork_shadow_tls::TlsConnector;
use rustls_fork_shadow_tls::{OwnedTrustAnchor, RootCertStore};
#[tokio::main]
async fn main() {
let mut root_store = RootCertStore::empty();
root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
}));
let config = rustls_fork_shadow_tls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth();
let connector = TlsConnector::from(Arc::new(config));
let stream = TcpStream::connect("rsproxy.cn:443").await.unwrap();
println!("rsproxy.cn:443 connected");
let domain = rustls_fork_shadow_tls::ServerName::try_from("rsproxy.cn").unwrap();
let mut stream = connector.connect(domain, stream).await.unwrap();
println!("handshake success");
let content = b"GET / HTTP/1.0\r\nHost: rsproxy.cn\r\n\r\n";
stream.write_all(content).await?;
println!("http request sent");
let buf = vec![0_u8; 64];
let n = stream.read(buf).await?;
let resp = String::from_utf8(buf).unwrap();
println!("http response recv: \n\n{resp}");
}

View file

@ -0,0 +1,86 @@
use std::sync::Arc;
use tokio::io::{AsyncRead, AsyncWrite};
use rustls_fork_shadow_tls::{ClientConfig, ClientConnection, ClientSessionIdGenerators};
use crate::{
split::{ReadHalf, WriteHalf},
stream::Stream,
TlsError,
};
/// A wrapper around an underlying raw stream which implements the TLS protocol.
pub type TlsStream<IO> = Stream<IO, ClientConnection>;
/// TlsStream for read only.
pub type TlsStreamReadHalf<IO> = ReadHalf<IO, ClientConnection>;
/// TlsStream for write only.
pub type TlsStreamWriteHalf<IO> = WriteHalf<IO, ClientConnection>;
/// A wrapper around a `rustls::ClientConfig`, providing an async `connect` method.
#[derive(Clone)]
pub struct TlsConnector {
inner: Arc<ClientConfig>,
}
impl From<Arc<ClientConfig>> for TlsConnector {
fn from(inner: Arc<ClientConfig>) -> TlsConnector {
TlsConnector { inner }
}
}
impl From<ClientConfig> for TlsConnector {
fn from(inner: ClientConfig) -> TlsConnector {
TlsConnector {
inner: Arc::new(inner),
}
}
}
impl TlsConnector {
pub async fn connect<IO>(
&self,
domain: rustls_fork_shadow_tls::ServerName,
stream: IO,
) -> Result<TlsStream<IO>, TlsError>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
let session = ClientConnection::new(self.inner.clone(), domain)?;
let mut stream = Stream::new(stream, session);
stream.handshake().await?;
Ok(stream)
}
pub async fn connect_with_session_id_generator<IO, F>(
&self,
domain: rustls_fork_shadow_tls::ServerName,
stream: IO,
generator: F,
) -> Result<TlsStream<IO>, TlsError>
where
IO: AsyncRead + AsyncWrite + Unpin,
F: Fn(&[u8]) -> [u8; 32] + Send + Sync + 'static,
{
let session =
ClientConnection::new_with_session_id_generator(self.inner.clone(), domain, generator)?;
let mut stream = Stream::new(stream, session);
stream.handshake().await?;
Ok(stream)
}
pub async fn connect_with_session_id_generators<IO>(
&self,
domain: rustls_fork_shadow_tls::ServerName,
stream: IO,
generators: ClientSessionIdGenerators,
) -> Result<TlsStream<IO>, TlsError>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
let session =
ClientConnection::new_with_session_id_generators(self.inner.clone(), domain, generators)?;
let mut stream = Stream::new(stream, session);
stream.handshake().await?;
Ok(stream)
}
}

View file

@ -0,0 +1,20 @@
use std::io;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum TlsError {
#[error("io error")]
Io(#[from] std::io::Error),
#[error("rustls error")]
Rustls(#[from] rustls_fork_shadow_tls::Error),
}
impl From<TlsError> for io::Error {
fn from(e: TlsError) -> Self {
match e {
TlsError::Io(e) => e,
TlsError::Rustls(e) => io::Error::new(io::ErrorKind::Other, e),
}
}
}

View file

@ -0,0 +1,21 @@
#![allow(stable_features)]
mod client;
mod error;
#[cfg(not(feature = "unsafe_io"))]
mod safe_io;
mod server;
mod split;
mod stream;
#[cfg(feature = "unsafe_io")]
mod unsafe_io;
pub use client::{
TlsConnector, TlsStream as ClientTlsStream, TlsStreamReadHalf as ClientTlsStreamReadHalf,
TlsStreamWriteHalf as ClientTlsStreamWriteHalf,
};
pub use error::TlsError;
pub use server::{
TlsAcceptor, TlsStream as ServerTlsStream, TlsStreamReadHalf as ServerTlsStreamReadHalf,
TlsStreamWriteHalf as ServerTlsStreamWriteHalf,
};

View file

@ -0,0 +1,227 @@
use std::{
fmt::Debug, hint::unreachable_unchecked, io
};
use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}
};
const BUFFER_SIZE: usize = 16 * 1024;
struct Buffer {
read: usize,
write: usize,
buf: Box<[u8]>,
}
impl Buffer {
fn new() -> Self {
Self {
read: 0,
write: 0,
buf: vec![0; BUFFER_SIZE].into_boxed_slice(),
}
}
fn len(&self) -> usize {
self.write - self.read
}
fn is_empty(&self) -> bool {
self.len() == 0
}
fn available(&self) -> usize {
self.buf.len() - self.write
}
fn is_full(&self) -> bool {
self.available() == 0
}
fn advance(&mut self, n: usize) {
assert!(self.write - self.read >= n);
self.read += n;
if self.read == self.write {
self.read = 0;
self.write = 0;
}
}
}
pub(crate) struct SafeRead {
// the option is only meant for temporary take, it always should be some
buffer: Option<Buffer>,
status: ReadStatus,
}
impl Debug for SafeRead {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SafeRead")
.field("status", &self.status)
.finish()
}
}
#[derive(Debug)]
enum ReadStatus {
Eof,
Err(io::Error),
Ok,
}
impl Default for SafeRead {
fn default() -> Self {
Self {
buffer: Some(Buffer::new()),
status: ReadStatus::Ok,
}
}
}
impl SafeRead {
pub(crate) async fn do_io<IO: AsyncRead + Unpin>(&mut self, mut io: IO) -> io::Result<usize> {
// if there are some data inside the buffer, just return.
let buffer = self.buffer.as_ref().expect("buffer ref expected");
if !buffer.is_empty() {
return Ok(buffer.len());
}
// read from raw io
let buffer = self.buffer.as_mut().expect("buffer ownership expected");
let buf = &mut buffer.buf.as_mut()[buffer.write..];
let result = io.read(buf).await;
match result {
Ok(0) => {
self.status = ReadStatus::Eof;
result
}
Ok(n) => {
buffer.write += n;
self.status = ReadStatus::Ok;
result
}
Err(e) => {
let rerr = e.kind().into();
self.status = ReadStatus::Err(e);
Err(rerr)
}
}
}
}
impl io::Read for SafeRead {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// if buffer is empty, return WoundBlock.
let buffer = self.buffer.as_mut().expect("buffer mut expected");
if buffer.is_empty() {
if !matches!(self.status, ReadStatus::Ok) {
match std::mem::replace(&mut self.status, ReadStatus::Ok) {
ReadStatus::Eof => return Ok(0),
ReadStatus::Err(e) => return Err(e),
ReadStatus::Ok => unsafe { unreachable_unchecked() },
}
}
return Err(io::ErrorKind::WouldBlock.into());
}
// now buffer is not empty. copy it.
let to_copy = buffer.len().min(buf.len());
unsafe { std::ptr::copy_nonoverlapping(buffer.buf.as_ptr().add(buffer.read), buf.as_mut_ptr(), to_copy) };
buffer.advance(to_copy);
Ok(to_copy)
}
}
pub(crate) struct SafeWrite {
// the option is only meant for temporary take, it always should be some
buffer: Option<Buffer>,
status: WriteStatus,
}
impl Debug for SafeWrite {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SafeWrite")
.field("status", &self.status)
.finish()
}
}
#[derive(Debug)]
enum WriteStatus {
Err(io::Error),
Ok,
}
impl Default for SafeWrite {
fn default() -> Self {
Self {
buffer: Some(Buffer::new()),
status: WriteStatus::Ok,
}
}
}
impl SafeWrite {
pub(crate) async fn do_io<IO: AsyncWrite + Unpin>(&mut self, mut io: IO) -> io::Result<usize> {
// if the buffer is empty, just return.
let buffer = self.buffer.as_ref().expect("buffer ref expected");
if buffer.is_empty() {
return Ok(0);
}
// buffer is not empty now. write it.
let buffer = self.buffer.as_mut().expect("buffer ownership expected");
let buf = &buffer.buf.as_ref()[buffer.read..buffer.write];
let result = io.write_all(buf).await;
match result {
Ok(_) => {
let n = buffer.write - buffer.read;
buffer.advance(n);
Ok(n)
}
Err(e) => {
let rerr = e.kind().into();
self.status = WriteStatus::Err(e);
Err(rerr)
}
}
}
}
impl io::Write for SafeWrite {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
// if there is too much data inside the buffer, return WoundBlock
let buffer = self.buffer.as_mut().expect("buffer mut expected");
if !matches!(self.status, WriteStatus::Ok) {
match std::mem::replace(&mut self.status, WriteStatus::Ok) {
WriteStatus::Err(e) => return Err(e),
WriteStatus::Ok => unsafe { unreachable_unchecked() },
}
}
if buffer.is_full() {
return Err(io::ErrorKind::WouldBlock.into());
}
// there is space inside the buffer, copy to it.
let to_copy = buf.len().min(buffer.available());
unsafe { std::ptr::copy_nonoverlapping(buf.as_ptr(), buffer.buf.as_mut_ptr().add(buffer.write), to_copy); }
buffer.write += to_copy;
Ok(to_copy)
}
fn flush(&mut self) -> io::Result<()> {
let buffer = self.buffer.as_mut().expect("buffer mut expected");
if !matches!(self.status, WriteStatus::Ok) {
match std::mem::replace(&mut self.status, WriteStatus::Ok) {
WriteStatus::Err(e) => return Err(e),
WriteStatus::Ok => unsafe { unreachable_unchecked() },
}
}
if !buffer.is_empty() {
return Err(io::ErrorKind::WouldBlock.into());
}
Ok(())
}
}

View file

@ -0,0 +1,49 @@
use std::sync::Arc;
use tokio::io::{AsyncRead, AsyncWrite};
use rustls_fork_shadow_tls::{ServerConfig, ServerConnection};
use crate::{
split::{ReadHalf, WriteHalf},
stream::Stream,
TlsError,
};
/// A wrapper around an underlying raw stream which implements the TLS protocol.
pub type TlsStream<IO> = Stream<IO, ServerConnection>;
/// TlsStream for read only.
pub type TlsStreamReadHalf<IO> = ReadHalf<IO, ServerConnection>;
/// TlsStream for write only.
pub type TlsStreamWriteHalf<IO> = WriteHalf<IO, ServerConnection>;
/// A wrapper around a `rustls::ServerConfig`, providing an async `accept` method.
#[derive(Clone)]
pub struct TlsAcceptor {
inner: Arc<ServerConfig>,
}
impl From<Arc<ServerConfig>> for TlsAcceptor {
fn from(inner: Arc<ServerConfig>) -> TlsAcceptor {
TlsAcceptor { inner }
}
}
impl From<ServerConfig> for TlsAcceptor {
fn from(inner: ServerConfig) -> TlsAcceptor {
TlsAcceptor {
inner: Arc::new(inner),
}
}
}
impl TlsAcceptor {
pub async fn accept<IO>(&self, stream: IO) -> Result<TlsStream<IO>, TlsError>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
let session = ServerConnection::new(self.inner.clone())?;
let mut stream = Stream::new(stream, session);
stream.handshake().await?;
Ok(stream)
}
}

View file

@ -0,0 +1,139 @@
//! Split implement for TlsStream.
//! Note: Here we depends on the behavior of monoio TcpStream.
//! Though it is not a good assumption, it can really make it
//! more efficient with less code. The read and write will not
//! interfere each other.
use std::{
cell::UnsafeCell,
future::Future,
io::IoSlice,
ops::{Deref, DerefMut},
pin::Pin,
rc::Rc,
task::{Context, Poll},
};
use tokio::{
pin,
io::{AsyncRead, AsyncWrite, ReadBuf}
};
use rustls_fork_shadow_tls::{ConnectionCommon, SideData};
use crate::stream::Stream;
#[derive(Debug)]
pub struct ReadHalf<IO, C> {
pub(crate) inner: Rc<UnsafeCell<Stream<IO, C>>>,
}
#[derive(Debug)]
pub struct WriteHalf<IO, C> {
pub(crate) inner: Rc<UnsafeCell<Stream<IO, C>>>,
}
impl<IO: AsyncRead + AsyncWrite + Unpin, C, SD: SideData + 'static> AsyncRead
for ReadHalf<IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>>,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>
) -> Poll<std::io::Result<()>> {
let inner = unsafe { &mut *self.inner.get() };
let ex = inner.read_inner(buf, true);
pin!(ex);
ex.poll(cx)
}
}
impl<IO, C> ReadHalf<IO, C> {
pub fn reunite(self, other: WriteHalf<IO, C>) -> Result<Stream<IO, C>, ReuniteError<IO, C>> {
reunite(self, other)
}
}
impl<IO: AsyncRead + AsyncWrite + Unpin, C: Unpin, SD: SideData + 'static> AsyncWrite
for WriteHalf<IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>>,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8]
) -> Poll<std::io::Result<usize>> {
let inner = unsafe { &mut *self.inner.get() };
Pin::new(inner).poll_write(cx, buf)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>]
) -> Poll<std::io::Result<usize>> {
let inner = unsafe { &mut *self.inner.get() };
Pin::new(inner).poll_write_vectored(cx, bufs)
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<std::io::Result<()>> {
let inner = unsafe { &mut *self.inner.get() };
Pin::new(inner).poll_flush(cx)
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<std::io::Result<()>> {
let inner = unsafe { &mut *self.inner.get() };
Pin::new(inner).poll_shutdown(cx)
}
fn is_write_vectored(&self) -> bool {
let inner = unsafe { &mut *self.inner.get() };
Pin::new(inner).is_write_vectored()
}
}
impl<IO, C> WriteHalf<IO, C> {
pub fn reunite(self, other: ReadHalf<IO, C>) -> Result<Stream<IO, C>, ReuniteError<IO, C>> {
reunite(other, self)
}
}
pub(crate) fn reunite<IO, C>(
read: ReadHalf<IO, C>,
write: WriteHalf<IO, C>,
) -> Result<Stream<IO, C>, ReuniteError<IO, C>> {
if Rc::ptr_eq(&read.inner, &write.inner) {
drop(write);
// This unwrap cannot fail as the api does not allow creating more than two Rcs,
// and we just dropped the other half.
Ok(Rc::try_unwrap(read.inner)
.expect("TlsStream: try_unwrap failed in reunite")
.into_inner())
} else {
Err(ReuniteError(read, write))
}
}
/// Error indicating that two halves were not from the same socket, and thus could
/// not be reunited.
#[derive(Debug)]
pub struct ReuniteError<IO, C>(pub ReadHalf<IO, C>, pub WriteHalf<IO, C>);
impl<IO: std::fmt::Debug, C: std::fmt::Debug> std::fmt::Display for ReuniteError<IO, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"tried to reunite halves that are not from the same socket"
)
}
}
impl<IO: std::fmt::Debug, C: std::fmt::Debug> std::error::Error for ReuniteError<IO, C> {}

View file

@ -0,0 +1,297 @@
use std::{
cell::UnsafeCell,
future::Future,
io::{IoSlice, Read, self, Write},
ops::{Deref, DerefMut},
pin::Pin,
rc::Rc,
task::{Context, Poll},
};
use tokio::{
pin,
io::{AsyncRead, AsyncWrite, ReadBuf}
};
use rustls_fork_shadow_tls::{ConnectionCommon, SideData};
use crate::split::{ReadHalf, WriteHalf};
#[derive(Debug)]
pub struct Stream<IO, C> {
pub(crate) io: IO,
pub(crate) session: C,
#[cfg(not(feature = "unsafe_io"))]
r_buffer: crate::safe_io::SafeRead,
#[cfg(not(feature = "unsafe_io"))]
w_buffer: crate::safe_io::SafeWrite,
#[cfg(feature = "unsafe_io")]
r_buffer: crate::unsafe_io::UnsafeRead,
#[cfg(feature = "unsafe_io")]
w_buffer: crate::unsafe_io::UnsafeWrite,
}
impl<IO, C> Stream<IO, C> {
pub fn new(io: IO, session: C) -> Self {
Self {
io,
session,
r_buffer: Default::default(),
w_buffer: Default::default(),
}
}
pub fn split(self) -> (ReadHalf<IO, C>, WriteHalf<IO, C>) {
let shared = Rc::new(UnsafeCell::new(self));
(
ReadHalf {
inner: shared.clone(),
},
WriteHalf { inner: shared },
)
}
pub fn into_parts(self) -> (IO, C) {
(self.io, self.session)
}
}
impl<IO: AsyncRead + AsyncWrite + Unpin, C, SD: SideData> Stream<IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>>,
{
pub(crate) async fn read_io(&mut self, splitted: bool) -> io::Result<usize> {
let n = loop {
match self.session.read_tls(&mut self.r_buffer) {
Ok(n) => {
break n;
}
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => (),
Err(err) => return Err(err),
}
#[allow(unused_unsafe)]
unsafe {
self.r_buffer.do_io(&mut self.io).await?
};
};
let state = match self.session.process_new_packets() {
Ok(state) => state,
Err(err) => {
// When to write_io? If we do this in read call, the UnsafeWrite may crash
// when we impl split in an UnsafeCell way.
// Here we choose not to do write when read.
// User should manually shutdown it on error.
if !splitted {
let _ = self.write_io().await;
}
return Err(io::Error::new(io::ErrorKind::InvalidData, err));
}
};
if state.peer_has_closed() && self.session.is_handshaking() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"tls handshake alert",
));
}
Ok(n)
}
pub(crate) async fn write_io(&mut self) -> io::Result<usize> {
let n = loop {
match self.session.write_tls(&mut self.w_buffer) {
Ok(n) => {
break n;
}
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => (),
Err(err) => return Err(err),
}
#[allow(unused_unsafe)]
unsafe {
self.w_buffer.do_io(&mut self.io).await?
};
};
// Flush buffered data, only needed for safe_io.
#[cfg(not(feature = "unsafe_io"))]
self.w_buffer.do_io(&mut self.io).await?;
Ok(n)
}
pub(crate) async fn handshake(&mut self) -> io::Result<(usize, usize)> {
let mut wrlen = 0;
let mut rdlen = 0;
let mut eof = false;
loop {
while self.session.wants_write() && self.session.is_handshaking() {
wrlen += self.write_io().await?;
}
while !eof && self.session.wants_read() && self.session.is_handshaking() {
let n = self.read_io(false).await?;
rdlen += n;
if n == 0 {
eof = true;
}
}
match (eof, self.session.is_handshaking()) {
(true, true) => {
let err = io::Error::new(io::ErrorKind::UnexpectedEof, "tls handshake eof");
return Err(err);
}
(false, true) => (),
(_, false) => {
break;
}
}
}
// flush buffer
while self.session.wants_write() {
wrlen += self.write_io().await?;
}
Ok((rdlen, wrlen))
}
pub(crate) async fn read_inner(
&mut self,
buf: &mut ReadBuf<'_>,
splitted: bool,
) -> std::io::Result<()> {
if buf.remaining() == 0 {
return Ok(());
}
let slice = buf.initialize_unfilled();
loop {
// read from rustls to buffer
match self.session.reader().read(slice) {
Ok(n) => {
buf.advance(n);
return Ok(());
}
// we need more data, read something.
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => (),
Err(e) => {
return Err(e);
}
}
// now we need data, read something into rustls
match self.read_io(splitted).await {
Ok(0) => {
return
Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"tls raw stream eof",
),
);
}
Ok(_) => (),
Err(e) => {
return Err(e);
}
}
}
}
}
impl<IO: AsyncRead + AsyncWrite + Unpin, C, SD: SideData + 'static> AsyncRead for Stream<IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>> + Unpin,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>
) -> Poll<std::io::Result<()>> {
let ex = self.read_inner(buf, false);
pin!(ex);
ex.poll(cx)
}
}
impl<IO: AsyncRead + AsyncWrite + Unpin, C, SD: SideData + 'static> AsyncWrite for Stream<IO, C>
where
C: DerefMut + Deref<Target = ConnectionCommon<SD>> + Unpin,
{
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8]
) -> Poll<std::io::Result<usize>> {
// write buf to rustls
let n = match self.session.writer().write(buf) {
Ok(n) => n,
Err(e) => return Poll::Ready(Err(e)),
};
// write from rustls to connection
while self.session.wants_write() {
let ex = self.write_io();
pin!(ex);
match ex.poll(cx) {
Poll::Ready(Ok(0)) => {
break;
}
Poll::Ready(Ok(_)) => (),
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
}
}
Poll::Ready(Ok(n))
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>]
) -> Poll<std::io::Result<usize>> {
let buf = bufs
.iter()
.find(|b| !b.is_empty())
.map_or(&[][..], |b| &**b);
self.poll_write(cx, buf)
}
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<std::io::Result<()>> {
self.session.writer().flush()?;
while self.session.wants_write() {
let ex = self.write_io();
pin!(ex);
match ex.poll(cx) {
Poll::Ready(Ok(_)) => (),
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
}
}
Pin::new(&mut self.io).poll_flush(cx)
}
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<std::io::Result<()>> {
self.session.send_close_notify();
while self.session.wants_write() {
let ex = self.write_io();
pin!(ex);
match ex.poll(cx) {
Poll::Ready(Ok(_)) => (),
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
}
}
Pin::new(&mut self.io).poll_shutdown(cx)
}
fn is_write_vectored(&self) -> bool {
Pin::new(&self.io).is_write_vectored()
}
}

View file

@ -0,0 +1,124 @@
use std::{
io,
slice::{from_raw_parts, from_raw_parts_mut}
};
use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}
};
/// Used by both UnsafeRead and UnsafeWrite.
#[derive(Debug)]
enum Status {
/// We haven't do real io, and maybe the dest is recorded.
WaitFill(Option<(*const u8, usize)>),
/// We have already do real io. The length maybe zero or non-zero.
Filled(usize),
}
impl Default for Status {
fn default() -> Self {
Status::WaitFill(None)
}
}
/// UnsafeRead is a wrapper of some meta data.
/// It implements std::io::Read trait. But it do real io in an async way.
/// On the first read, it may returns WouldBlock error, which means the
/// `fullfill` should be called to do real io.
/// The data is read directly into the dest that last std read passes.
/// Note that this action is an unsafe hack to avoid data copy.
/// You can only use this wrapper when you make sure the read dest is always
/// a valid buffer.
#[derive(Default, Debug)]
pub(crate) struct UnsafeRead {
status: Status,
}
impl UnsafeRead {
/// `do_io` must be called after calling to io::Read::read.
pub(crate) async unsafe fn do_io<IO: AsyncRead + Unpin>(
&mut self,
mut io: IO,
) -> io::Result<usize> {
match self.status {
Status::WaitFill(Some((ptr, len))) => {
let buf = unsafe { from_raw_parts_mut(ptr as *mut u8, len) };
let n = io.read(buf).await?;
self.status = Status::Filled(n);
Ok(n)
}
Status::Filled(len) => Ok(len),
Status::WaitFill(None) => Err(io::ErrorKind::WouldBlock.into()),
}
}
}
impl io::Read for UnsafeRead {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.status {
Status::WaitFill(_) => {
let ptr = buf.as_ptr();
let len = buf.len();
self.status = Status::WaitFill(Some((ptr, len)));
Err(io::ErrorKind::WouldBlock.into())
}
Status::Filled(len) => {
if len != 0 {
// reset only when not eof
self.status = Status::WaitFill(None);
}
Ok(len)
}
}
}
}
/// UnsafeWrite behaves like `UnsafeRead`.
#[derive(Default, Debug)]
pub(crate) struct UnsafeWrite {
status: Status,
}
impl UnsafeWrite {
/// `do_io` must be called after calling to io::Write::write.
pub(crate) async unsafe fn do_io<IO: AsyncWrite + Unpin>(
&mut self,
mut io: IO,
) -> io::Result<usize> {
match self.status {
Status::WaitFill(Some((ptr, len))) => {
let buf = unsafe { from_raw_parts(ptr, len) };
let n = io.write(buf).await?;
self.status = Status::Filled(n);
Ok(n)
}
Status::Filled(len) => Ok(len),
Status::WaitFill(None) => Err(io::ErrorKind::WouldBlock.into()),
}
}
}
impl io::Write for UnsafeWrite {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self.status {
Status::WaitFill(_) => {
let ptr = buf.as_ptr();
let len = buf.len();
self.status = Status::WaitFill(Some((ptr, len)));
Err(io::ErrorKind::WouldBlock.into())
}
Status::Filled(len) => {
if len != 0 {
// reset only when not eof
self.status = Status::WaitFill(None);
}
Ok(len)
}
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}