Remove tokio-native-tls
This commit is contained in:
parent
c59daba22e
commit
9c9431be30
@ -1,6 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"tokio-native-tls",
|
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tokio-rustls/examples/client",
|
"tokio-rustls/examples/client",
|
||||||
"tokio-rustls/examples/server"
|
"tokio-rustls/examples/server"
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
# 0.3.0 (December 23, 2020)
|
|
||||||
|
|
||||||
- Upgrade to `tokio 1.0`.
|
|
||||||
|
|
||||||
# 0.2.0 (October 16, 2020)
|
|
||||||
|
|
||||||
- Upgrade to `tokio 0.3`.
|
|
||||||
|
|
||||||
# 0.1.0 (January 9th, 2019)
|
|
||||||
|
|
||||||
- Initial release from `tokio-tls 0.3`
|
|
@ -1,41 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "tokio-native-tls"
|
|
||||||
# When releasing to crates.io:
|
|
||||||
# - Remove path dependencies
|
|
||||||
# - Update html_root_url.
|
|
||||||
# - Update doc url
|
|
||||||
# - Cargo.toml
|
|
||||||
# - README.md
|
|
||||||
# - Update CHANGELOG.md.
|
|
||||||
# - Create "v0.1.x" git tag.
|
|
||||||
version = "0.3.1"
|
|
||||||
edition = "2018"
|
|
||||||
authors = ["Tokio Contributors <team@tokio.rs>"]
|
|
||||||
license = "MIT"
|
|
||||||
repository = "https://github.com/tokio-rs/tls"
|
|
||||||
homepage = "https://tokio.rs"
|
|
||||||
documentation = "https://docs.rs/tokio-native-tls"
|
|
||||||
description = """
|
|
||||||
An implementation of TLS/SSL streams for Tokio using native-tls giving an implementation of TLS
|
|
||||||
for nonblocking I/O streams.
|
|
||||||
"""
|
|
||||||
categories = ["asynchronous", "network-programming"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
native-tls = "0.2"
|
|
||||||
tokio = "1.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
vendored = ["native-tls/vendored"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
tokio = { version = "1.0", features = ["macros", "rt", "rt-multi-thread", "io-util", "net"] }
|
|
||||||
cfg-if = "1.0"
|
|
||||||
env_logger = { version = "0.10", default-features = false }
|
|
||||||
futures = { version = "0.3.0", features = ["async-await"] }
|
|
||||||
|
|
||||||
tempfile = "3.1"
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
@ -1,25 +0,0 @@
|
|||||||
Copyright (c) 2019 Tokio Contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
|
||||||
person obtaining a copy of this software and associated
|
|
||||||
documentation files (the "Software"), to deal in the
|
|
||||||
Software without restriction, including without
|
|
||||||
limitation the rights to use, copy, modify, merge,
|
|
||||||
publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software
|
|
||||||
is furnished to do so, subject to the following
|
|
||||||
conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice
|
|
||||||
shall be included in all copies or substantial portions
|
|
||||||
of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
||||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
||||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
||||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
||||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
@ -1,18 +0,0 @@
|
|||||||
# tokio-native-tls
|
|
||||||
[![github actions](https://github.com/tokio-rs/tls/workflows/CI/badge.svg)](https://github.com/tokio-rs/tls/actions)
|
|
||||||
[![crates](https://img.shields.io/crates/v/tokio-native-tls.svg)](https://crates.io/crates/tokio-native-tls)
|
|
||||||
[![license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/tokio-rs/tls/blob/master/tokio-native-tls/LICENSE)
|
|
||||||
[![docs.rs](https://docs.rs/tokio-native-tls/badge.svg)](https://docs.rs/tokio-native-tls/)
|
|
||||||
|
|
||||||
An implementation of TLS/SSL streams for Tokio built on top of the
|
|
||||||
[`native-tls`](https://crates.io/crates/native-tls) crate.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is licensed under the [MIT license](./LICENSE).
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
||||||
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
|
|
||||||
terms or conditions.
|
|
@ -1,39 +0,0 @@
|
|||||||
// #![warn(rust_2018_idioms)]
|
|
||||||
|
|
||||||
use native_tls::TlsConnector;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::net::ToSocketAddrs;
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
use tokio::net::TcpStream;
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
|
||||||
let addr = "www.rust-lang.org:443"
|
|
||||||
.to_socket_addrs()?
|
|
||||||
.next()
|
|
||||||
.ok_or("failed to resolve www.rust-lang.org")?;
|
|
||||||
|
|
||||||
let socket = TcpStream::connect(&addr).await?;
|
|
||||||
let cx = TlsConnector::builder().build()?;
|
|
||||||
let cx = tokio_native_tls::TlsConnector::from(cx);
|
|
||||||
|
|
||||||
let mut socket = cx.connect("www.rust-lang.org", socket).await?;
|
|
||||||
|
|
||||||
socket
|
|
||||||
.write_all(
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.0\r\n\
|
|
||||||
Host: www.rust-lang.org\r\n\
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
.as_bytes(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut data = Vec::new();
|
|
||||||
socket.read_to_end(&mut data).await?;
|
|
||||||
|
|
||||||
// println!("data: {:?}", &data);
|
|
||||||
println!("{}", String::from_utf8_lossy(&data[..]));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
|
||||||
|
|
||||||
// A tiny async TLS echo server with Tokio
|
|
||||||
use native_tls::Identity;
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
use tokio::net::TcpListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
an example to setup a tls server.
|
|
||||||
how to test:
|
|
||||||
wget https://127.0.0.1:12345 --no-check-certificate
|
|
||||||
*/
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
// Bind the server's socket
|
|
||||||
let addr = "127.0.0.1:12345".to_string();
|
|
||||||
let tcp: TcpListener = TcpListener::bind(&addr).await?;
|
|
||||||
|
|
||||||
// Create the TLS acceptor.
|
|
||||||
let der = include_bytes!("identity.p12");
|
|
||||||
let cert = Identity::from_pkcs12(der, "mypass")?;
|
|
||||||
let tls_acceptor =
|
|
||||||
tokio_native_tls::TlsAcceptor::from(native_tls::TlsAcceptor::builder(cert).build()?);
|
|
||||||
loop {
|
|
||||||
// Asynchronously wait for an inbound socket.
|
|
||||||
let (socket, remote_addr) = tcp.accept().await?;
|
|
||||||
let tls_acceptor = tls_acceptor.clone();
|
|
||||||
println!("accept connection from {}", remote_addr);
|
|
||||||
tokio::spawn(async move {
|
|
||||||
// Accept the TLS connection.
|
|
||||||
let mut tls_stream = tls_acceptor.accept(socket).await.expect("accept error");
|
|
||||||
// In a loop, read data from the socket and write the data back.
|
|
||||||
|
|
||||||
let mut buf = [0; 1024];
|
|
||||||
let n = tls_stream
|
|
||||||
.read(&mut buf)
|
|
||||||
.await
|
|
||||||
.expect("failed to read data from socket");
|
|
||||||
|
|
||||||
if n == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println!("read={}", unsafe {
|
|
||||||
String::from_utf8_unchecked(buf[0..n].into())
|
|
||||||
});
|
|
||||||
tls_stream
|
|
||||||
.write_all(&buf[0..n])
|
|
||||||
.await
|
|
||||||
.expect("failed to write data to socket");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,384 +0,0 @@
|
|||||||
#![doc(html_root_url = "https://docs.rs/tokio-native-tls/0.3.0")]
|
|
||||||
#![warn(
|
|
||||||
missing_debug_implementations,
|
|
||||||
missing_docs,
|
|
||||||
rust_2018_idioms,
|
|
||||||
unreachable_pub
|
|
||||||
)]
|
|
||||||
#![deny(rustdoc::broken_intra_doc_links)]
|
|
||||||
#![doc(test(
|
|
||||||
no_crate_inject,
|
|
||||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
|
||||||
))]
|
|
||||||
|
|
||||||
//! Async TLS streams
|
|
||||||
//!
|
|
||||||
//! This library is an implementation of TLS streams using the most appropriate
|
|
||||||
//! system library by default for negotiating the connection. That is, on
|
|
||||||
//! Windows this library uses SChannel, on OSX it uses SecureTransport, and on
|
|
||||||
//! other platforms it uses OpenSSL.
|
|
||||||
//!
|
|
||||||
//! Each TLS stream implements the `Read` and `Write` traits to interact and
|
|
||||||
//! interoperate with the rest of the futures I/O ecosystem. Client connections
|
|
||||||
//! initiated from this crate verify hostnames automatically and by default.
|
|
||||||
//!
|
|
||||||
//! This crate primarily exports this ability through two newtypes,
|
|
||||||
//! `TlsConnector` and `TlsAcceptor`. These newtypes augment the
|
|
||||||
//! functionality provided by the `native-tls` crate, on which this crate is
|
|
||||||
//! built. Configuration of TLS parameters is still primarily done through the
|
|
||||||
//! `native-tls` crate.
|
|
||||||
|
|
||||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
|
||||||
|
|
||||||
use crate::native_tls::{Error, HandshakeError, MidHandshakeTlsStream};
|
|
||||||
use std::fmt;
|
|
||||||
use std::future::Future;
|
|
||||||
use std::io::{self, Read, Write};
|
|
||||||
use std::marker::Unpin;
|
|
||||||
#[cfg(unix)]
|
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
|
||||||
#[cfg(windows)]
|
|
||||||
use std::os::windows::io::{AsRawSocket, RawSocket};
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::ptr::null_mut;
|
|
||||||
use std::task::{Context, Poll};
|
|
||||||
|
|
||||||
/// An intermediate wrapper for the inner stream `S`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AllowStd<S> {
|
|
||||||
inner: S,
|
|
||||||
context: *mut (),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> AllowStd<S> {
|
|
||||||
/// Returns a shared reference to the inner stream.
|
|
||||||
pub fn get_ref(&self) -> &S {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the inner stream.
|
|
||||||
pub fn get_mut(&mut self) -> &mut S {
|
|
||||||
&mut self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper around an underlying raw stream which implements the TLS or SSL
|
|
||||||
/// protocol.
|
|
||||||
///
|
|
||||||
/// A `TlsStream<S>` represents a handshake that has been completed successfully
|
|
||||||
/// and both the server and the client are ready for receiving and sending
|
|
||||||
/// data. Bytes read from a `TlsStream` are decrypted from `S` and bytes written
|
|
||||||
/// to a `TlsStream` are encrypted when passing through to `S`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TlsStream<S>(native_tls::TlsStream<AllowStd<S>>);
|
|
||||||
|
|
||||||
/// A wrapper around a `native_tls::TlsConnector`, providing an async `connect`
|
|
||||||
/// method.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TlsConnector(native_tls::TlsConnector);
|
|
||||||
|
|
||||||
/// A wrapper around a `native_tls::TlsAcceptor`, providing an async `accept`
|
|
||||||
/// method.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TlsAcceptor(native_tls::TlsAcceptor);
|
|
||||||
|
|
||||||
struct MidHandshake<S>(Option<MidHandshakeTlsStream<AllowStd<S>>>);
|
|
||||||
|
|
||||||
enum StartedHandshake<S> {
|
|
||||||
Done(TlsStream<S>),
|
|
||||||
Mid(MidHandshakeTlsStream<AllowStd<S>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StartedHandshakeFuture<F, S>(Option<StartedHandshakeFutureInner<F, S>>);
|
|
||||||
struct StartedHandshakeFutureInner<F, S> {
|
|
||||||
f: F,
|
|
||||||
stream: S,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Guard<'a, S>(&'a mut TlsStream<S>)
|
|
||||||
where
|
|
||||||
AllowStd<S>: Read + Write;
|
|
||||||
|
|
||||||
impl<S> Drop for Guard<'_, S>
|
|
||||||
where
|
|
||||||
AllowStd<S>: Read + Write,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
(self.0).0.get_mut().context = null_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// *mut () context is neither Send nor Sync
|
|
||||||
unsafe impl<S: Send> Send for AllowStd<S> {}
|
|
||||||
unsafe impl<S: Sync> Sync for AllowStd<S> {}
|
|
||||||
|
|
||||||
impl<S> AllowStd<S>
|
|
||||||
where
|
|
||||||
S: Unpin,
|
|
||||||
{
|
|
||||||
fn with_context<F, R>(&mut self, f: F) -> io::Result<R>
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Context<'_>, Pin<&mut S>) -> Poll<io::Result<R>>,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
assert!(!self.context.is_null());
|
|
||||||
let waker = &mut *(self.context as *mut _);
|
|
||||||
match f(waker, Pin::new(&mut self.inner)) {
|
|
||||||
Poll::Ready(r) => r,
|
|
||||||
Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Read for AllowStd<S>
|
|
||||||
where
|
|
||||||
S: AsyncRead + Unpin,
|
|
||||||
{
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
let mut buf = ReadBuf::new(buf);
|
|
||||||
self.with_context(|ctx, stream| stream.poll_read(ctx, &mut buf))?;
|
|
||||||
Ok(buf.filled().len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Write for AllowStd<S>
|
|
||||||
where
|
|
||||||
S: AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.with_context(|ctx, stream| stream.poll_write(ctx, buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
self.with_context(|ctx, stream| stream.poll_flush(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> TlsStream<S> {
|
|
||||||
fn with_context<F, R>(&mut self, ctx: &mut Context<'_>, f: F) -> Poll<io::Result<R>>
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut native_tls::TlsStream<AllowStd<S>>) -> io::Result<R>,
|
|
||||||
AllowStd<S>: Read + Write,
|
|
||||||
{
|
|
||||||
self.0.get_mut().context = ctx as *mut _ as *mut ();
|
|
||||||
let g = Guard(self);
|
|
||||||
match f(&mut (g.0).0) {
|
|
||||||
Ok(v) => Poll::Ready(Ok(v)),
|
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending,
|
|
||||||
Err(e) => Poll::Ready(Err(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a shared reference to the inner stream.
|
|
||||||
pub fn get_ref(&self) -> &native_tls::TlsStream<AllowStd<S>> {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the inner stream.
|
|
||||||
pub fn get_mut(&mut self) -> &mut native_tls::TlsStream<AllowStd<S>> {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> AsyncRead for TlsStream<S>
|
|
||||||
where
|
|
||||||
S: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
fn poll_read(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
ctx: &mut Context<'_>,
|
|
||||||
buf: &mut ReadBuf<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
self.with_context(ctx, |s| {
|
|
||||||
let n = s.read(buf.initialize_unfilled())?;
|
|
||||||
buf.advance(n);
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> AsyncWrite for TlsStream<S>
|
|
||||||
where
|
|
||||||
S: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
fn poll_write(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
ctx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
self.with_context(ctx, |s| s.write(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
self.with_context(ctx, |s| s.flush())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
self.with_context(ctx, |s| s.shutdown())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
impl<S> AsRawFd for TlsStream<S>
|
|
||||||
where
|
|
||||||
S: AsRawFd,
|
|
||||||
{
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.get_ref().get_ref().get_ref().as_raw_fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
impl<S> AsRawSocket for TlsStream<S>
|
|
||||||
where
|
|
||||||
S: AsRawSocket,
|
|
||||||
{
|
|
||||||
fn as_raw_socket(&self) -> RawSocket {
|
|
||||||
self.get_ref().get_ref().get_ref().as_raw_socket()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handshake<F, S>(f: F, stream: S) -> Result<TlsStream<S>, Error>
|
|
||||||
where
|
|
||||||
F: FnOnce(
|
|
||||||
AllowStd<S>,
|
|
||||||
) -> Result<native_tls::TlsStream<AllowStd<S>>, HandshakeError<AllowStd<S>>>
|
|
||||||
+ Unpin,
|
|
||||||
S: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
let start = StartedHandshakeFuture(Some(StartedHandshakeFutureInner { f, stream }));
|
|
||||||
|
|
||||||
match start.await {
|
|
||||||
Err(e) => Err(e),
|
|
||||||
Ok(StartedHandshake::Done(s)) => Ok(s),
|
|
||||||
Ok(StartedHandshake::Mid(s)) => MidHandshake(Some(s)).await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F, S> Future for StartedHandshakeFuture<F, S>
|
|
||||||
where
|
|
||||||
F: FnOnce(
|
|
||||||
AllowStd<S>,
|
|
||||||
) -> Result<native_tls::TlsStream<AllowStd<S>>, HandshakeError<AllowStd<S>>>
|
|
||||||
+ Unpin,
|
|
||||||
S: Unpin,
|
|
||||||
AllowStd<S>: Read + Write,
|
|
||||||
{
|
|
||||||
type Output = Result<StartedHandshake<S>, Error>;
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
ctx: &mut Context<'_>,
|
|
||||||
) -> Poll<Result<StartedHandshake<S>, Error>> {
|
|
||||||
let inner = self.0.take().expect("future polled after completion");
|
|
||||||
let stream = AllowStd {
|
|
||||||
inner: inner.stream,
|
|
||||||
context: ctx as *mut _ as *mut (),
|
|
||||||
};
|
|
||||||
|
|
||||||
match (inner.f)(stream) {
|
|
||||||
Ok(mut s) => {
|
|
||||||
s.get_mut().context = null_mut();
|
|
||||||
Poll::Ready(Ok(StartedHandshake::Done(TlsStream(s))))
|
|
||||||
}
|
|
||||||
Err(HandshakeError::WouldBlock(mut s)) => {
|
|
||||||
s.get_mut().context = null_mut();
|
|
||||||
Poll::Ready(Ok(StartedHandshake::Mid(s)))
|
|
||||||
}
|
|
||||||
Err(HandshakeError::Failure(e)) => Poll::Ready(Err(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TlsConnector {
|
|
||||||
/// Connects the provided stream with this connector, assuming the provided
|
|
||||||
/// domain.
|
|
||||||
///
|
|
||||||
/// This function will internally call `TlsConnector::connect` to connect
|
|
||||||
/// the stream and returns a future representing the resolution of the
|
|
||||||
/// connection operation. The returned future will resolve to either
|
|
||||||
/// `TlsStream<S>` or `Error` depending if it's successful or not.
|
|
||||||
///
|
|
||||||
/// This is typically used for clients who have already established, for
|
|
||||||
/// example, a TCP connection to a remote server. That stream is then
|
|
||||||
/// provided here to perform the client half of a connection to a
|
|
||||||
/// TLS-powered server.
|
|
||||||
pub async fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, Error>
|
|
||||||
where
|
|
||||||
S: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
handshake(move |s| self.0.connect(domain, s), stream).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for TlsConnector {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("TlsConnector").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<native_tls::TlsConnector> for TlsConnector {
|
|
||||||
fn from(inner: native_tls::TlsConnector) -> TlsConnector {
|
|
||||||
TlsConnector(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TlsAcceptor {
|
|
||||||
/// Accepts a new client connection with the provided stream.
|
|
||||||
///
|
|
||||||
/// This function will internally call `TlsAcceptor::accept` to connect
|
|
||||||
/// the stream and returns a future representing the resolution of the
|
|
||||||
/// connection operation. The returned future will resolve to either
|
|
||||||
/// `TlsStream<S>` or `Error` depending if it's successful or not.
|
|
||||||
///
|
|
||||||
/// This is typically used after a new socket has been accepted from a
|
|
||||||
/// `TcpListener`. That socket is then passed to this function to perform
|
|
||||||
/// the server half of accepting a client connection.
|
|
||||||
pub async fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, Error>
|
|
||||||
where
|
|
||||||
S: AsyncRead + AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
handshake(move |s| self.0.accept(s), stream).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for TlsAcceptor {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("TlsAcceptor").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<native_tls::TlsAcceptor> for TlsAcceptor {
|
|
||||||
fn from(inner: native_tls::TlsAcceptor) -> TlsAcceptor {
|
|
||||||
TlsAcceptor(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: AsyncRead + AsyncWrite + Unpin> Future for MidHandshake<S> {
|
|
||||||
type Output = Result<TlsStream<S>, Error>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let mut_self = self.get_mut();
|
|
||||||
let mut s = mut_self.0.take().expect("future polled after completion");
|
|
||||||
|
|
||||||
s.get_mut().context = cx as *mut _ as *mut ();
|
|
||||||
match s.handshake() {
|
|
||||||
Ok(mut s) => {
|
|
||||||
s.get_mut().context = null_mut();
|
|
||||||
Poll::Ready(Ok(TlsStream(s)))
|
|
||||||
}
|
|
||||||
Err(HandshakeError::WouldBlock(mut s)) => {
|
|
||||||
s.get_mut().context = null_mut();
|
|
||||||
mut_self.0 = Some(s);
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
Err(HandshakeError::Failure(e)) => Poll::Ready(Err(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// re-export native_tls
|
|
||||||
pub mod native_tls {
|
|
||||||
pub use native_tls::*;
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
use native_tls::TlsConnector;
|
|
||||||
use std::io::{self, Error};
|
|
||||||
use std::net::ToSocketAddrs;
|
|
||||||
use tokio::net::TcpStream;
|
|
||||||
|
|
||||||
macro_rules! t {
|
|
||||||
($e:expr) => {
|
|
||||||
match $e {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg_if! {
|
|
||||||
if #[cfg(feature = "force-rustls")] {
|
|
||||||
fn verify_failed(err: &Error, s: &str) {
|
|
||||||
let err = err.to_string();
|
|
||||||
assert!(err.contains(s), "bad error: {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_expired_error(err: &Error) {
|
|
||||||
verify_failed(err, "CertExpired");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_wrong_host(err: &Error) {
|
|
||||||
verify_failed(err, "CertNotValidForName");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_self_signed(err: &Error) {
|
|
||||||
verify_failed(err, "UnknownIssuer");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_untrusted_root(err: &Error) {
|
|
||||||
verify_failed(err, "UnknownIssuer");
|
|
||||||
}
|
|
||||||
} else if #[cfg(any(feature = "force-openssl",
|
|
||||||
all(not(target_os = "macos"),
|
|
||||||
not(target_os = "windows"),
|
|
||||||
not(target_os = "ios"))))] {
|
|
||||||
fn verify_failed(err: &Error) {
|
|
||||||
assert!(format!("{}", err).contains("certificate verify failed"))
|
|
||||||
}
|
|
||||||
|
|
||||||
use verify_failed as assert_expired_error;
|
|
||||||
use verify_failed as assert_wrong_host;
|
|
||||||
use verify_failed as assert_self_signed;
|
|
||||||
use verify_failed as assert_untrusted_root;
|
|
||||||
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
|
|
||||||
|
|
||||||
fn assert_invalid_cert_chain(err: &Error) {
|
|
||||||
assert!(format!("{}", err).contains("was not trusted."))
|
|
||||||
}
|
|
||||||
|
|
||||||
use crate::assert_invalid_cert_chain as assert_expired_error;
|
|
||||||
use crate::assert_invalid_cert_chain as assert_wrong_host;
|
|
||||||
use crate::assert_invalid_cert_chain as assert_self_signed;
|
|
||||||
use crate::assert_invalid_cert_chain as assert_untrusted_root;
|
|
||||||
} else {
|
|
||||||
fn assert_expired_error(err: &Error) {
|
|
||||||
let s = err.to_string();
|
|
||||||
assert!(s.contains("system clock"), "error = {:?}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_wrong_host(err: &Error) {
|
|
||||||
let s = err.to_string();
|
|
||||||
assert!(s.contains("CN name"), "error = {:?}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_self_signed(err: &Error) {
|
|
||||||
let s = err.to_string();
|
|
||||||
assert!(s.contains("root certificate which is not trusted"), "error = {:?}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
use assert_self_signed as assert_untrusted_root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_host(host: &'static str) -> Error {
|
|
||||||
drop(env_logger::try_init());
|
|
||||||
|
|
||||||
let addr = format!("{}:443", host);
|
|
||||||
let addr = t!(addr.to_socket_addrs()).next().unwrap();
|
|
||||||
|
|
||||||
let socket = t!(TcpStream::connect(&addr).await);
|
|
||||||
let builder = TlsConnector::builder();
|
|
||||||
let cx = t!(builder.build());
|
|
||||||
let cx = tokio_native_tls::TlsConnector::from(cx);
|
|
||||||
let res = cx
|
|
||||||
.connect(host, socket)
|
|
||||||
.await
|
|
||||||
.map_err(|e| Error::new(io::ErrorKind::Other, e));
|
|
||||||
|
|
||||||
assert!(res.is_err());
|
|
||||||
res.err().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn expired() {
|
|
||||||
assert_expired_error(&get_host("expired.badssl.com").await)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: the OSX builders on Travis apparently fail this tests spuriously?
|
|
||||||
// passes locally though? Seems... bad!
|
|
||||||
#[tokio::test]
|
|
||||||
#[cfg_attr(all(target_os = "macos", feature = "force-openssl"), ignore)]
|
|
||||||
async fn wrong_host() {
|
|
||||||
assert_wrong_host(&get_host("wrong.host.badssl.com").await)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn self_signed() {
|
|
||||||
assert_self_signed(&get_host("self-signed.badssl.com").await)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn untrusted_root() {
|
|
||||||
assert_untrusted_root(&get_host("untrusted-root.badssl.com").await)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,99 +0,0 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
use native_tls::TlsConnector;
|
|
||||||
use std::io;
|
|
||||||
use std::net::ToSocketAddrs;
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
use tokio::net::TcpStream;
|
|
||||||
|
|
||||||
macro_rules! t {
|
|
||||||
($e:expr) => {
|
|
||||||
match $e {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg_if! {
|
|
||||||
if #[cfg(feature = "force-rustls")] {
|
|
||||||
fn assert_bad_hostname_error(err: &io::Error) {
|
|
||||||
let err = err.to_string();
|
|
||||||
assert!(err.contains("CertNotValidForName"), "bad error: {}", err);
|
|
||||||
}
|
|
||||||
} else if #[cfg(any(feature = "force-openssl",
|
|
||||||
all(not(target_os = "macos"),
|
|
||||||
not(target_os = "windows"),
|
|
||||||
not(target_os = "ios"))))] {
|
|
||||||
fn assert_bad_hostname_error(err: &io::Error) {
|
|
||||||
let err = err.get_ref().unwrap();
|
|
||||||
let err = err.downcast_ref::<native_tls::Error>().unwrap();
|
|
||||||
assert!(format!("{}", err).contains("certificate verify failed"));
|
|
||||||
}
|
|
||||||
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
|
|
||||||
fn assert_bad_hostname_error(err: &io::Error) {
|
|
||||||
let err = err.get_ref().unwrap();
|
|
||||||
let err = err.downcast_ref::<native_tls::Error>().unwrap();
|
|
||||||
assert!(format!("{}", err).contains("was not trusted."));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fn assert_bad_hostname_error(err: &io::Error) {
|
|
||||||
let err = err.get_ref().unwrap();
|
|
||||||
let err = err.downcast_ref::<native_tls::Error>().unwrap();
|
|
||||||
assert!(format!("{}", err).contains("CN name"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn fetch_google() {
|
|
||||||
drop(env_logger::try_init());
|
|
||||||
|
|
||||||
// First up, resolve google.com
|
|
||||||
let addr = t!("google.com:443".to_socket_addrs()).next().unwrap();
|
|
||||||
|
|
||||||
let socket = TcpStream::connect(&addr).await.unwrap();
|
|
||||||
|
|
||||||
// Send off the request by first negotiating an SSL handshake, then writing
|
|
||||||
// of our request, then flushing, then finally read off the response.
|
|
||||||
let builder = TlsConnector::builder();
|
|
||||||
let connector = t!(builder.build());
|
|
||||||
let connector = tokio_native_tls::TlsConnector::from(connector);
|
|
||||||
let mut socket = t!(connector.connect("google.com", socket).await);
|
|
||||||
t!(socket.write_all(b"GET / HTTP/1.0\r\n\r\n").await);
|
|
||||||
let mut data = Vec::new();
|
|
||||||
t!(socket.read_to_end(&mut data).await);
|
|
||||||
|
|
||||||
// any response code is fine
|
|
||||||
assert!(data.starts_with(b"HTTP/1.0 "));
|
|
||||||
|
|
||||||
let data = String::from_utf8_lossy(&data);
|
|
||||||
let data = data.trim_end();
|
|
||||||
assert!(data.ends_with("</html>") || data.ends_with("</HTML>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn native2io(e: native_tls::Error) -> io::Error {
|
|
||||||
io::Error::new(io::ErrorKind::Other, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// see comment in bad.rs for ignore reason
|
|
||||||
#[cfg_attr(all(target_os = "macos", feature = "force-openssl"), ignore)]
|
|
||||||
#[tokio::test]
|
|
||||||
async fn wrong_hostname_error() {
|
|
||||||
drop(env_logger::try_init());
|
|
||||||
|
|
||||||
let addr = t!("google.com:443".to_socket_addrs()).next().unwrap();
|
|
||||||
|
|
||||||
let socket = t!(TcpStream::connect(&addr).await);
|
|
||||||
let builder = TlsConnector::builder();
|
|
||||||
let connector = t!(builder.build());
|
|
||||||
let connector = tokio_native_tls::TlsConnector::from(connector);
|
|
||||||
let res = connector
|
|
||||||
.connect("rust-lang.org", socket)
|
|
||||||
.await
|
|
||||||
.map_err(native2io);
|
|
||||||
|
|
||||||
assert!(res.is_err());
|
|
||||||
assert_bad_hostname_error(&res.err().unwrap());
|
|
||||||
}
|
|
Binary file not shown.
Binary file not shown.
@ -1,172 +0,0 @@
|
|||||||
use futures::join;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use native_tls::{Certificate, Identity};
|
|
||||||
use std::{fs, io::Error, path::PathBuf, process::Command};
|
|
||||||
use tokio::{
|
|
||||||
io::{AsyncReadExt, AsyncWrite, AsyncWriteExt},
|
|
||||||
net::{TcpListener, TcpStream},
|
|
||||||
};
|
|
||||||
use tokio_native_tls::{TlsAcceptor, TlsConnector};
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref CERT_DIR: PathBuf = {
|
|
||||||
if cfg!(unix) {
|
|
||||||
let dir = tempfile::TempDir::new().unwrap();
|
|
||||||
let path = dir.path().to_str().unwrap();
|
|
||||||
|
|
||||||
Command::new("sh")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(format!("./scripts/generate-certificate.sh {}", path))
|
|
||||||
.output()
|
|
||||||
.expect("failed to execute process");
|
|
||||||
|
|
||||||
dir.into_path()
|
|
||||||
} else {
|
|
||||||
PathBuf::from("tests")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn client_to_server() {
|
|
||||||
let srv = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
||||||
let addr = srv.local_addr().unwrap();
|
|
||||||
|
|
||||||
let (server_tls, client_tls) = context();
|
|
||||||
|
|
||||||
// Create a future to accept one socket, connect the ssl stream, and then
|
|
||||||
// read all the data from it.
|
|
||||||
let server = async move {
|
|
||||||
let (socket, _) = srv.accept().await.unwrap();
|
|
||||||
let mut socket = server_tls.accept(socket).await.unwrap();
|
|
||||||
|
|
||||||
// Verify access to all of the nested inner streams (e.g. so that peer
|
|
||||||
// certificates can be accessed). This is just a compile check.
|
|
||||||
let native_tls_stream: &native_tls::TlsStream<_> = socket.get_ref();
|
|
||||||
let _peer_cert = native_tls_stream.peer_certificate().unwrap();
|
|
||||||
let allow_std_stream: &tokio_native_tls::AllowStd<_> = native_tls_stream.get_ref();
|
|
||||||
let _tokio_tcp_stream: &tokio::net::TcpStream = allow_std_stream.get_ref();
|
|
||||||
|
|
||||||
let mut data = Vec::new();
|
|
||||||
socket.read_to_end(&mut data).await.unwrap();
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a future to connect to our server, connect the ssl stream, and
|
|
||||||
// then write a bunch of data to it.
|
|
||||||
let client = async move {
|
|
||||||
let socket = TcpStream::connect(&addr).await.unwrap();
|
|
||||||
let socket = client_tls.connect("foobar.com", socket).await.unwrap();
|
|
||||||
copy_data(socket).await
|
|
||||||
};
|
|
||||||
|
|
||||||
// Finally, run everything!
|
|
||||||
let (data, _) = join!(server, client);
|
|
||||||
// assert_eq!(amt, AMT);
|
|
||||||
assert!(data == vec![9; AMT]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn server_to_client() {
|
|
||||||
// Create a server listening on a port, then figure out what that port is
|
|
||||||
let srv = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
||||||
let addr = srv.local_addr().unwrap();
|
|
||||||
|
|
||||||
let (server_tls, client_tls) = context();
|
|
||||||
|
|
||||||
let server = async move {
|
|
||||||
let (socket, _) = srv.accept().await.unwrap();
|
|
||||||
let socket = server_tls.accept(socket).await.unwrap();
|
|
||||||
copy_data(socket).await
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = async move {
|
|
||||||
let socket = TcpStream::connect(&addr).await.unwrap();
|
|
||||||
let mut socket = client_tls.connect("foobar.com", socket).await.unwrap();
|
|
||||||
let mut data = Vec::new();
|
|
||||||
socket.read_to_end(&mut data).await.unwrap();
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
// Finally, run everything!
|
|
||||||
let (_, data) = join!(server, client);
|
|
||||||
assert!(data == vec![9; AMT]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn one_byte_at_a_time() {
|
|
||||||
const AMT: usize = 1024;
|
|
||||||
|
|
||||||
let srv = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
||||||
let addr = srv.local_addr().unwrap();
|
|
||||||
|
|
||||||
let (server_tls, client_tls) = context();
|
|
||||||
|
|
||||||
let server = async move {
|
|
||||||
let (socket, _) = srv.accept().await.unwrap();
|
|
||||||
let mut socket = server_tls.accept(socket).await.unwrap();
|
|
||||||
let mut amt = 0;
|
|
||||||
for b in std::iter::repeat(9).take(AMT) {
|
|
||||||
let data = [b as u8];
|
|
||||||
socket.write_all(&data).await.unwrap();
|
|
||||||
amt += 1;
|
|
||||||
}
|
|
||||||
amt
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = async move {
|
|
||||||
let socket = TcpStream::connect(&addr).await.unwrap();
|
|
||||||
let mut socket = client_tls.connect("foobar.com", socket).await.unwrap();
|
|
||||||
let mut data = Vec::new();
|
|
||||||
loop {
|
|
||||||
let mut buf = [0; 1];
|
|
||||||
match socket.read_exact(&mut buf).await {
|
|
||||||
Ok(_) => data.extend_from_slice(&buf),
|
|
||||||
Err(ref err) if err.kind() == std::io::ErrorKind::UnexpectedEof => break,
|
|
||||||
Err(err) => panic!("{}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
let (amt, data) = join!(server, client);
|
|
||||||
assert_eq!(amt, AMT);
|
|
||||||
assert!(data == vec![9; AMT as usize]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn context() -> (TlsAcceptor, TlsConnector) {
|
|
||||||
let pkcs12 = fs::read(CERT_DIR.join("identity.p12")).unwrap();
|
|
||||||
let der = fs::read(CERT_DIR.join("root-ca.der")).unwrap();
|
|
||||||
|
|
||||||
let identity = Identity::from_pkcs12(&pkcs12, "mypass").unwrap();
|
|
||||||
let acceptor = native_tls::TlsAcceptor::builder(identity).build().unwrap();
|
|
||||||
|
|
||||||
let cert = Certificate::from_der(&der).unwrap();
|
|
||||||
let connector = native_tls::TlsConnector::builder()
|
|
||||||
.add_root_certificate(cert)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
(acceptor.into(), connector.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
const AMT: usize = 128 * 1024;
|
|
||||||
|
|
||||||
async fn copy_data<W: AsyncWrite + Unpin>(mut w: W) -> Result<usize, Error> {
|
|
||||||
let mut data = vec![9; AMT as usize];
|
|
||||||
let mut amt = 0;
|
|
||||||
while !data.is_empty() {
|
|
||||||
let written = w.write(&data).await?;
|
|
||||||
if written <= data.len() {
|
|
||||||
amt += written;
|
|
||||||
data.resize(data.len() - written, 0);
|
|
||||||
} else {
|
|
||||||
w.write_all(&data).await?;
|
|
||||||
amt += data.len();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("remaining: {}", data.len());
|
|
||||||
}
|
|
||||||
Ok(amt)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user