From 7b252edb5d054dec54b904a7efc92bd67108084d Mon Sep 17 00:00:00 2001
From: Christian Visintin
Date: Fri, 24 Feb 2023 16:04:46 +0100
Subject: [PATCH] 33 bug features are not additive (#34)
* sync with multi type
* async with additive features
* My boss forced me to build this feature... Pure shit.
* release notes
---
.github/workflows/cargo.yml | 24 +---
CHANGELOG.md | 15 ++
README.md | 47 +++---
suppaftp-cli/Cargo.toml | 4 +-
suppaftp-cli/src/actions.rs | 4 +-
suppaftp-cli/src/main.rs | 2 +-
suppaftp/Cargo.toml | 5 +-
suppaftp/src/async_ftp/data_stream.rs | 39 ++---
suppaftp/src/async_ftp/mod.rs | 175 +++++++++++++++--------
suppaftp/src/async_ftp/tls.rs | 84 ++++++++++-
suppaftp/src/async_ftp/tls/native_tls.rs | 101 +++++++++++--
suppaftp/src/async_ftp/tls/rustls.rs | 98 +++++++++++--
suppaftp/src/lib.rs | 83 ++++++-----
suppaftp/src/sync_ftp/data_stream.rs | 38 +++--
suppaftp/src/sync_ftp/mod.rs | 116 +++++++++------
suppaftp/src/sync_ftp/tls.rs | 48 ++++++-
suppaftp/src/sync_ftp/tls/native_tls.rs | 50 ++++---
suppaftp/src/sync_ftp/tls/rustls.rs | 28 ++--
18 files changed, 681 insertions(+), 280 deletions(-)
diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml
index bedde89..ca39ff5 100644
--- a/.github/workflows/cargo.yml
+++ b/.github/workflows/cargo.yml
@@ -42,31 +42,19 @@ jobs:
with:
command: build
args: --features async-native-tls,deprecated --package suppaftp
- - name: Run tests (native-tls)
+ - name: Build all features
uses: actions-rs/cargo@v1
with:
- command: test
- args: --lib --package suppaftp --no-default-features --features native-tls,async-native-tls,with-containers --no-fail-fast
- env:
- RUST_LOG: trace
- - name: Run tests (rustls)
+ command: build
+ args: --features deprecated,native-tls,rustls,async-native-tls,async-rustls --package suppaftp
+ - name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
- args: --lib --package suppaftp --no-default-features --features rustls,async-rustls,with-containers --no-fail-fast
+ args: --lib --package suppaftp --no-default-features --features rustls,native-tls,async-native-tls,async-rustls,with-containers --no-fail-fast
env:
RUST_LOG: trace
- name: Format
run: cargo fmt --all -- --check
- name: Clippy
- run: cargo clippy --package suppaftp --features deprecated -- -Dwarnings
- - name: Clippy (async)
- run: cargo clippy --package suppaftp --features async,deprecated -- -Dwarnings
- - name: Clippy (native-tls)
- run: cargo clippy --package suppaftp --features native-tls,deprecated -- -Dwarnings
- - name: Clippy (async-native-tls)
- run: cargo clippy --package suppaftp --features async-native-tls,deprecated -- -Dwarnings
- - name: Clippy (rustls)
- run: cargo clippy --package suppaftp --features rustls,deprecated -- -Dwarnings
- - name: Clippy (async-rustls)
- run: cargo clippy --package suppaftp --features async-rustls,deprecated -- -Dwarnings
+ run: cargo clippy --package suppaftp --features deprecated,native-tls,rustls,async-native-tls,async-rustls -- -Dwarnings
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a4bbcad..14eadc0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
# Changelog
- [Changelog](#changelog)
+ - [5.0.0](#500)
- [4.7.0](#470)
- [4.6.1](#461)
- [4.6.0](#460)
@@ -21,6 +22,20 @@
---
+## 5.0.0
+
+Released on 24/02/2023
+
+- [Issue 33](https://github.com/veeso/suppaftp/issues/33) **‼️ BREAKING CHANGES ‼️**
+ - Features are now additive. This means that you can successfully build suppaftp with all the features enabled at the same time.
+ - Ftp stream has now been split into different types:
+ - `FtpStream`: sync no-tls stream
+ - `NativeTlsFtpStream`: ftp stream with TLS with native-tls
+ - `RustlsFtpStream`: ftp stream with TLS with rustls
+ - `AsyncFtpStream`: async no-tls stream
+ - `AsyncNativeTlsFtpStream`: async ftp stream with TLS with async-native-tls
+ - `AsyncRustlsFtpStream`: async ftp stream with TLS with async-rustls
+
## 4.7.0
Released on 01/02/2023
diff --git a/README.md b/README.md
index 6ee6d04..123c26c 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
Developed by veeso and Matt McCoy
-Current version: 4.7.0 (01/02/2023)
+Current version: 5.0.0 (24/02/2023)
💡 If you don't know what to choose, `native-tls` should be preferred for compatibility reasons.
+> 💡 If you don't know what to choose, `native-tls` should be preferred for compatibility reasons.
> ❗ If you want to link libssl statically, enable feature `native-tls-vendored`
#### Async support
@@ -139,11 +139,11 @@ suppaftp = { version = "^4.7.0", features = ["rustls"] }
If you want to enable **async** support, you must enable `async` feature in your cargo dependencies.
```toml
-suppaftp = { version = "^4.7.0", features = ["async"] }
+suppaftp = { version = "^5.0.0", features = ["async"] }
```
-> ⚠️ If you want to enable both **native-tls** and **async** you must use the **async-native-tls** feature ⚠️
-> ⚠️ If you want to enable both **rustls** and **async** you must use the **async-rustls** feature ⚠️
+> ⚠️ If you want to enable both **native-tls** and **async** you must use the **async-native-tls** feature ⚠️
+> ⚠️ If you want to enable both **rustls** and **async** you must use the **async-rustls** feature ⚠️
> ❗ If you want to link libssl statically, enable feature `async-native-tls-vendored`
#### Deprecated methods
@@ -194,18 +194,16 @@ fn main() {
#### Ftp with TLS (native-tls)
```rust
-use std::str;
-use std::io::Cursor;
-use suppaftp::{FtpStream};
-use suppaftp::native_tls::TlsConnector;
+use suppaftp::{NativeTlsFtpStream, NativeTlsConnector};
+use suppaftp::native_tls::{TlsConnector, TlsStream};
fn main() {
- // Create a connection to an FTP server and authenticate to it.
- let mut ftp_stream = FtpStream::connect("127.0.0.1:21")
- .into_secure(NativeTlsConnector::new().unwrap().into(), "domain-name")
- .unwrap();
- // Terminate the connection to the server.
- let _ = ftp_stream.quit();
+ let ftp_stream = NativeTlsFtpStream::connect("test.rebex.net:21").unwrap();
+ // Switch to the secure mode
+ let mut ftp_stream = ftp_stream.into_secure(NativeTlsConnector::from(TlsConnector::new().unwrap()), "test.rebex.net").unwrap();
+ ftp_stream.login("demo", "password").unwrap();
+ // Do other secret stuff
+ assert!(ftp_stream.quit().is_ok());
}
```
@@ -215,7 +213,7 @@ fn main() {
use std::str;
use std::io::Cursor;
use std::sync::Arc;
-use suppaftp::{FtpStream};
+use suppaftp::{RustlsFtpStream, RustlsConnector};
use suppaftp::rustls::ClientConfig;
fn main() {
@@ -233,9 +231,9 @@ fn main() {
.with_no_client_auth();
// Create a connection to an FTP server and authenticate to it.
let config = Arc::new(rustls_config());
- let mut ftp_stream = FtpStream::connect("test.rebex.net:21")
+ let mut ftp_stream = RustlsFtpStream::connect("test.rebex.net:21")
.unwrap()
- .into_secure(Arc::clone(&config).into(), "test.rebex.net")
+ .into_secure(RustlsConnector::from(Arc::clone(&config)), "test.rebex.net")
.unwrap();
// Terminate the connection to the server.
let _ = ftp_stream.quit();
@@ -245,14 +243,13 @@ fn main() {
#### Going Async
```rust
-use suppaftp::FtpStream;
+use suppaftp::{AsyncFtpStream, AsyncNativeTlsConnector};
use suppaftp::async_native_tls::{TlsConnector, TlsStream};
-let ftp_stream = FtpStream::connect("test.rebex.net:21").await.unwrap();
+let ftp_stream = AsyncFtpStream::connect("test.rebex.net:21").await.unwrap();
// Switch to the secure mode
-let mut ftp_stream = ftp_stream.into_secure(TlsConnector::new().into(), "test.rebex.net").await.unwrap();
+let mut ftp_stream = ftp_stream.into_secure(AsyncNativeTlsConnector::from(TlsConnector::new()), "test.rebex.net").await.unwrap();
ftp_stream.login("demo", "password").await.unwrap();
// Do other secret stuff
-// Do all public stuff
assert!(ftp_stream.quit().await.is_ok());
```
diff --git a/suppaftp-cli/Cargo.toml b/suppaftp-cli/Cargo.toml
index ee3b88b..b3787b9 100644
--- a/suppaftp-cli/Cargo.toml
+++ b/suppaftp-cli/Cargo.toml
@@ -10,7 +10,7 @@ license = "MIT"
name = "suppaftp-cli"
readme = "../README.md"
repository = "https://github.com/veeso/suppaftp"
-version = "4.7.0"
+version = "5.0.0"
[[bin]]
name = "suppaftp"
@@ -21,4 +21,4 @@ argh = "^0.1"
env_logger = "^0.10"
log = "^0.4"
rpassword = "^7.2"
-suppaftp = { path = "../suppaftp", version = "^4.7", features = [ "native-tls" ] }
+suppaftp = { path = "../suppaftp", version = "^5.0", features = [ "native-tls" ] }
diff --git a/suppaftp-cli/src/actions.rs b/suppaftp-cli/src/actions.rs
index 4d8c168..0a07bf9 100644
--- a/suppaftp-cli/src/actions.rs
+++ b/suppaftp-cli/src/actions.rs
@@ -5,7 +5,7 @@ use std::io;
use std::path::Path;
use suppaftp::native_tls::TlsConnector;
use suppaftp::types::FileType;
-use suppaftp::Mode;
+use suppaftp::{Mode, NativeTlsConnector};
pub fn quit(mut ftp: Option) {
if let Some(mut ftp) = ftp.take() {
@@ -60,7 +60,7 @@ pub fn connect(remote: &str, secure: bool) -> Option {
};
// Get address without port
let address: &str = remote.split(':').next().unwrap();
- stream = match stream.into_secure(ctx.into(), address) {
+ stream = match stream.into_secure(NativeTlsConnector::from(ctx), address) {
Ok(s) => s,
Err(err) => {
eprintln!("Failed to setup TLS stream: {err}");
diff --git a/suppaftp-cli/src/main.rs b/suppaftp-cli/src/main.rs
index a11281f..81f4181 100644
--- a/suppaftp-cli/src/main.rs
+++ b/suppaftp-cli/src/main.rs
@@ -17,7 +17,7 @@ use log::LevelFilter;
use std::io;
use std::io::Write;
use std::str::FromStr;
-use suppaftp::{FtpError, FtpStream};
+use suppaftp::{FtpError, NativeTlsFtpStream as FtpStream};
const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
const APP_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
diff --git a/suppaftp/Cargo.toml b/suppaftp/Cargo.toml
index a1c8fb3..cd58cd3 100644
--- a/suppaftp/Cargo.toml
+++ b/suppaftp/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "suppaftp"
-version = "4.7.0"
+version = "5.0.0"
authors = ["Christian Visintin ", "Matt McCoy "]
edition = "2021"
documentation = "https://docs.rs/suppaftp/"
@@ -24,6 +24,7 @@ thiserror = "^1"
# async
async-std = { version = "^1.10", optional = true }
async-native-tls-crate = { package = "async-native-tls", version = "^0.4", optional = true }
+async-trait = { version = "0.1.64", optional = true }
async-tls = { version = "^0.11", optional = true }
pin-project = { version = "^1", optional = true }
# secure
@@ -41,7 +42,7 @@ webpki-roots = "0.22.5"
[features]
default = [ ]
# Enable async support for suppaftp
-async = ["async-std", "pin-project"]
+async = ["async-std", "async-trait", "pin-project"]
async-default-tls = [ "async-native-tls" ]
async-native-tls = [ "async-native-tls-crate", "async-secure" ]
async-rustls = [ "async-tls", "async-secure" ]
diff --git a/suppaftp/src/async_ftp/data_stream.rs b/suppaftp/src/async_ftp/data_stream.rs
index c76f7bf..8ed63cc 100644
--- a/suppaftp/src/async_ftp/data_stream.rs
+++ b/suppaftp/src/async_ftp/data_stream.rs
@@ -2,27 +2,30 @@
//!
//! This module exposes the async data stream implementation where bytes must be written to/read from
-#[cfg(feature = "async-native-tls")]
-use async_native_tls::TlsStream;
#[cfg(any(feature = "async", feature = "async-secure"))]
use async_std::io::{Read, Result, Write};
#[cfg(any(feature = "async", feature = "async-secure"))]
use async_std::net::TcpStream;
-#[cfg(feature = "async-rustls")]
-use async_tls::client::TlsStream;
use pin_project::pin_project;
use std::pin::Pin;
+use super::AsyncTlsStream;
+
/// Data Stream used for communications. It can be both of type Tcp in case of plain communication or Ssl in case of FTPS
#[pin_project(project = DataStreamProj)]
-pub enum DataStream {
+pub enum DataStream
+where
+ T: AsyncTlsStream,
+{
Tcp(#[pin] TcpStream),
- #[cfg(feature = "async-secure")]
- Ssl(#[pin] Box>),
+ Ssl(#[pin] Box),
}
#[cfg(feature = "async-secure")]
-impl DataStream {
+impl DataStream
+where
+ T: AsyncTlsStream,
+{
/// Unwrap the stream into TcpStream. This method is only used in secure connection.
pub fn into_tcp_stream(self) -> TcpStream {
match self {
@@ -32,12 +35,14 @@ impl DataStream {
}
}
-impl DataStream {
+impl DataStream
+where
+ T: AsyncTlsStream,
+{
/// Returns a reference to the underlying TcpStream.
pub fn get_ref(&self) -> &TcpStream {
match self {
DataStream::Tcp(ref stream) => stream,
- #[cfg(feature = "async-secure")]
DataStream::Ssl(ref stream) => stream.get_ref(),
}
}
@@ -45,7 +50,10 @@ impl DataStream {
// -- async
-impl Read for DataStream {
+impl Read for DataStream
+where
+ T: AsyncTlsStream,
+{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
@@ -53,13 +61,15 @@ impl Read for DataStream {
) -> std::task::Poll> {
match self.project() {
DataStreamProj::Tcp(stream) => stream.poll_read(cx, buf),
- #[cfg(feature = "async-secure")]
DataStreamProj::Ssl(stream) => stream.poll_read(cx, buf),
}
}
}
-impl Write for DataStream {
+impl Write for DataStream
+where
+ T: AsyncTlsStream,
+{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
@@ -67,7 +77,6 @@ impl Write for DataStream {
) -> std::task::Poll> {
match self.project() {
DataStreamProj::Tcp(stream) => stream.poll_write(cx, buf),
- #[cfg(feature = "async-secure")]
DataStreamProj::Ssl(stream) => stream.poll_write(cx, buf),
}
}
@@ -78,7 +87,6 @@ impl Write for DataStream {
) -> std::task::Poll> {
match self.project() {
DataStreamProj::Tcp(stream) => stream.poll_flush(cx),
- #[cfg(feature = "async-secure")]
DataStreamProj::Ssl(stream) => stream.poll_flush(cx),
}
}
@@ -89,7 +97,6 @@ impl Write for DataStream {
) -> std::task::Poll> {
match self.project() {
DataStreamProj::Tcp(stream) => stream.poll_close(cx),
- #[cfg(feature = "async-secure")]
DataStreamProj::Ssl(stream) => stream.poll_close(cx),
}
}
diff --git a/suppaftp/src/async_ftp/mod.rs b/suppaftp/src/async_ftp/mod.rs
index 4901344..165f845 100644
--- a/suppaftp/src/async_ftp/mod.rs
+++ b/suppaftp/src/async_ftp/mod.rs
@@ -3,7 +3,6 @@
//! This module contains the definition for all async implementation of suppaftp
mod data_stream;
-#[cfg(feature = "async-secure")]
mod tls;
use super::regex::{EPSV_PORT_RE, MDTM_RE, PASV_PORT_RE, SIZE_RE};
@@ -12,30 +11,48 @@ use super::Status;
use crate::command::Command;
#[cfg(feature = "async-secure")]
use crate::command::ProtectionLevel;
+use async_std::io::prelude::BufReadExt;
use data_stream::DataStream;
-#[cfg(feature = "async-secure")]
-pub use tls::TlsConnector;
+use tls::AsyncTlsStream;
-use async_std::io::{copy, BufReader, Read, Write};
-use async_std::net::ToSocketAddrs;
-use async_std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream};
-use async_std::prelude::*;
+use async_std::io::{copy, BufReader, Read, Write, WriteExt};
+use async_std::net::{TcpListener, TcpStream, ToSocketAddrs};
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
+#[cfg(not(feature = "async-secure"))]
+use std::marker::PhantomData;
+use std::net::{Ipv4Addr, SocketAddr};
use std::string::String;
+// export
+pub use tls::AsyncNoTlsStream;
+#[cfg(feature = "async-secure")]
+pub use tls::AsyncTlsConnector;
+#[cfg(feature = "async-native-tls")]
+pub use tls::{AsyncNativeTlsConnector, AsyncNativeTlsStream};
+#[cfg(feature = "async-rustls")]
+pub use tls::{AsyncRustlsConnector, AsyncRustlsStream};
+
/// Stream to interface with the FTP server. This interface is only for the command stream.
-pub struct FtpStream {
- reader: BufReader,
+pub struct ImplAsyncFtpStream
+where
+ T: AsyncTlsStream,
+{
+ reader: BufReader>,
mode: Mode,
nat_workaround: bool,
welcome_msg: Option,
+ #[cfg(not(feature = "async-secure"))]
+ marker: PhantomData,
#[cfg(feature = "async-secure")]
- tls_ctx: Option,
+ tls_ctx: Option>>,
#[cfg(feature = "async-secure")]
domain: Option,
}
-impl FtpStream {
+impl ImplAsyncFtpStream
+where
+ T: AsyncTlsStream,
+{
/// Creates an FTP Stream.
#[cfg(not(feature = "async-secure"))]
pub async fn connect(addr: A) -> FtpResult {
@@ -45,8 +62,9 @@ impl FtpStream {
.map_err(FtpError::ConnectionError)?;
debug!("Established connection with server");
- let mut ftp_stream = FtpStream {
+ let mut ftp_stream = ImplAsyncFtpStream {
reader: BufReader::new(DataStream::Tcp(stream)),
+ marker: PhantomData {},
mode: Mode::Passive,
nat_workaround: false,
welcome_msg: None,
@@ -71,7 +89,7 @@ impl FtpStream {
.await
.map_err(FtpError::ConnectionError)?;
debug!("Connecting to server");
- let mut ftp_stream = FtpStream {
+ let mut ftp_stream = ImplAsyncFtpStream {
reader: BufReader::new(DataStream::Tcp(stream)),
mode: Mode::Passive,
nat_workaround: false,
@@ -97,20 +115,20 @@ impl FtpStream {
/// ## Example
///
/// ```rust,no_run
- /// use suppaftp::FtpStream;
+ /// use suppaftp::ImplAsyncFtpStream;
/// use suppaftp::async_native_tls::{TlsConnector, TlsStream};
/// use std::path::Path;
///
/// // Create a TlsConnector
/// // NOTE: For custom options see
/// let mut ctx = TlsConnector::new();
- /// let mut ftp_stream = FtpStream::connect("127.0.0.1:21").await.unwrap();
+ /// let mut ftp_stream = ImplAsyncFtpStream::connect("127.0.0.1:21").await.unwrap();
/// let mut ftp_stream = ftp_stream.into_secure(ctx, "localhost").await.unwrap();
/// ```
#[cfg(feature = "async-secure")]
pub async fn into_secure(
mut self,
- tls_connector: TlsConnector,
+ tls_connector: impl AsyncTlsConnector + 'static,
domain: &str,
) -> FtpResult {
debug!("Initializing TLS auth");
@@ -125,11 +143,11 @@ impl FtpStream {
)
.await
.map_err(|e| FtpError::SecureError(format!("{e}")))?;
- let mut secured_ftp_tream = FtpStream {
+ let mut secured_ftp_tream = ImplAsyncFtpStream {
reader: BufReader::new(DataStream::Ssl(Box::new(stream))),
mode: self.mode,
nat_workaround: self.nat_workaround,
- tls_ctx: Some(tls_connector),
+ tls_ctx: Some(Box::new(tls_connector)),
domain: Some(String::from(domain)),
welcome_msg: self.welcome_msg,
};
@@ -152,19 +170,19 @@ impl FtpStream {
/// ## Example
///
/// ```rust,no_run
- /// use suppaftp::FtpStream;
+ /// use suppaftp::ImplAsyncFtpStream;
/// use suppaftp::native_tls::{TlsConnector, TlsStream};
/// use std::path::Path;
///
/// // Create a TlsConnector
/// // NOTE: For custom options see
/// let mut ctx = TlsConnector::new();
- /// let mut ftp_stream = FtpStream::connect_secure_implicit("127.0.0.1:990", ctx, "localhost").await.unwrap();
+ /// let mut ftp_stream = ImplAsyncFtpStream::connect_secure_implicit("127.0.0.1:990", ctx, "localhost").await.unwrap();
/// ```
#[cfg(all(feature = "async-secure", feature = "deprecated"))]
pub async fn connect_secure_implicit(
addr: A,
- tls_connector: TlsConnector,
+ tls_connector: impl AsyncTlsConnector + 'static,
domain: &str,
) -> FtpResult {
debug!("Connecting to server (secure)");
@@ -173,7 +191,7 @@ impl FtpStream {
.map_err(FtpError::ConnectionError)
.map(|stream| {
debug!("Established connection with server");
- FtpStream {
+ Self {
reader: BufReader::new(DataStream::Tcp(stream)),
mode: Mode::Passive,
nat_workaround: false,
@@ -189,11 +207,11 @@ impl FtpStream {
.await
.map_err(|e| FtpError::SecureError(format!("{e}")))?;
debug!("TLS Steam OK");
- let mut stream = FtpStream {
+ let mut stream = ImplAsyncFtpStream {
reader: BufReader::new(DataStream::Ssl(stream.into())),
mode: Mode::Passive,
nat_workaround: false,
- tls_ctx: Some(tls_connector),
+ tls_ctx: Some(Box::new(tls_connector)),
domain: Some(String::from(domain)),
welcome_msg: None,
};
@@ -363,15 +381,16 @@ impl FtpStream {
/// The implementation of `RETR` command where `filename` is the name of the file
/// to download from FTP and `reader` is the function which operates with the
/// data stream opened.
- pub async fn retr(&mut self, file_name: S, mut reader: F) -> FtpResult
+ pub async fn retr(&mut self, file_name: S, mut reader: F) -> FtpResult
where
- F: FnMut(&mut dyn Read) -> FtpResult,
+ F: FnMut(&mut dyn Read) -> FtpResult,
S: AsRef,
{
match self.retr_as_stream(file_name).await {
Ok(mut stream) => {
let result = reader(&mut stream)?;
- self.finalize_retr_stream(stream).await.map(|_| result)
+ self.finalize_retr_stream(stream).await?;
+ Ok(result)
}
Err(err) => Err(err),
}
@@ -382,7 +401,10 @@ impl FtpStream {
/// The reader returned should be dropped.
/// Also you will have to read the response to make sure it has the correct value.
/// Once file has been read, call `finalize_retr_stream()`
- pub async fn retr_as_stream>(&mut self, file_name: S) -> FtpResult {
+ pub async fn retr_as_stream>(
+ &mut self,
+ file_name: S,
+ ) -> FtpResult> {
debug!("Retrieving '{}'", file_name.as_ref());
let data_stream = self
.data_command(Command::Retr(file_name.as_ref().to_string()))
@@ -444,7 +466,10 @@ impl FtpStream {
/// The returned stream must be then correctly manipulated to write the content of the source file to the remote destination
/// The stream must be then correctly dropped.
/// Once you've finished the write, YOU MUST CALL THIS METHOD: `finalize_put_stream`
- pub async fn put_with_stream>(&mut self, filename: S) -> FtpResult {
+ pub async fn put_with_stream>(
+ &mut self,
+ filename: S,
+ ) -> FtpResult> {
debug!("Put file {}", filename.as_ref());
let stream = self
.data_command(Command::Store(filename.as_ref().to_string()))
@@ -473,7 +498,7 @@ impl FtpStream {
pub async fn append_with_stream>(
&mut self,
filename: S,
- ) -> FtpResult {
+ ) -> FtpResult> {
debug!("Appending to file {}", filename.as_ref());
let stream = self
.data_command(Command::Appe(filename.as_ref().to_string()))
@@ -612,7 +637,7 @@ impl FtpStream {
// -- private
/// Execute command which send data back in a separate stream
- async fn data_command(&mut self, cmd: Command) -> FtpResult {
+ async fn data_command(&mut self, cmd: Command) -> FtpResult> {
let stream = match self.mode {
Mode::Active => {
let listener = self.active().await?;
@@ -730,9 +755,7 @@ impl FtpStream {
let ip = match self.reader.get_mut() {
DataStream::Tcp(stream) => stream.local_addr().unwrap().ip(),
-
- #[cfg(feature = "async-secure")]
- DataStream::Ssl(stream) => stream.get_mut().local_addr().unwrap().ip(),
+ DataStream::Ssl(stream) => stream.get_ref().local_addr().unwrap().ip(),
};
let msb = addr.port() / 256;
@@ -749,7 +772,7 @@ impl FtpStream {
/// Retrieve stream "message"
async fn get_lines_from_stream(
- data_stream: &mut BufReader,
+ data_stream: &mut BufReader>,
) -> FtpResult> {
let mut lines: Vec = Vec::new();
@@ -860,7 +883,12 @@ mod test {
use super::*;
#[cfg(feature = "with-containers")]
use crate::types::FormatControl;
+ use crate::AsyncFtpStream;
+ #[cfg(feature = "async-native-tls")]
+ use crate::{AsyncNativeTlsConnector, AsyncNativeTlsFtpStream};
+ #[cfg(feature = "async-rustls")]
+ use crate::{AsyncRustlsConnector, AsyncRustlsFtpStream};
#[cfg(feature = "async-native-tls")]
use async_native_tls::TlsConnector as NativeTlsConnector;
#[cfg(feature = "async-rustls")]
@@ -877,7 +905,7 @@ mod test {
#[serial]
async fn connect() {
crate::log_init();
- let stream: FtpStream = setup_stream().await;
+ let stream = setup_stream().await;
finalize_stream(stream).await;
}
@@ -885,10 +913,17 @@ mod test {
#[cfg(feature = "async-native-tls")]
#[serial]
async fn should_connect_ssl_native_tls() {
+ use crate::AsyncNativeTlsFtpStream;
+
crate::log_init();
- let ftp_stream = FtpStream::connect("test.rebex.net:21").await.unwrap();
+ let ftp_stream = AsyncNativeTlsFtpStream::connect("test.rebex.net:21")
+ .await
+ .unwrap();
let mut ftp_stream = ftp_stream
- .into_secure(NativeTlsConnector::new().into(), "test.rebex.net")
+ .into_secure(
+ AsyncNativeTlsConnector::from(NativeTlsConnector::new()),
+ "test.rebex.net",
+ )
.await
.unwrap();
// Set timeout (to test ref to ssl)
@@ -906,9 +941,9 @@ mod test {
#[cfg(all(feature = "async-native-tls", feature = "deprecated"))]
async fn should_connect_ssl_implicit_native_tls() {
crate::log_init();
- let mut ftp_stream = FtpStream::connect_secure_implicit(
+ let mut ftp_stream = AsyncNativeTlsFtpStream::connect_secure_implicit(
"test.rebex.net:990",
- NativeTlsConnector::new().into(),
+ AsyncNativeTlsConnector::from(NativeTlsConnector::new()),
"test.rebex.net",
)
.await
@@ -928,10 +963,13 @@ mod test {
#[serial]
async fn should_work_after_clear_command_channel_native_tls() {
crate::log_init();
- let mut ftp_stream = FtpStream::connect("test.rebex.net:21")
+ let mut ftp_stream = AsyncNativeTlsFtpStream::connect("test.rebex.net:21")
.await
.unwrap()
- .into_secure(NativeTlsConnector::new().into(), "test.rebex.net")
+ .into_secure(
+ AsyncNativeTlsConnector::from(NativeTlsConnector::new()),
+ "test.rebex.net",
+ )
.await
.unwrap()
.clear_command_channel()
@@ -950,9 +988,14 @@ mod test {
#[serial]
async fn should_connect_ssl_rustls() {
crate::log_init();
- let ftp_stream = FtpStream::connect("ftp.uni-bayreuth.de:21").await.unwrap();
+ let ftp_stream = AsyncRustlsFtpStream::connect("ftp.uni-bayreuth.de:21")
+ .await
+ .unwrap();
let mut ftp_stream = ftp_stream
- .into_secure(RustlsTlsConnector::new().into(), "ftp.uni-bayreuth.de")
+ .into_secure(
+ AsyncRustlsConnector::from(RustlsTlsConnector::new()),
+ "ftp.uni-bayreuth.de",
+ )
.await
.unwrap();
// Set timeout (to test ref to ssl)
@@ -965,7 +1008,7 @@ mod test {
#[serial]
async fn should_change_mode() {
crate::log_init();
- let mut ftp_stream = FtpStream::connect("test.rebex.net:21")
+ let mut ftp_stream = AsyncFtpStream::connect("test.rebex.net:21")
.await
.map(|x| x.active_mode())
.unwrap();
@@ -979,7 +1022,7 @@ mod test {
#[serial]
async fn welcome_message() {
crate::log_init();
- let stream: FtpStream = setup_stream().await;
+ let stream = setup_stream().await;
assert_eq!(
stream.get_welcome_msg().unwrap(),
"220 You will be disconnected after 15 minutes of inactivity."
@@ -992,7 +1035,7 @@ mod test {
#[serial]
async fn should_set_passive_nat_workaround() {
crate::log_init();
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
stream.set_passive_nat_workaround(true);
assert!(stream.nat_workaround);
finalize_stream(stream).await;
@@ -1003,7 +1046,7 @@ mod test {
#[serial]
async fn get_ref() {
crate::log_init();
- let stream: FtpStream = setup_stream().await;
+ let stream = setup_stream().await;
assert!(stream.get_ref().await.set_ttl(255).is_ok());
finalize_stream(stream).await;
}
@@ -1013,7 +1056,7 @@ mod test {
#[serial]
async fn change_wrkdir() {
crate::log_init();
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
let wrkdir: String = stream.pwd().await.unwrap();
assert!(stream.cwd("/").await.is_ok());
assert_eq!(stream.pwd().await.unwrap().as_str(), "/");
@@ -1026,7 +1069,7 @@ mod test {
#[serial]
async fn cd_up() {
crate::log_init();
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
let wrkdir: String = stream.pwd().await.unwrap();
assert!(stream.cdup().await.is_ok());
assert_eq!(stream.pwd().await.unwrap().as_str(), "/");
@@ -1039,7 +1082,7 @@ mod test {
#[serial]
async fn noop() {
crate::log_init();
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
assert!(stream.noop().await.is_ok());
finalize_stream(stream).await;
}
@@ -1049,7 +1092,7 @@ mod test {
#[serial]
async fn make_and_remove_dir() {
crate::log_init();
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
// Make directory
assert!(stream.mkdir("omar").await.is_ok());
// It shouldn't allow me to re-create the directory; should return error code 550
@@ -1069,7 +1112,7 @@ mod test {
#[serial]
async fn set_transfer_type() {
crate::log_init();
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
assert!(stream.transfer_type(FileType::Binary).await.is_ok());
assert!(stream
.transfer_type(FileType::Ascii(FormatControl::Default))
@@ -1085,7 +1128,7 @@ mod test {
crate::log_init();
use async_std::io::Cursor;
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
// Set transfer type to Binary
assert!(stream.transfer_type(FileType::Binary).await.is_ok());
// Write file
@@ -1098,7 +1141,11 @@ mod test {
// Read file
let mut reader = stream.retr_as_stream("test.txt").await.unwrap();
let mut buffer = Vec::new();
- assert!(reader.read_to_end(&mut buffer).await.is_ok());
+ assert!(
+ async_std::io::ReadExt::read_to_end(&mut reader, &mut buffer)
+ .await
+ .is_ok()
+ );
// Verify file matches
assert_eq!(buffer.as_slice(), "test data\ntest data\n".as_bytes());
// Finalize
@@ -1132,7 +1179,7 @@ mod test {
#[serial]
async fn should_abort_transfer() {
crate::log_init();
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
// Set transfer type to Binary
assert!(stream.transfer_type(FileType::Binary).await.is_ok());
// cleanup
@@ -1161,7 +1208,7 @@ mod test {
#[cfg(feature = "with-containers")]
async fn should_resume_transfer() {
crate::log_init();
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
// Set transfer type to Binary
assert!(stream.transfer_type(FileType::Binary).await.is_ok());
// get dir
@@ -1179,7 +1226,9 @@ mod test {
drop(stream);
drop(transfer_stream);
// Re-connect to server
- let mut stream = FtpStream::connect("127.0.0.1:10021").await.unwrap();
+ let mut stream = ImplAsyncFtpStream::connect("127.0.0.1:10021")
+ .await
+ .unwrap();
assert!(stream.login("test", "test").await.is_ok());
// Go back to previous dir
assert!(stream.cwd(wrkdir).await.is_ok());
@@ -1213,7 +1262,7 @@ mod test {
crate::log_init();
use async_std::io::Cursor;
- let mut stream: FtpStream = setup_stream().await;
+ let mut stream = setup_stream().await;
// Set transfer type to Binary
assert!(stream.transfer_type(FileType::Binary).await.is_ok());
stream.set_mode(Mode::ExtendedPassive);
@@ -1229,9 +1278,11 @@ mod test {
// -- test utils
#[cfg(feature = "with-containers")]
- async fn setup_stream() -> FtpStream {
+ async fn setup_stream() -> crate::AsyncFtpStream {
crate::log_init();
- let mut ftp_stream = FtpStream::connect("127.0.0.1:10021").await.unwrap();
+ let mut ftp_stream = ImplAsyncFtpStream::connect("127.0.0.1:10021")
+ .await
+ .unwrap();
assert!(ftp_stream.login("test", "test").await.is_ok());
// Create wrkdir
let tempdir: String = generate_tempdir();
@@ -1242,7 +1293,7 @@ mod test {
}
#[cfg(feature = "with-containers")]
- async fn finalize_stream(mut stream: FtpStream) {
+ async fn finalize_stream(mut stream: crate::AsyncFtpStream) {
crate::log_init();
// Get working directory
let wrkdir: String = stream.pwd().await.unwrap();
diff --git a/suppaftp/src/async_ftp/tls.rs b/suppaftp/src/async_ftp/tls.rs
index 497eb65..7bed758 100644
--- a/suppaftp/src/async_ftp/tls.rs
+++ b/suppaftp/src/async_ftp/tls.rs
@@ -2,12 +2,92 @@
//!
//! Tls wrappers
+use async_std::io::{Read, Write};
+use async_std::net::TcpStream;
+use async_trait::async_trait;
+use std::fmt::Debug;
+
+use crate::FtpResult;
+
#[cfg(feature = "async-native-tls")]
mod native_tls;
#[cfg(feature = "async-native-tls")]
-pub use self::native_tls::TlsConnector;
+pub use self::native_tls::{AsyncNativeTlsConnector, AsyncNativeTlsStream};
#[cfg(feature = "async-rustls")]
mod rustls;
#[cfg(feature = "async-rustls")]
-pub use self::rustls::TlsConnector;
+pub use self::rustls::{AsyncRustlsConnector, AsyncRustlsStream};
+
+#[async_trait]
+pub trait AsyncTlsConnector: Debug {
+ type Stream: AsyncTlsStream;
+
+ async fn connect(&self, domain: &str, stream: TcpStream) -> FtpResult;
+}
+
+pub trait AsyncTlsStream: Debug + Read + Write + Unpin {
+ type InnerStream: Read + Write;
+
+ /// Get underlying tcp stream
+ fn tcp_stream(self) -> TcpStream;
+
+ /// Get ref to underlying tcp stream
+ fn get_ref(&self) -> &TcpStream;
+
+ /// Get mutable reference to tls stream
+ fn mut_ref(&mut self) -> &mut Self::InnerStream;
+}
+
+#[derive(Debug)]
+pub struct AsyncNoTlsStream;
+
+impl Read for AsyncNoTlsStream {
+ fn poll_read(
+ self: std::pin::Pin<&mut Self>,
+ _cx: &mut std::task::Context<'_>,
+ _buf: &mut [u8],
+ ) -> std::task::Poll> {
+ panic!()
+ }
+}
+
+impl Write for AsyncNoTlsStream {
+ fn poll_write(
+ self: std::pin::Pin<&mut Self>,
+ _cx: &mut std::task::Context<'_>,
+ _buf: &[u8],
+ ) -> std::task::Poll> {
+ panic!()
+ }
+
+ fn poll_flush(
+ self: std::pin::Pin<&mut Self>,
+ _cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll> {
+ panic!()
+ }
+
+ fn poll_close(
+ self: std::pin::Pin<&mut Self>,
+ _cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll> {
+ panic!()
+ }
+}
+
+impl AsyncTlsStream for AsyncNoTlsStream {
+ type InnerStream = TcpStream;
+
+ fn tcp_stream(self) -> TcpStream {
+ panic!()
+ }
+
+ fn get_ref(&self) -> &TcpStream {
+ panic!()
+ }
+
+ fn mut_ref(&mut self) -> &mut Self::InnerStream {
+ panic!()
+ }
+}
diff --git a/suppaftp/src/async_ftp/tls/native_tls.rs b/suppaftp/src/async_ftp/tls/native_tls.rs
index dcc230f..f122cb3 100644
--- a/suppaftp/src/async_ftp/tls/native_tls.rs
+++ b/suppaftp/src/async_ftp/tls/native_tls.rs
@@ -2,27 +2,102 @@
//!
//! Native tls types for suppaftp
-use async_native_tls::{Error as TlsError, TlsConnector as NativeTlsConnector, TlsStream};
-use async_std::net::TcpStream;
+use async_native_tls::{TlsConnector, TlsStream};
+use async_std::{
+ io::{Read, Write},
+ net::TcpStream,
+};
+use async_trait::async_trait;
+use pin_project::pin_project;
+use std::pin::Pin;
+
+use super::{AsyncTlsConnector, AsyncTlsStream};
+use crate::{FtpError, FtpResult};
#[derive(Debug)]
/// A Wrapper for the tls connector
-pub struct TlsConnector {
- connector: NativeTlsConnector,
+pub struct AsyncNativeTlsConnector {
+ connector: TlsConnector,
}
-impl From for TlsConnector {
- fn from(connector: NativeTlsConnector) -> Self {
+impl From for AsyncNativeTlsConnector {
+ fn from(connector: TlsConnector) -> Self {
Self { connector }
}
}
-impl TlsConnector {
- pub async fn connect(
- &self,
- domain: &str,
- stream: TcpStream,
- ) -> Result, TlsError> {
- self.connector.connect(domain, stream).await
+#[async_trait]
+impl AsyncTlsConnector for AsyncNativeTlsConnector {
+ type Stream = AsyncNativeTlsStream;
+
+ async fn connect(&self, domain: &str, stream: TcpStream) -> FtpResult {
+ self.connector
+ .connect(domain, stream)
+ .await
+ .map(AsyncNativeTlsStream::from)
+ .map_err(|e| FtpError::SecureError(e.to_string()))
+ }
+}
+
+#[derive(Debug)]
+#[pin_project(project = AsyncNativeTlsStreamProj)]
+pub struct AsyncNativeTlsStream {
+ #[pin]
+ stream: TlsStream,
+}
+
+impl From> for AsyncNativeTlsStream {
+ fn from(stream: TlsStream) -> Self {
+ Self { stream }
+ }
+}
+
+impl Read for AsyncNativeTlsStream {
+ fn poll_read(
+ self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ buf: &mut [u8],
+ ) -> std::task::Poll> {
+ self.project().stream.poll_read(cx, buf)
+ }
+}
+
+impl Write for AsyncNativeTlsStream {
+ fn poll_write(
+ self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ buf: &[u8],
+ ) -> std::task::Poll> {
+ self.project().stream.poll_write(cx, buf)
+ }
+
+ fn poll_flush(
+ self: Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll> {
+ self.project().stream.poll_flush(cx)
+ }
+
+ fn poll_close(
+ self: Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll> {
+ self.project().stream.poll_close(cx)
+ }
+}
+
+impl AsyncTlsStream for AsyncNativeTlsStream {
+ type InnerStream = TlsStream;
+
+ fn get_ref(&self) -> &TcpStream {
+ self.stream.get_ref()
+ }
+
+ fn mut_ref(&mut self) -> &mut Self::InnerStream {
+ &mut self.stream
+ }
+
+ fn tcp_stream(self) -> TcpStream {
+ self.stream.get_ref().clone()
}
}
diff --git a/suppaftp/src/async_ftp/tls/rustls.rs b/suppaftp/src/async_ftp/tls/rustls.rs
index 9cdb414..8c85110 100644
--- a/suppaftp/src/async_ftp/tls/rustls.rs
+++ b/suppaftp/src/async_ftp/tls/rustls.rs
@@ -2,33 +2,107 @@
//!
//! rustls types for suppaftp
-use async_std::io::Error as IoError;
-use async_std::net::TcpStream;
+use async_std::{
+ io::{Read, Write},
+ net::TcpStream,
+};
use async_tls::{client::TlsStream, TlsConnector as RustlsTlsConnector};
+use async_trait::async_trait;
+use pin_project::pin_project;
+use std::pin::Pin;
+
+use super::{AsyncTlsConnector, AsyncTlsStream};
+use crate::{FtpError, FtpResult};
/// A Wrapper for the tls connector
-pub struct TlsConnector {
+pub struct AsyncRustlsConnector {
connector: RustlsTlsConnector,
}
-impl std::fmt::Debug for TlsConnector {
+impl std::fmt::Debug for AsyncRustlsConnector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, ">")
}
}
-impl From for TlsConnector {
+impl From for AsyncRustlsConnector {
fn from(connector: RustlsTlsConnector) -> Self {
Self { connector }
}
}
-impl TlsConnector {
- pub async fn connect(
- &self,
- domain: &str,
- stream: TcpStream,
- ) -> Result, IoError> {
- self.connector.connect(domain, stream).await
+#[async_trait]
+impl AsyncTlsConnector for AsyncRustlsConnector {
+ type Stream = AsyncRustlsStream;
+
+ async fn connect(&self, domain: &str, stream: TcpStream) -> FtpResult {
+ self.connector
+ .connect(domain, stream)
+ .await
+ .map(AsyncRustlsStream::from)
+ .map_err(|e| FtpError::SecureError(e.to_string()))
+ }
+}
+
+#[derive(Debug)]
+#[pin_project(project = AsyncRustlsStreamProj)]
+pub struct AsyncRustlsStream {
+ #[pin]
+ stream: TlsStream,
+}
+
+impl From> for AsyncRustlsStream {
+ fn from(stream: TlsStream) -> Self {
+ Self { stream }
+ }
+}
+
+impl Read for AsyncRustlsStream {
+ fn poll_read(
+ self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ buf: &mut [u8],
+ ) -> std::task::Poll> {
+ self.project().stream.poll_read(cx, buf)
+ }
+}
+
+impl Write for AsyncRustlsStream {
+ fn poll_write(
+ self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ buf: &[u8],
+ ) -> std::task::Poll> {
+ self.project().stream.poll_write(cx, buf)
+ }
+
+ fn poll_flush(
+ self: Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll> {
+ self.project().stream.poll_flush(cx)
+ }
+
+ fn poll_close(
+ self: Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll> {
+ self.project().stream.poll_close(cx)
+ }
+}
+
+impl AsyncTlsStream for AsyncRustlsStream {
+ type InnerStream = TlsStream;
+
+ fn get_ref(&self) -> &TcpStream {
+ self.stream.get_ref()
+ }
+
+ fn mut_ref(&mut self) -> &mut Self::InnerStream {
+ &mut self.stream
+ }
+
+ fn tcp_stream(self) -> TcpStream {
+ self.stream.get_ref().clone()
}
}
diff --git a/suppaftp/src/lib.rs b/suppaftp/src/lib.rs
index e273289..6c12c29 100644
--- a/suppaftp/src/lib.rs
+++ b/suppaftp/src/lib.rs
@@ -21,7 +21,7 @@
//! To get started, first add **suppaftp** to your dependencies:
//!
//! ```toml
-//! suppaftp = "^4.7.0"
+//! suppaftp = "^5.0.0"
//! ```
//!
//! ### Features
@@ -31,9 +31,9 @@
//! If you want to enable **support for FTPS**, you must enable the `native-tls` or `rustls` feature in your cargo dependencies, based on the TLS provider you prefer.
//!
//! ```toml
-//! suppaftp = { version = "^4.7.0", features = ["native-tls"] }
+//! suppaftp = { version = "^5.0.0", features = ["native-tls"] }
//! # or
-//! suppaftp = { version = "^4.7.0", features = ["rustls"] }
+//! suppaftp = { version = "^5.0.0", features = ["rustls"] }
//! ```
//!
//! > 💡 If you don't know what to choose, `native-tls` should be preferred for compatibility reasons.
@@ -43,7 +43,7 @@
//! If you want to enable **async** support, you must enable `async` feature in your cargo dependencies.
//!
//! ```toml
-//! suppaftp = { version = "^4.7.0", features = ["async"] }
+//! suppaftp = { version = "^5.0.0", features = ["async"] }
//! ```
//!
//! > ⚠️ If you want to enable both **native-tls** and **async** you must use the **async-native-tls** feature ⚠️
@@ -86,19 +86,14 @@
//! ### FTPS Usage
//!
//! ```rust
-//! extern crate suppaftp;
-//!
-//! use suppaftp::FtpStream;
+//! use suppaftp::{NativeTlsFtpStream, NativeTlsConnector};
//! use suppaftp::native_tls::{TlsConnector, TlsStream};
//!
-//! let ftp_stream = FtpStream::connect("test.rebex.net:21").unwrap();
+//! let ftp_stream = NativeTlsFtpStream::connect("test.rebex.net:21").unwrap();
//! // Switch to the secure mode
-//! let mut ftp_stream = ftp_stream.into_secure(TlsConnector::new().unwrap().into(), "test.rebex.net").unwrap();
+//! let mut ftp_stream = ftp_stream.into_secure(NativeTlsConnector::from(TlsConnector::new().unwrap()), "test.rebex.net").unwrap();
//! ftp_stream.login("demo", "password").unwrap();
//! // Do other secret stuff
-//! // Switch back to the insecure mode (if required)
-//! let mut ftp_stream = ftp_stream.into_insecure().unwrap();
-//! // Do all public stuff
//! assert!(ftp_stream.quit().is_ok());
//! ```
//!
@@ -109,17 +104,14 @@
//! Let's quickly see in the example how it works
//!
//! ```rust
-//! extern crate suppaftp;
-//!
-//! use suppaftp::FtpStream;
+//! use suppaftp::{AsyncFtpStream, AsyncNativeTlsConnector};
//! use suppaftp::async_native_tls::{TlsConnector, TlsStream};
//!
-//! let ftp_stream = FtpStream::connect("test.rebex.net:21").await.unwrap();
+//! let ftp_stream = AsyncFtpStream::connect("test.rebex.net:21").await.unwrap();
//! // Switch to the secure mode
-//! let mut ftp_stream = ftp_stream.into_secure(TlsConnector::new().into(), "test.rebex.net").await.unwrap();
+//! let mut ftp_stream = ftp_stream.into_secure(AsyncNativeTlsConnector::from(TlsConnector::new()), "test.rebex.net").await.unwrap();
//! ftp_stream.login("demo", "password").await.unwrap();
//! // Do other secret stuff
-//! // Do all public stuff
//! assert!(ftp_stream.quit().await.is_ok());
//! ```
//!
@@ -139,12 +131,11 @@ extern crate lazy_regex;
extern crate log;
// -- private
-#[cfg(any(feature = "async", feature = "async-secure"))]
+#[cfg(feature = "async")]
mod async_ftp;
pub(crate) mod command;
mod regex;
mod status;
-#[cfg(any(test, not(any(feature = "async", feature = "async-secure"))))]
mod sync_ftp;
// -- public
@@ -160,22 +151,50 @@ pub extern crate rustls_crate as rustls;
#[cfg(feature = "async-native-tls")]
pub extern crate async_native_tls_crate as async_native_tls;
-// -- export async
-#[cfg(any(feature = "async", feature = "async-secure"))]
-pub use async_ftp::FtpStream;
-// -- export sync
-#[cfg(not(any(feature = "async", feature = "async-secure")))]
-pub use sync_ftp::FtpStream;
-// -- export secure
-#[cfg(all(feature = "secure", not(feature = "async-secure")))]
-pub use sync_ftp::TlsConnector;
-// -- export async secure
-#[cfg(all(feature = "async-secure", not(feature = "secure")))]
-pub use async_ftp::TlsConnector;
// -- export (common)
pub use status::Status;
pub use types::{FtpError, FtpResult, Mode};
+// -- export sync
+use sync_ftp::{ImplFtpStream, NoTlsStream};
+pub type FtpStream = ImplFtpStream;
+// -- export secure (native-tls)
+#[cfg(feature = "native-tls")]
+pub use sync_ftp::NativeTlsConnector;
+#[cfg(feature = "native-tls")]
+use sync_ftp::NativeTlsStream;
+#[cfg(feature = "native-tls")]
+pub type NativeTlsFtpStream = ImplFtpStream;
+// -- export secure (rustls)
+#[cfg(feature = "rustls")]
+pub use sync_ftp::RustlsConnector;
+#[cfg(feature = "rustls")]
+use sync_ftp::RustlsStream;
+#[cfg(feature = "rustls")]
+pub type RustlsFtpStream = ImplFtpStream;
+
+// -- export async
+#[cfg(feature = "async")]
+use async_ftp::AsyncNoTlsStream;
+#[cfg(feature = "async")]
+use async_ftp::ImplAsyncFtpStream;
+#[cfg(feature = "async")]
+pub type AsyncFtpStream = ImplAsyncFtpStream;
+// -- export async secure (native-tls)
+#[cfg(feature = "async-native-tls")]
+pub use async_ftp::AsyncNativeTlsConnector;
+#[cfg(feature = "async-native-tls")]
+use async_ftp::AsyncNativeTlsStream;
+#[cfg(feature = "async-native-tls")]
+pub type AsyncNativeTlsFtpStream = ImplAsyncFtpStream;
+// -- export async secure (rustls)
+#[cfg(feature = "async-rustls")]
+pub use async_ftp::AsyncRustlsConnector;
+#[cfg(feature = "async-rustls")]
+use async_ftp::AsyncRustlsStream;
+#[cfg(feature = "async-rustls")]
+pub type AsyncRustlsFtpStream = ImplAsyncFtpStream;
+
// -- test logging
#[cfg(test)]
pub fn log_init() {
diff --git a/suppaftp/src/sync_ftp/data_stream.rs b/suppaftp/src/sync_ftp/data_stream.rs
index 0f03344..05483b8 100644
--- a/suppaftp/src/sync_ftp/data_stream.rs
+++ b/suppaftp/src/sync_ftp/data_stream.rs
@@ -2,7 +2,6 @@
//!
//! This module exposes the data stream where bytes must be written to/read from
-#[cfg(feature = "secure")]
use super::tls::TlsStream;
use std::io::{Read, Result, Write};
@@ -10,14 +9,19 @@ use std::net::TcpStream;
/// Data Stream used for communications. It can be both of type Tcp in case of plain communication or Ssl in case of FTPS
#[derive(Debug)]
-pub enum DataStream {
+pub enum DataStream
+where
+ T: TlsStream,
+{
Tcp(TcpStream),
- #[cfg(feature = "secure")]
- Ssl(Box),
+ Ssl(Box),
}
#[cfg(feature = "secure")]
-impl DataStream {
+impl DataStream
+where
+ T: TlsStream,
+{
/// Unwrap the stream into TcpStream. This method is only used in secure connection.
pub fn into_tcp_stream(self) -> TcpStream {
match self {
@@ -27,12 +31,14 @@ impl DataStream {
}
}
-impl DataStream {
+impl DataStream
+where
+ T: TlsStream,
+{
/// Returns a reference to the underlying TcpStream.
pub fn get_ref(&self) -> &TcpStream {
match self {
DataStream::Tcp(ref stream) => stream,
- #[cfg(feature = "secure")]
DataStream::Ssl(ref stream) => stream.get_ref(),
}
}
@@ -40,29 +46,35 @@ impl DataStream {
// -- sync
-impl Read for DataStream {
+impl Read for DataStream
+where
+ T: TlsStream,
+{
fn read(&mut self, buf: &mut [u8]) -> Result {
match self {
DataStream::Tcp(ref mut stream) => stream.read(buf),
- #[cfg(feature = "secure")]
DataStream::Ssl(ref mut stream) => stream.mut_ref().read(buf),
}
}
}
-impl Write for DataStream {
+impl Write for DataStream
+where
+ T: TlsStream,
+{
fn write(&mut self, buf: &[u8]) -> Result {
match self {
DataStream::Tcp(ref mut stream) => stream.write(buf),
- #[cfg(feature = "secure")]
DataStream::Ssl(ref mut stream) => stream.mut_ref().write(buf),
}
}
- fn flush(&mut self) -> Result<()> {
+ fn flush(&mut self) -> Result<()>
+ where
+ T: TlsStream,
+ {
match self {
DataStream::Tcp(ref mut stream) => stream.flush(),
- #[cfg(feature = "secure")]
DataStream::Ssl(ref mut stream) => stream.mut_ref().flush(),
}
}
diff --git a/suppaftp/src/sync_ftp/mod.rs b/suppaftp/src/sync_ftp/mod.rs
index 70ad276..cc46ef3 100644
--- a/suppaftp/src/sync_ftp/mod.rs
+++ b/suppaftp/src/sync_ftp/mod.rs
@@ -12,45 +12,60 @@ use crate::command::Command;
#[cfg(feature = "secure")]
use crate::command::ProtectionLevel;
use data_stream::DataStream;
-
-#[cfg(feature = "secure")]
use tls::TlsStream;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
-
+use std::fmt::Debug;
use std::io::{copy, BufRead, BufReader, Cursor, Read, Write};
+#[cfg(not(feature = "secure"))]
+use std::marker::PhantomData;
use std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream, ToSocketAddrs};
+// export
+pub use tls::NoTlsStream;
#[cfg(feature = "secure")]
pub use tls::TlsConnector;
+#[cfg(feature = "native-tls")]
+pub use tls::{NativeTlsConnector, NativeTlsStream};
+#[cfg(feature = "rustls")]
+pub use tls::{RustlsConnector, RustlsStream};
/// Stream to interface with the FTP server. This interface is only for the command stream.
#[derive(Debug)]
-pub struct FtpStream {
- reader: BufReader,
+pub struct ImplFtpStream
+where
+ T: TlsStream,
+{
+ reader: BufReader>,
mode: Mode,
nat_workaround: bool,
welcome_msg: Option,
+ #[cfg(not(feature = "secure"))]
+ marker: PhantomData,
#[cfg(feature = "secure")]
- tls_ctx: Option,
+ tls_ctx: Option>>,
#[cfg(feature = "secure")]
domain: Option,
}
-impl FtpStream {
- /// Creates an FTP Stream.
- #[cfg(not(feature = "secure"))]
+impl ImplFtpStream
+where
+ T: TlsStream,
+{
+ #[cfg(feature = "secure")]
pub fn connect(addr: A) -> FtpResult {
debug!("Connecting to server");
TcpStream::connect(addr)
.map_err(FtpError::ConnectionError)
.and_then(|stream| {
debug!("Established connection with server");
- let mut ftp_stream = FtpStream {
+ let mut ftp_stream = Self {
reader: BufReader::new(DataStream::Tcp(stream)),
mode: Mode::Passive,
nat_workaround: false,
welcome_msg: None,
+ tls_ctx: None,
+ domain: None,
};
debug!("Reading server response...");
match ftp_stream.read_response(Status::Ready) {
@@ -65,21 +80,20 @@ impl FtpStream {
})
}
+ #[cfg(not(feature = "secure"))]
/// Creates an FTP Stream.
- #[cfg(feature = "secure")]
pub fn connect(addr: A) -> FtpResult {
debug!("Connecting to server");
TcpStream::connect(addr)
.map_err(FtpError::ConnectionError)
.and_then(|stream| {
debug!("Established connection with server");
- let mut ftp_stream = FtpStream {
+ let mut ftp_stream = Self {
reader: BufReader::new(DataStream::Tcp(stream)),
mode: Mode::Passive,
nat_workaround: false,
welcome_msg: None,
- tls_ctx: None,
- domain: None,
+ marker: PhantomData {},
};
debug!("Reading server response...");
match ftp_stream.read_response(Status::Ready) {
@@ -93,7 +107,6 @@ impl FtpStream {
}
})
}
-
/// Enable active mode for data channel
pub fn active_mode(mut self) -> Self {
self.mode = Mode::Active;
@@ -117,18 +130,22 @@ impl FtpStream {
/// ## Example
///
/// ```rust,no_run
- /// use suppaftp::FtpStream;
+ /// use suppaftp::{NativeTlsFtpStream, NativeTlsConnector};
/// use suppaftp::native_tls::{TlsConnector, TlsStream};
/// use std::path::Path;
///
/// // Create a TlsConnector
/// // NOTE: For custom options see
- /// let mut ctx = TlsConnector::new().unwrap();
- /// let mut ftp_stream = FtpStream::connect("127.0.0.1:21").unwrap();
+ /// let mut ctx = NativeTlsConnector::from(TlsConnector::new().unwrap());
+ /// let mut ftp_stream = NativeTlsFtpStream::connect("127.0.0.1:21").unwrap();
/// let mut ftp_stream = ftp_stream.into_secure(ctx, "localhost").unwrap();
/// ```
#[cfg(feature = "secure")]
- pub fn into_secure(mut self, tls_connector: TlsConnector, domain: &str) -> FtpResult {
+ pub fn into_secure(
+ mut self,
+ tls_connector: impl TlsConnector + 'static,
+ domain: &str,
+ ) -> FtpResult {
// Ask the server to start securing data.
debug!("Initializing TLS auth");
self.perform(Command::Auth)?;
@@ -138,11 +155,11 @@ impl FtpStream {
.connect(domain, self.reader.into_inner().into_tcp_stream())
.map_err(|e| FtpError::SecureError(format!("{e}")))?;
debug!("TLS Steam OK");
- let mut secured_ftp_tream = FtpStream {
+ let mut secured_ftp_tream = Self {
reader: BufReader::new(DataStream::Ssl(Box::new(stream))),
mode: self.mode,
nat_workaround: self.nat_workaround,
- tls_ctx: Some(tls_connector),
+ tls_ctx: Some(Box::new(tls_connector)),
domain: Some(String::from(domain)),
welcome_msg: self.welcome_msg,
};
@@ -175,7 +192,7 @@ impl FtpStream {
#[cfg(all(feature = "secure", feature = "deprecated"))]
pub fn connect_secure_implicit(
addr: A,
- tls_connector: TlsConnector,
+ tls_connector: impl TlsConnector + 'static,
domain: &str,
) -> FtpResult {
debug!("Connecting to server (secure)");
@@ -183,7 +200,7 @@ impl FtpStream {
.map_err(FtpError::ConnectionError)
.map(|stream| {
debug!("Established connection with server");
- FtpStream {
+ Self {
reader: BufReader::new(DataStream::Tcp(stream)),
mode: Mode::Passive,
nat_workaround: false,
@@ -198,11 +215,11 @@ impl FtpStream {
.connect(domain, stream.reader.into_inner().into_tcp_stream())
.map_err(|e| FtpError::SecureError(format!("{e}")))?;
debug!("TLS Steam OK");
- let mut stream = FtpStream {
+ let mut stream = Self {
reader: BufReader::new(DataStream::Ssl(Box::new(stream))),
mode: Mode::Passive,
nat_workaround: false,
- tls_ctx: Some(tls_connector),
+ tls_ctx: Some(Box::new(tls_connector)),
domain: Some(String::from(domain)),
welcome_msg: None,
};
@@ -379,14 +396,15 @@ impl FtpStream {
/// }).is_ok());
/// # assert!(conn.rm("retr.txt").is_ok());
/// ```
- pub fn retr(&mut self, file_name: &str, mut reader: F) -> FtpResult
+ pub fn retr(&mut self, file_name: &str, mut reader: F) -> FtpResult
where
- F: FnMut(&mut dyn Read) -> FtpResult,
+ F: FnMut(&mut dyn Read) -> FtpResult,
{
match self.retr_as_stream(file_name) {
Ok(mut stream) => {
let result = reader(&mut stream)?;
- self.finalize_retr_stream(stream).map(|_| result)
+ self.finalize_retr_stream(stream)?;
+ Ok(result)
}
Err(err) => Err(err),
}
@@ -423,7 +441,7 @@ impl FtpStream {
/// The reader returned should be dropped.
/// Also you will have to read the response to make sure it has the correct value.
/// Once file has been read, call `finalize_retr_stream()`
- pub fn retr_as_stream>(&mut self, file_name: S) -> FtpResult {
+ pub fn retr_as_stream>(&mut self, file_name: S) -> FtpResult> {
debug!("Retrieving '{}'", file_name.as_ref());
let data_stream = self.data_command(Command::Retr(file_name.as_ref().to_string()))?;
self.read_response_in(&[Status::AboutToSend, Status::AlreadyOpen])?;
@@ -472,7 +490,7 @@ impl FtpStream {
/// The returned stream must be then correctly manipulated to write the content of the source file to the remote destination
/// The stream must be then correctly dropped.
/// Once you've finished the write, YOU MUST CALL THIS METHOD: `finalize_put_stream`
- pub fn put_with_stream>(&mut self, filename: S) -> FtpResult {
+ pub fn put_with_stream>(&mut self, filename: S) -> FtpResult> {
debug!("Put file {}", filename.as_ref());
let stream = self.data_command(Command::Store(filename.as_ref().to_string()))?;
self.read_response_in(&[Status::AlreadyOpen, Status::AboutToSend])?;
@@ -494,7 +512,7 @@ impl FtpStream {
/// Open specified file for appending data. Returns the stream to append data to specified file.
/// Once you've finished the write, YOU MUST CALL THIS METHOD: `finalize_put_stream`
- pub fn append_with_stream>(&mut self, filename: S) -> FtpResult {
+ pub fn append_with_stream>(&mut self, filename: S) -> FtpResult> {
debug!("Appending to file {}", filename.as_ref());
let stream = self.data_command(Command::Appe(filename.as_ref().to_string()))?;
self.read_response_in(&[Status::AlreadyOpen, Status::AboutToSend])?;
@@ -633,7 +651,7 @@ impl FtpStream {
// -- private
/// Retrieve stream "message"
- fn get_lines_from_stream(data_stream: &mut BufReader) -> FtpResult> {
+ fn get_lines_from_stream(data_stream: &mut BufReader>) -> FtpResult> {
let mut lines: Vec = Vec::new();
loop {
@@ -729,7 +747,7 @@ impl FtpStream {
}
/// Execute command which send data back in a separate stream
- fn data_command(&mut self, cmd: Command) -> FtpResult {
+ fn data_command(&mut self, cmd: Command) -> FtpResult> {
let stream = match self.mode {
Mode::Active => self
.active()
@@ -755,7 +773,6 @@ impl FtpStream {
match self.tls_ctx {
Some(ref tls_ctx) => tls_ctx
.connect(self.domain.as_ref().unwrap(), stream)
- .map(TlsStream::from)
.map(|x| DataStream::Ssl(Box::new(x)))
.map_err(|e| FtpError::SecureError(format!("{e}"))),
None => Ok(DataStream::Tcp(stream)),
@@ -772,7 +789,6 @@ impl FtpStream {
let ip = match self.reader.get_mut() {
DataStream::Tcp(stream) => stream.local_addr().unwrap().ip(),
- #[cfg(feature = "secure")]
DataStream::Ssl(stream) => stream.get_ref().local_addr().unwrap().ip(),
};
@@ -865,12 +881,13 @@ impl FtpStream {
mod test {
use super::*;
+ use crate::FtpStream;
#[cfg(feature = "with-containers")]
use crate::types::FormatControl;
#[cfg(feature = "native-tls")]
- use native_tls::TlsConnector as NativeTlsConnector;
+ use native_tls::TlsConnector;
#[cfg(any(feature = "with-containers", feature = "secure"))]
use pretty_assertions::assert_eq;
#[cfg(feature = "with-containers")]
@@ -895,9 +912,12 @@ mod test {
fn should_connect_ssl_native_tls() {
crate::log_init();
use std::time::Duration;
- let ftp_stream = FtpStream::connect("test.rebex.net:21").unwrap();
+ let ftp_stream = crate::NativeTlsFtpStream::connect("test.rebex.net:21").unwrap();
let mut ftp_stream = ftp_stream
- .into_secure(NativeTlsConnector::new().unwrap().into(), "test.rebex.net")
+ .into_secure(
+ NativeTlsConnector::from(TlsConnector::new().unwrap()),
+ "test.rebex.net",
+ )
.unwrap();
// Set timeout (to test ref to ssl)
assert!(ftp_stream
@@ -917,9 +937,12 @@ mod test {
#[cfg(feature = "native-tls")]
fn should_work_after_clear_command_channel_native_tls() {
crate::log_init();
- let mut ftp_stream = FtpStream::connect("test.rebex.net:21")
+ let mut ftp_stream = crate::NativeTlsFtpStream::connect("test.rebex.net:21")
.unwrap()
- .into_secure(NativeTlsConnector::new().unwrap().into(), "test.rebex.net")
+ .into_secure(
+ NativeTlsConnector::from(TlsConnector::new().unwrap()),
+ "test.rebex.net",
+ )
.unwrap()
.clear_command_channel()
.unwrap();
@@ -937,9 +960,9 @@ mod test {
fn should_connect_ssl_implicit_native_tls() {
use std::time::Duration;
crate::log_init();
- let mut ftp_stream = FtpStream::connect_secure_implicit(
+ let mut ftp_stream = crate::NativeTlsFtpStream::connect_secure_implicit(
"test.rebex.net:990",
- NativeTlsConnector::new().unwrap().into(),
+ NativeTlsConnector::from(TlsConnector::new().unwrap()),
"test.rebex.net",
)
.unwrap();
@@ -960,11 +983,16 @@ mod test {
#[serial]
#[cfg(feature = "rustls")]
fn should_connect_ssl_rustls() {
+ use super::tls::RustlsConnector;
+
crate::log_init();
let config = Arc::new(rustls_config());
- let mut ftp_stream = FtpStream::connect("ftp.uni-bayreuth.de:21")
+ let mut ftp_stream = crate::RustlsFtpStream::connect("ftp.uni-bayreuth.de:21")
.unwrap()
- .into_secure(Arc::clone(&config).into(), "ftp.uni-bayreuth.de")
+ .into_secure(
+ RustlsConnector::from(Arc::clone(&config)),
+ "ftp.uni-bayreuth.de",
+ )
.unwrap();
// Quit
assert!(ftp_stream.quit().is_ok());
diff --git a/suppaftp/src/sync_ftp/tls.rs b/suppaftp/src/sync_ftp/tls.rs
index 96b9bb4..47de45c 100644
--- a/suppaftp/src/sync_ftp/tls.rs
+++ b/suppaftp/src/sync_ftp/tls.rs
@@ -2,12 +2,56 @@
//!
//! Tls wrappers
+use std::io::Write;
+use std::net::TcpStream;
+use std::{fmt::Debug, io::Read};
+
#[cfg(feature = "native-tls")]
mod native_tls;
+use crate::FtpResult;
+
#[cfg(feature = "native-tls")]
-pub use self::native_tls::{TlsConnector, TlsStream};
+pub use self::native_tls::{NativeTlsConnector, NativeTlsStream};
#[cfg(feature = "rustls")]
mod rustls;
#[cfg(feature = "rustls")]
-pub use self::rustls::{TlsConnector, TlsStream};
+pub use self::rustls::{RustlsConnector, RustlsStream};
+
+pub trait TlsConnector: Debug {
+ type Stream: TlsStream;
+
+ fn connect(&self, domain: &str, stream: TcpStream) -> FtpResult;
+}
+
+pub trait TlsStream: Debug {
+ type InnerStream: Read + Write;
+
+ /// Get underlying tcp stream
+ fn tcp_stream(self) -> TcpStream;
+
+ /// Get ref to underlying tcp stream
+ fn get_ref(&self) -> &TcpStream;
+
+ /// Get mutable reference to tls stream
+ fn mut_ref(&mut self) -> &mut Self::InnerStream;
+}
+
+#[derive(Debug)]
+pub struct NoTlsStream;
+
+impl TlsStream for NoTlsStream {
+ type InnerStream = TcpStream;
+
+ fn tcp_stream(self) -> TcpStream {
+ panic!()
+ }
+
+ fn get_ref(&self) -> &TcpStream {
+ panic!()
+ }
+
+ fn mut_ref(&mut self) -> &mut Self::InnerStream {
+ panic!()
+ }
+}
diff --git a/suppaftp/src/sync_ftp/tls/native_tls.rs b/suppaftp/src/sync_ftp/tls/native_tls.rs
index 6ee5cc0..e9d5724 100644
--- a/suppaftp/src/sync_ftp/tls/native_tls.rs
+++ b/suppaftp/src/sync_ftp/tls/native_tls.rs
@@ -2,31 +2,33 @@
//!
//! Native tls implementation of TLS types
-use native_tls::{
- HandshakeError, TlsConnector as NativeTlsConnector, TlsStream as NativeTlsStream,
-};
+use native_tls_crate::{TlsConnector, TlsStream};
use std::io::Write;
use std::net::TcpStream;
+use super::{TlsConnector as TlsConnectorTrait, TlsStream as TlsStreamTrait};
+use crate::{FtpError, FtpResult};
+
#[derive(Debug)]
/// A Wrapper for the tls connector
-pub struct TlsConnector {
- connector: NativeTlsConnector,
+pub struct NativeTlsConnector {
+ connector: TlsConnector,
}
-impl From for TlsConnector {
- fn from(connector: NativeTlsConnector) -> Self {
+impl From for NativeTlsConnector {
+ fn from(connector: TlsConnector) -> Self {
Self { connector }
}
}
-impl TlsConnector {
- pub fn connect(
- &self,
- domain: &str,
- stream: TcpStream,
- ) -> Result> {
- self.connector.connect(domain, stream).map(TlsStream::from)
+impl TlsConnectorTrait for NativeTlsConnector {
+ type Stream = NativeTlsStream;
+
+ fn connect(&self, domain: &str, stream: TcpStream) -> FtpResult {
+ self.connector
+ .connect(domain, stream)
+ .map(NativeTlsStream::from)
+ .map_err(|e| FtpError::SecureError(e.to_string()))
}
}
@@ -35,14 +37,16 @@ impl TlsConnector {
/// Tls stream wrapper. This type is a garbage data type used to impl the drop trait for the tls stream.
/// This allows me to keep returning `Read` and `Write` traits in stream methods
#[derive(Debug)]
-pub struct TlsStream {
- stream: NativeTlsStream,
+pub struct NativeTlsStream {
+ stream: TlsStream,
ssl_shutdown: bool,
}
-impl TlsStream {
+impl TlsStreamTrait for NativeTlsStream {
+ type InnerStream = TlsStream;
+
/// Get underlying tcp stream
- pub(crate) fn tcp_stream(mut self) -> TcpStream {
+ fn tcp_stream(mut self) -> TcpStream {
let mut stream = self.stream.get_ref().try_clone().unwrap();
// Don't perform shutdown later
self.ssl_shutdown = false;
@@ -55,18 +59,18 @@ impl TlsStream {
}
/// Get ref to underlying tcp stream
- pub(crate) fn get_ref(&self) -> &TcpStream {
+ fn get_ref(&self) -> &TcpStream {
self.stream.get_ref()
}
/// Get mutable reference to tls stream
- pub(crate) fn mut_ref(&mut self) -> &mut NativeTlsStream {
+ fn mut_ref(&mut self) -> &mut TlsStream {
&mut self.stream
}
}
-impl From> for TlsStream {
- fn from(stream: NativeTlsStream) -> Self {
+impl From> for NativeTlsStream {
+ fn from(stream: TlsStream) -> Self {
Self {
stream,
ssl_shutdown: true,
@@ -74,7 +78,7 @@ impl From> for TlsStream {
}
}
-impl Drop for TlsStream {
+impl Drop for NativeTlsStream {
fn drop(&mut self) {
if self.ssl_shutdown {
if let Err(err) = self.stream.shutdown() {
diff --git a/suppaftp/src/sync_ftp/tls/rustls.rs b/suppaftp/src/sync_ftp/tls/rustls.rs
index 3f7f00b..08be499 100644
--- a/suppaftp/src/sync_ftp/tls/rustls.rs
+++ b/suppaftp/src/sync_ftp/tls/rustls.rs
@@ -9,31 +9,35 @@ use std::io::Write;
use std::net::TcpStream;
use std::sync::Arc;
+use super::{TlsConnector, TlsStream};
+
/// A Wrapper for the tls connector
-pub struct TlsConnector {
+pub struct RustlsConnector {
connector: Arc,
}
-impl std::fmt::Debug for TlsConnector {
+impl std::fmt::Debug for RustlsConnector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, ">")
}
}
-impl From> for TlsConnector {
+impl From> for RustlsConnector {
fn from(connector: Arc) -> Self {
Self { connector }
}
}
-impl TlsConnector {
- pub fn connect(&self, domain: &str, stream: TcpStream) -> FtpResult {
+impl TlsConnector for RustlsConnector {
+ type Stream = RustlsStream;
+
+ fn connect(&self, domain: &str, stream: TcpStream) -> FtpResult {
let server_name =
ServerName::try_from(domain).map_err(|e| FtpError::SecureError(e.to_string()))?;
let connection = ClientConnection::new(Arc::clone(&self.connector), server_name)
.map_err(|e| FtpError::SecureError(e.to_string()))?;
let stream = StreamOwned::new(connection, stream);
- Ok(TlsStream { stream })
+ Ok(RustlsStream { stream })
}
}
@@ -42,13 +46,15 @@ impl TlsConnector {
/// Tls stream wrapper. This type is a garbage data type used to impl the drop trait for the tls stream.
/// This allows me to keep returning `Read` and `Write` traits in stream methods
#[derive(Debug)]
-pub struct TlsStream {
+pub struct RustlsStream {
stream: StreamOwned,
}
-impl TlsStream {
+impl TlsStream for RustlsStream {
+ type InnerStream = StreamOwned;
+
/// Get underlying tcp stream
- pub(crate) fn tcp_stream(self) -> TcpStream {
+ fn tcp_stream(self) -> TcpStream {
let mut stream = self.get_ref().try_clone().unwrap();
// flush stream (otherwise can cause bad chars on channel)
if let Err(err) = stream.flush() {
@@ -59,12 +65,12 @@ impl TlsStream {
}
/// Get ref to underlying tcp stream
- pub(crate) fn get_ref(&self) -> &TcpStream {
+ fn get_ref(&self) -> &TcpStream {
self.stream.get_ref()
}
/// Get mutable reference to tls stream
- pub(crate) fn mut_ref(&mut self) -> &mut StreamOwned {
+ fn mut_ref(&mut self) -> &mut Self::InnerStream {
&mut self.stream
}
}