update fastwebsockets, revert custom asyncread impl

This commit is contained in:
Toshit Chawda 2024-07-10 21:59:17 -07:00
parent 1916a8e7c8
commit 5571a63f40
No known key found for this signature in database
GPG key ID: 91480ED99E2B3D9D
13 changed files with 646 additions and 703 deletions

70
Cargo.lock generated
View file

@ -143,6 +143,16 @@ dependencies = [
"syn",
]
[[package]]
name = "async_io_stream"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c"
dependencies = [
"futures",
"rustc_version",
]
[[package]]
name = "atomic-counter"
version = "1.0.1"
@ -174,7 +184,7 @@ dependencies = [
"futures-util",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.29",
"hyper 0.14.30",
"itoa",
"matchit",
"memchr",
@ -295,9 +305,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.106"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2"
checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8"
[[package]]
name = "certs-grabber"
@ -315,9 +325,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.8"
version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
dependencies = [
"clap_builder",
"clap_derive",
@ -325,9 +335,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.8"
version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
dependencies = [
"anstream",
"anstyle",
@ -511,7 +521,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "epoxy-client"
version = "2.0.5"
version = "2.0.6"
dependencies = [
"async-compression",
"async-trait",
@ -526,7 +536,7 @@ dependencies = [
"getrandom",
"http 1.1.0",
"http-body-util",
"hyper 1.4.0",
"hyper 1.4.1",
"hyper-util-wasm",
"js-sys",
"parking_lot_core",
@ -557,7 +567,7 @@ dependencies = [
"fastwebsockets",
"futures-util",
"http-body-util",
"hyper 1.4.0",
"hyper 1.4.1",
"hyper-util",
"tokio",
"tokio-util",
@ -599,14 +609,14 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "fastwebsockets"
version = "0.7.2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93da8b19e29f202ef35ddd20ddea8c86166850fce5ba2a3c3f3e3174cdbb0620"
checksum = "26da0c7b5cef45c521a6f9cdfffdfeb6c9f5804fbac332deb5ae254634c7a6be"
dependencies = [
"base64 0.21.7",
"bytes",
"http-body-util",
"hyper 1.4.0",
"hyper 1.4.1",
"hyper-util",
"pin-project",
"rand",
@ -962,9 +972,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.29"
version = "0.14.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33"
checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
dependencies = [
"bytes",
"futures-channel",
@ -986,9 +996,9 @@ dependencies = [
[[package]]
name = "hyper"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc"
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
dependencies = [
"bytes",
"futures-channel",
@ -1011,7 +1021,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
dependencies = [
"hyper 0.14.29",
"hyper 0.14.30",
"pin-project-lite",
"tokio",
"tokio-io-timeout",
@ -1027,7 +1037,7 @@ dependencies = [
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
"hyper 1.4.0",
"hyper 1.4.1",
"pin-project-lite",
"tokio",
]
@ -1043,7 +1053,7 @@ dependencies = [
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
"hyper 1.4.0",
"hyper 1.4.1",
"pin-project-lite",
"tower",
"tower-service",
@ -1547,6 +1557,15 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.34"
@ -1653,6 +1672,12 @@ dependencies = [
"libc",
]
[[package]]
name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "send_wrapper"
version = "0.4.0"
@ -1737,7 +1762,7 @@ dependencies = [
"futures",
"http-body-util",
"humantime",
"hyper 1.4.0",
"hyper 1.4.1",
"simple_moving_average",
"tokio",
"tokio-native-tls",
@ -1959,7 +1984,7 @@ dependencies = [
"h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.29",
"hyper 0.14.30",
"hyper-timeout",
"percent-encoding",
"pin-project",
@ -2462,6 +2487,7 @@ name = "wisp-mux"
version = "5.0.0"
dependencies = [
"async-trait",
"async_io_stream",
"bytes",
"dashmap",
"event-listener",

View file

@ -1,6 +1,6 @@
[package]
name = "epoxy-client"
version = "2.0.5"
version = "2.0.6"
edition = "2021"
[lib]
@ -13,7 +13,7 @@ base64 = { version = "0.22.1", optional = true }
bytes = "1.6.0"
cfg-if = "1.0.0"
event-listener = "5.3.1"
fastwebsockets = { version = "0.7.2", features = ["unstable-split"], optional = true }
fastwebsockets = { version = "0.8.0", features = ["unstable-split"], optional = true }
flume = "0.11.0"
futures-rustls = { version = "0.26.0", default-features = false, features = ["tls12", "ring"] }
futures-util = { version = "0.3.30", features = ["sink"] }

View file

@ -196,9 +196,11 @@ onmessage = async (msg) => {
[],
{ "x-header": "abc" },
);
let i = 0;
while (true) {
log("sending `data`");
await ws.send("data");
log(`sending \`data${i}\``);
await ws.send("data"+i);
i++;
await (new Promise((res, _) => setTimeout(res, 10)));
}
} else if (should_tls_test) {

View file

@ -1,6 +1,6 @@
{
"name": "@mercuryworkshop/epoxy-tls",
"version": "2.0.5-1",
"version": "2.0.6-1",
"description": "A wasm library for using raw encrypted tls/ssl/https/websocket streams on the browser",
"scripts": {
"build": "./build.sh"

View file

@ -1,13 +1,12 @@
use std::{pin::Pin, sync::Arc, task::Poll};
use bytes::Bytes;
use futures_rustls::{
rustls::{ClientConfig, RootCertStore},
TlsConnector, TlsStream,
};
use futures_util::{
future::Either,
lock::{Mutex, MutexGuard},
AsyncRead, AsyncWrite, Future,
future::Either, lock::{Mutex, MutexGuard}, AsyncRead, AsyncWrite, Future
};
use hyper_util_wasm::client::legacy::connect::{Connected, Connection};
use js_sys::{Array, Reflect, Uint8Array};
@ -17,7 +16,8 @@ use tower_service::Service;
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::spawn_local;
use wisp_mux::{
extensions::{udp::UdpProtocolExtensionBuilder, ProtocolExtensionBuilder}, ClientMux, MuxStreamAsyncRW, MuxStreamIo, StreamType
extensions::{udp::UdpProtocolExtensionBuilder, ProtocolExtensionBuilder},
ClientMux, IoStream, MuxStreamIo, StreamType,
};
use crate::{ws_wrapper::WebSocketWrapper, EpoxyClientOptions, EpoxyError};
@ -49,7 +49,7 @@ pub struct StreamProvider {
}
pub type ProviderUnencryptedStream = MuxStreamIo;
pub type ProviderUnencryptedAsyncRW = MuxStreamAsyncRW;
pub type ProviderUnencryptedAsyncRW = IoStream<ProviderUnencryptedStream, Bytes>;
pub type ProviderTlsAsyncRW = TlsStream<ProviderUnencryptedAsyncRW>;
pub type ProviderAsyncRW = Either<ProviderTlsAsyncRW, ProviderUnencryptedAsyncRW>;

View file

@ -9,11 +9,24 @@ use http::{HeaderValue, Uri};
use hyper::{body::Body, rt::Executor};
use js_sys::{Array, ArrayBuffer, Object, Reflect, Uint8Array};
use pin_project_lite::pin_project;
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen::{prelude::*, JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use crate::EpoxyError;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console, js_name = log)]
pub fn js_console_log(s: &str);
}
#[macro_export]
macro_rules! console_log {
($($expr:expr),*) => {
$crate::utils::js_console_log(&format!($($expr),*));
};
}
pub trait UriExt {
fn get_redirect(&self, location: &HeaderValue) -> Result<Uri, EpoxyError>;
}

View file

@ -118,7 +118,6 @@ impl EpoxyWebSocket {
);
}
OpCode::Close => {
let _ = onclose.call0(&JsValue::null());
break;
}
// ping/pong/continue

View file

@ -10,7 +10,7 @@ clap = { version = "4.4.18", features = ["derive", "help", "usage", "color", "wr
clio = { version = "0.3.5", features = ["clap-parse"] }
console-subscriber = { version = "0.2.0", optional = true }
dashmap = "5.5.3"
fastwebsockets = { version = "0.7.1", features = ["upgrade", "simdutf8", "unstable-split"] }
fastwebsockets = { version = "0.8.0", features = ["upgrade", "simdutf8", "unstable-split"] }
futures-util = { version = "0.3.30", features = ["sink"] }
http-body-util = "0.1.0"
hyper = { version = "1.1.0", features = ["server", "http1"] }

View file

@ -17,7 +17,7 @@ use hyper_util::rt::TokioIo;
#[cfg(unix)]
use tokio::net::{UnixListener, UnixStream};
use tokio::{
io::{copy, AsyncBufReadExt, AsyncWriteExt},
io::{copy, copy_bidirectional, AsyncBufReadExt, AsyncWriteExt},
net::{lookup_host, TcpListener, TcpStream, UdpSocket},
select,
};
@ -34,7 +34,7 @@ use wisp_mux::{
udp::UdpProtocolExtensionBuilder,
ProtocolExtensionBuilder,
},
CloseReason, ConnectPacket, MuxStream, MuxStreamAsyncRW, ServerMux, StreamType, WispError,
CloseReason, ConnectPacket, MuxStream, IoStream, ServerMux, StreamType, WispError,
};
type HttpBody = http_body_util::Full<hyper::body::Bytes>;
@ -269,6 +269,8 @@ async fn accept_http(
}
}
// re-enable once MuxStreamAsyncRW is fixed
/*
async fn copy_buf(mux: MuxStreamAsyncRW, tcp: TcpStream) -> std::io::Result<()> {
let (muxrx, muxtx) = mux.into_split();
let mut muxrx = muxrx.compat();
@ -300,6 +302,7 @@ async fn copy_buf(mux: MuxStreamAsyncRW, tcp: TcpStream) -> std::io::Result<()>
x = slow_fut => x.map(|_| ()),
}
}
*/
async fn handle_mux(
packet: ConnectPacket,
@ -311,9 +314,9 @@ async fn handle_mux(
);
match packet.stream_type {
StreamType::Tcp => {
let tcp_stream = TcpStream::connect(uri).await?;
let mux = stream.into_io().into_asyncrw();
copy_buf(mux, tcp_stream).await?;
let mut tcp_stream = TcpStream::connect(uri).await?;
let mut mux = stream.into_io().into_asyncrw().compat();
copy_bidirectional(&mut mux, &mut tcp_stream).await?;
}
StreamType::Udp => {
let uri = lookup_host(uri)

View file

@ -8,7 +8,7 @@ atomic-counter = "1.0.1"
bytes = "1.5.0"
clap = { version = "4.5.4", features = ["cargo", "derive"] }
console-subscriber = { version = "0.2.0", optional = true }
fastwebsockets = { version = "0.7.1", features = ["unstable-split", "upgrade"] }
fastwebsockets = { version = "0.8.0", features = ["unstable-split", "upgrade"] }
futures = "0.3.30"
http-body-util = "0.1.0"
humantime = "2.1.0"

View file

@ -10,10 +10,11 @@ edition = "2021"
[dependencies]
async-trait = "0.1.79"
async_io_stream = "0.3.3"
bytes = "1.5.0"
dashmap = { version = "5.5.3", features = ["inline"] }
event-listener = "5.0.0"
fastwebsockets = { version = "0.7.1", features = ["unstable-split"], optional = true }
fastwebsockets = { version = "0.8.0", features = ["unstable-split"], optional = true }
flume = "0.11.0"
futures = "0.3.30"
futures-timer = "3.0.3"

View file

@ -14,6 +14,7 @@ mod stream;
pub mod ws;
pub use crate::{packet::*, stream::*};
pub use async_io_stream::IoStream;
use bytes::Bytes;
use dashmap::DashMap;

View file

@ -4,15 +4,15 @@ use crate::{
CloseReason, Packet, Role, StreamType, WispError,
};
use async_io_stream::IoStream;
use bytes::{BufMut, Bytes, BytesMut};
use event_listener::Event;
use flume as mpsc;
use futures::{
channel::oneshot,
select,
stream::{self, IntoAsyncRead, SplitSink, SplitStream},
select, stream,
task::{Context, Poll},
AsyncBufRead, AsyncRead, AsyncWrite, FutureExt, Sink, Stream, StreamExt, TryStreamExt,
FutureExt, Sink, Stream,
};
use pin_project_lite::pin_project;
use std::{
@ -21,7 +21,6 @@ use std::{
atomic::{AtomicBool, AtomicU32, Ordering},
Arc,
},
task::ready,
};
pub(crate) enum WsEvent {
@ -298,8 +297,12 @@ impl MuxStream {
/// Turn the stream into one that implements futures `Stream + Sink`, consuming it.
pub fn into_io(self) -> MuxStreamIo {
MuxStreamIo {
rx: MuxStreamIoStream {
rx: self.rx.into_stream(),
},
tx: MuxStreamIoSink {
tx: self.tx.into_sink(),
},
}
}
}
@ -361,31 +364,71 @@ pin_project! {
/// Multiplexor stream that implements futures `Stream + Sink`.
pub struct MuxStreamIo {
#[pin]
rx: Pin<Box<dyn Stream<Item = Bytes> + Send>>,
rx: MuxStreamIoStream,
#[pin]
tx: Pin<Box<dyn Sink<Bytes, Error = WispError> + Send>>,
tx: MuxStreamIoSink,
}
}
impl MuxStreamIo {
/// Turn the stream into one that implements futures `AsyncRead + AsyncBufRead + AsyncWrite`.
pub fn into_asyncrw(self) -> MuxStreamAsyncRW {
let (tx, rx) = self.split();
MuxStreamAsyncRW {
rx: MuxStreamAsyncRead::new(rx),
tx: MuxStreamAsyncWrite::new(tx),
pub fn into_asyncrw(self) -> IoStream<MuxStreamIo, Bytes> {
IoStream::new(self)
}
/// Split the stream into read and write parts, consuming it.
pub fn into_split(self) -> (MuxStreamIoStream, MuxStreamIoSink) {
(self.rx, self.tx)
}
}
impl Stream for MuxStreamIo {
type Item = Result<Bytes, std::io::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project().rx.poll_next(cx).map(|x| x.map(Ok))
self.project().rx.poll_next(cx)
}
}
impl Sink<Bytes> for MuxStreamIo {
type Error = std::io::Error;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().tx.poll_ready(cx)
}
fn start_send(self: Pin<&mut Self>, item: Bytes) -> Result<(), Self::Error> {
self.project().tx.start_send(item)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().tx.poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project().tx.poll_close(cx)
}
}
pin_project! {
/// Read side of a multiplexor stream that implements futures `Stream`.
pub struct MuxStreamIoStream {
#[pin]
rx: Pin<Box<dyn Stream<Item = Bytes> + Send>>,
}
}
impl Stream for MuxStreamIoStream {
type Item = Result<Bytes, std::io::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project().rx.poll_next(cx).map(|x| x.map(Ok))
}
}
pin_project! {
/// Write side of a multiplexor stream that implements futures `Sink`.
pub struct MuxStreamIoSink {
#[pin]
tx: Pin<Box<dyn Sink<Bytes, Error = WispError> + Send>>,
}
}
impl Sink<Bytes> for MuxStreamIoSink {
type Error = std::io::Error;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.project()
@ -412,148 +455,3 @@ impl Sink<Bytes> for MuxStreamIo {
.map_err(std::io::Error::other)
}
}
pin_project! {
/// Multiplexor stream that implements futures `AsyncRead + AsyncBufRead + AsyncWrite`.
pub struct MuxStreamAsyncRW {
#[pin]
rx: MuxStreamAsyncRead,
#[pin]
tx: MuxStreamAsyncWrite,
}
}
impl MuxStreamAsyncRW {
/// Split the stream into read and write parts, consuming it.
pub fn into_split(self) -> (MuxStreamAsyncRead, MuxStreamAsyncWrite) {
(self.rx, self.tx)
}
}
impl AsyncRead for MuxStreamAsyncRW {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<std::io::Result<usize>> {
self.project().rx.poll_read(cx, buf)
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [std::io::IoSliceMut<'_>],
) -> Poll<std::io::Result<usize>> {
self.project().rx.poll_read_vectored(cx, bufs)
}
}
impl AsyncBufRead for MuxStreamAsyncRW {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<&[u8]>> {
self.project().rx.poll_fill_buf(cx)
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.project().rx.consume(amt)
}
}
impl AsyncWrite for MuxStreamAsyncRW {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
self.project().tx.poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
self.project().tx.poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
self.project().tx.poll_close(cx)
}
}
pin_project! {
/// Read side of a multiplexor stream that implements futures `AsyncRead + AsyncBufRead`.
pub struct MuxStreamAsyncRead {
#[pin]
rx: IntoAsyncRead<SplitStream<MuxStreamIo>>,
}
}
impl MuxStreamAsyncRead {
pub(crate) fn new(stream: SplitStream<MuxStreamIo>) -> Self {
Self {
rx: stream.into_async_read(),
}
}
}
impl AsyncRead for MuxStreamAsyncRead {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<std::io::Result<usize>> {
self.project().rx.poll_read(cx, buf)
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [std::io::IoSliceMut<'_>],
) -> Poll<std::io::Result<usize>> {
self.project().rx.poll_read_vectored(cx, bufs)
}
}
impl AsyncBufRead for MuxStreamAsyncRead {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<&[u8]>> {
self.project().rx.poll_fill_buf(cx)
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.project().rx.consume(amt)
}
}
pin_project! {
/// Write side of a multiplexor stream that implements futures `AsyncWrite`.
pub struct MuxStreamAsyncWrite {
#[pin]
tx: SplitSink<MuxStreamIo, Bytes>,
}
}
impl MuxStreamAsyncWrite {
pub(crate) fn new(sink: SplitSink<MuxStreamIo, Bytes>) -> Self {
Self { tx: sink }
}
}
impl AsyncWrite for MuxStreamAsyncWrite {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
let mut this = self.project();
ready!(this.tx.as_mut().poll_ready(cx))?;
match this.tx.start_send(Bytes::copy_from_slice(buf)) {
Ok(()) => Poll::Ready(Ok(buf.len())),
Err(e) => Poll::Ready(Err(e)),
}
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
self.project().tx.poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
self.project().tx.poll_close(cx)
}
}