diff --git a/Cargo.lock b/Cargo.lock index cd9669c..030b87e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,7 +568,7 @@ dependencies = [ [[package]] name = "oblivion" -version = "1.3.0" +version = "2.0.0" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 513844d..589cf28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oblivion" -version = "1.3.0" +version = "2.0.0" authors = ["苏向夜 "] description = "Rust High Concurrency Implementation of Oblivion, an End-to-End Encryption Protocol Based on ECDHE Encryption Algorithm" edition = "2021" diff --git a/src/api.rs b/src/api.rs index a051cca..5017591 100644 --- a/src/api.rs +++ b/src/api.rs @@ -25,35 +25,29 @@ pub async fn request( olps: &str, data: Option, file: Option>, - tfo: bool, ) -> Result { let session = Session::new(); session - .request(method.to_string(), olps.to_string(), data, file, tfo) + .request(method.to_string(), olps.to_string(), data, file) .await } /// GET method -pub async fn get(olps: &str, tfo: bool) -> Result { - request("get", olps, None, None, tfo).await +pub async fn get(olps: &str) -> Result { + request("get", olps, None, None).await } /// POST method -pub async fn post(olps: &str, data: Value, tfo: bool) -> Result { - request("post", olps, Some(data), None, tfo).await +pub async fn post(olps: &str, data: Value) -> Result { + request("post", olps, Some(data), None).await } /// PUT method -pub async fn put(olps: &str, data: Option, file: Vec, tfo: bool) -> Result { - request("put", olps, data, Some(file), tfo).await +pub async fn put(olps: &str, data: Option, file: Vec) -> Result { + request("put", olps, data, Some(file)).await } -#[deprecated(since = "1.0.0", note = "FORWARD method may no longer supported.")] -pub async fn forward( - olps: &str, - data: Option, - file: Vec, - tfo: bool, -) -> Result { - request("forward", olps, data, Some(file), tfo).await +#[deprecated(since = "1.0.0", note = "FORWARD method is no longer supported.")] +pub async fn forward(olps: &str, data: Option, file: Vec) -> Result { + request("forward", olps, data, Some(file)).await } diff --git a/src/bin/main.rs b/src/bin/main.rs index 2df4755..bb635dd 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -3,15 +3,15 @@ use oblivion::api::get; use oblivion::models::render::{BaseResponse, Response}; use oblivion::models::router::{RoutePath, RouteType, Router}; use oblivion::models::server::Server; +use oblivion::models::session::Session; use oblivion::path_route; -use oblivion::utils::parser::OblivionRequest; use oblivion_codegen::async_route; use serde_json::json; use std::env::args; use std::time::Instant; #[async_route] -fn handler(mut _req: OblivionRequest) -> Response { +fn handler(_sess: &mut Session) -> Response { Ok(BaseResponse::TextResponse( "每一个人都应该拥有守护信息与获得真实信息的神圣权利, 任何与之对抗的都是我们的敌人" .to_string(), @@ -20,21 +20,29 @@ fn handler(mut _req: OblivionRequest) -> Response { } #[async_route] -fn welcome(mut req: OblivionRequest) -> Response { +fn welcome(_sess: &mut Session) -> Response { Ok(BaseResponse::TextResponse( - format!("欢迎进入信息绝对安全区, 来自[{}]的朋友", req.get_ip()), + format!("欢迎进入信息绝对安全区, 来自[{}]的朋友", 1), 200, )) } #[async_route] -fn json(_req: OblivionRequest) -> Response { +fn json(_sess: &mut Session) -> Response { Ok(BaseResponse::JsonResponse( json!({"status": true, "msg": "只身堕入极暗之永夜, 以期再世涅槃之阳光"}), 200, )) } +#[async_route] +async fn alive(mut _sess: Session) -> Response { + Ok(BaseResponse::JsonResponse( + json!({"status": true, "msg": "结束"}), + 200, + )) +} + #[tokio::main] async fn main() -> Result<()> { let args: Vec = args().collect(); @@ -42,7 +50,7 @@ async fn main() -> Result<()> { if !is_server { loop { let now = Instant::now(); - get("127.0.0.1:7076/path", true).await.unwrap(); + get("127.0.0.1:7076/path").await?; println!("执行时间: {}", now.elapsed().as_millis()); } } else { diff --git a/src/models/client.rs b/src/models/client.rs index 303c940..a5fd05b 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -1,5 +1,5 @@ //! # Oblivion Client -use crate::models::packet::{OED, OKE, OSC}; +use crate::models::packet::{OED, OSC}; use crate::exceptions::OblivionException; #[cfg(feature = "python")] @@ -7,7 +7,7 @@ use crate::exceptions::PyOblivionException; use crate::utils::gear::Socket; use crate::utils::generator::generate_key_pair; -use crate::utils::parser::{length, Oblivion, OblivionPath}; +use crate::utils::parser::{Oblivion, OblivionPath}; use anyhow::{Error, Result}; use p256::ecdh::EphemeralSecret; @@ -17,11 +17,14 @@ use tokio::net::TcpStream; #[cfg(feature = "python")] use pyo3::prelude::*; #[cfg(not(feature = "python"))] -use serde_json::{from_str, json, Value}; +use serde_json::{from_str, Value}; #[cfg(feature = "python")] use serde_json::{json, Value}; +use super::session::Session; + #[cfg_attr(feature = "python", pyclass)] +#[derive(Debug, Default)] pub struct Response { #[cfg_attr(feature = "python", pyo3(get))] pub header: String, @@ -92,47 +95,29 @@ impl Response { } } -pub struct Request { - method: String, +pub struct Client { olps: String, path: OblivionPath, - data: Option, - file: Option>, - tfo: bool, plain_text: String, - prepared: bool, private_key: Option, public_key: Option, - aes_key: Option>, - tcp: Option, + session: Option, } -impl Request { - pub fn new( - method: String, - olps: String, - data: Option, - file: Option>, - tfo: bool, - ) -> Result { +impl Client { + pub fn new(method: String, olps: String) -> Result { let method = method.to_uppercase(); let path = OblivionPath::new(&olps)?; let olps = path.get_olps(); let oblivion = Oblivion::new(&method, &olps); let plain_text = oblivion.plain_text(); Ok(Self { - method, olps, path, - data, - file, - tfo, - plain_text, - prepared: false, + plain_text: plain_text.clone(), private_key: None, public_key: None, - aes_key: None, - tcp: None, + session: None, }) } @@ -150,96 +135,48 @@ impl Request { } Err(_) => return Err(Error::from(OblivionException::ConnectionRefusedError)), }; - self.tcp = Some(Socket::new(tcp)); - - if self.tfo { - // 在这里启用TCP Fast Open - }; - self.send_header().await?; + self.session = Some(Session::new_with_header( + &self.plain_text, + Socket::new(tcp), + )?); - let mut oke = OKE::new(Some(&self.private_key.as_ref().unwrap()), self.public_key)?; - oke.from_stream_with_salt(self.tcp.as_mut().unwrap()) - .await?; - self.aes_key = Some(oke.get_aes_key()); - oke.to_stream(self.tcp.as_mut().unwrap()).await?; + let session = self.session.as_mut().unwrap(); + session.handshake(0).await?; - self.prepared = true; Ok(()) } - pub async fn send_header(&mut self) -> Result<()> { - let tcp = self.tcp.as_mut().unwrap(); - let header = self.plain_text.as_bytes().to_vec(); - tcp.send(&length(&header)?).await?; - tcp.send(&header).await?; + pub async fn send(&mut self, bytes: Vec) -> Result<()> { + let session = self.session.as_mut().unwrap(); + let tcp = &mut session.socket; + let mut oed = OED::new(session.aes_key.clone()); + oed.from_bytes(bytes)?; + oed.to_stream(tcp, 5).await?; Ok(()) } - pub async fn send(&mut self) -> Result<()> { - if self.method == "GET" { - return Ok(()); - }; - - let tcp = self.tcp.as_mut().unwrap(); - let mut oed = if self.method == "POST" { - if self.data.is_none() { - let mut oed = OED::new(self.aes_key.clone()); - oed.from_dict(json!({}))?; - oed - } else { - let mut oed = OED::new(self.aes_key.clone()); - oed.from_dict(self.data.clone().unwrap())?; - oed - } - } else if self.method == "PUT" { - let mut oed = if self.data.is_none() { - let mut oed = OED::new(self.aes_key.clone()); - oed.from_dict(json!({}))?; - oed - } else { - let mut oed = OED::new(self.aes_key.clone()); - oed.from_dict(self.data.clone().unwrap())?; - oed - }; + pub async fn recv(&mut self) -> Result { + let session = self.session.as_mut().unwrap(); + let tcp = &mut session.socket; - oed.to_stream(tcp, 5).await?; + let flag = OSC::from_stream(tcp).await?; - let mut oed = OED::new(self.aes_key.clone()); - oed.from_bytes(self.file.clone().unwrap())?; - oed - } else { - return Err(Error::from(OblivionException::UnsupportedMethod { - method: self.method.to_string(), - })); - }; + let mut oed = OED::new(session.aes_key.clone()); + oed.from_stream(tcp, 5).await?; - oed.to_stream(tcp, 5).await?; - Ok(()) - } + let osc = OSC::from_stream(tcp).await?; - pub async fn recv(&mut self) -> Result { - let tcp = self.tcp.as_mut().unwrap(); - - if !self.prepared { - Err(Error::from(OblivionException::ErrorNotPrepared)) - } else { - let mut oed = OED::new(self.aes_key.clone()); - oed.from_stream(tcp, 5).await?; - - let osc = OSC::from_stream(tcp).await?; - - let response = Response::new( - self.plain_text.clone(), - oed.get_data(), - self.olps.clone(), - osc.status_code, - ); - Ok(response) - } - } + let response = Response::new( + self.plain_text.clone(), + oed.get_data(), + self.olps.clone(), + osc.status_code, + ); - pub fn is_prepared(&mut self) -> bool { - self.prepared + if flag.status_code == 1 { + tcp.close().await?; + } + Ok(response) } } diff --git a/src/models/handler.rs b/src/models/handler.rs index c396899..d9c0b57 100644 --- a/src/models/handler.rs +++ b/src/models/handler.rs @@ -1,18 +1,18 @@ //! # Oblivion Default Handler -use super::{render::BaseResponse, render::Response}; -use crate::utils::parser::OblivionRequest; -use oblivion_codegen::async_route; +use super::render::{BaseResponse, Response}; +use super::session::Session; +use futures::FutureExt; /// Not Found Handler /// /// Handling a non-existent route request. -#[async_route] -pub fn not_found(mut request: OblivionRequest) -> Response { - Ok(BaseResponse::TextResponse( - format!( - "Path {} is not found, error with code 404.", - request.get_olps() - ), - 404, - )) +pub fn not_found(session: &mut Session) -> Response { + let olps = session.request.as_mut().unwrap().get_ip(); + async move { + Ok(BaseResponse::TextResponse( + format!("Path {} is not found, error with code 404.", olps), + 404, + )) + } + .boxed() } diff --git a/src/models/mod.rs b/src/models/mod.rs index dc3de74..527208c 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -4,4 +4,4 @@ pub mod packet; pub mod render; pub mod router; pub mod server; -// pub mod session; +pub mod session; diff --git a/src/models/router.rs b/src/models/router.rs index 48ca507..4c94f58 100644 --- a/src/models/router.rs +++ b/src/models/router.rs @@ -1,18 +1,20 @@ //! # Oblivion Router use super::handler::not_found; use super::render::Response; -use crate::utils::parser::OblivionRequest; +use super::session::Session; use anyhow::Result; use regex::Regex; use std::collections::HashMap; +pub type Handler = fn(&mut Session) -> Response; + #[derive(Clone)] pub struct Route { - handler: fn(OblivionRequest) -> Response, + handler: Handler, } impl Route { - pub fn new(handler: fn(OblivionRequest) -> Response) -> Self { + pub fn new(handler: Handler) -> Self { Self { handler } } @@ -22,7 +24,7 @@ impl Route { } } - pub fn get_handler(&mut self) -> fn(OblivionRequest) -> Response { + pub fn get_handler(&mut self) -> Handler { self.handler.clone() } } @@ -72,11 +74,7 @@ impl Router { } } - pub fn route( - &mut self, - path: RoutePath, - handler: fn(OblivionRequest) -> Response, - ) -> &mut Self { + pub fn route(&mut self, path: RoutePath, handler: Handler) -> &mut Self { self.routes.insert(path.clone(), Route { handler: handler }); self } diff --git a/src/models/server.rs b/src/models/server.rs index e750f3b..8f0c50a 100644 --- a/src/models/server.rs +++ b/src/models/server.rs @@ -1,169 +1,83 @@ //! # Oblivion Server use std::net::SocketAddr; +use std::sync::Arc; -use crate::exceptions::OblivionException; -use crate::models::packet::{OED, OKE, OSC}; use crate::utils::gear::Socket; -use crate::utils::generator::generate_key_pair; -use crate::utils::parser::OblivionRequest; - -use chrono::Local; -use p256::ecdh::EphemeralSecret; -use p256::PublicKey; use anyhow::{Error, Result}; +use chrono::Local; use colored::Colorize; -use serde_json::from_slice; use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::Mutex; -use super::router::{Route, Router}; +use super::packet::{OED, OSC}; +use super::router::Router; +use super::session::Session; -/// Server Connection Solver -/// -/// Handshake between server and client. -pub struct ServerConnection { - private_key: EphemeralSecret, - public_key: PublicKey, - aes_key: Option>, -} +async fn _handle(router: &mut Router, mut socket: Socket, peer: SocketAddr) -> Result { + socket.set_ttl(20)?; -impl ServerConnection { - pub fn new() -> Result { - let (private_key, public_key) = generate_key_pair()?; + let session = Arc::new(Mutex::new(Session::new(socket)?)); - Ok(Self { - private_key, - public_key, - aes_key: None, - }) - } + let mut brd_sess = session.lock().await; - pub async fn handshake( - &mut self, - stream: &mut Socket, - peer: SocketAddr, - ) -> Result { - let len_header = stream.recv_usize().await?; - let header = stream.recv_str(len_header).await?; - let mut request = OblivionRequest::new(&header)?; - request.set_remote_peer(&peer); - - let mut oke = OKE::new(Some(&self.private_key), Some(self.public_key))?; - oke.to_stream_with_salt(stream).await?; - oke.from_stream(stream).await?; - - request.aes_key = Some(oke.get_aes_key()); - self.aes_key = Some(oke.get_aes_key()); - - if request.method == "POST" { - let mut oed = OED::new(self.aes_key.clone()); - oed.from_stream(stream, 5).await?; - request.set_post(from_slice(&oed.get_data())?); - } else if request.method == "GET" { - } else if request.method == "PUT" { - let mut oed = OED::new(self.aes_key.clone()); - oed.from_stream(stream, 5).await?; - request.set_post(from_slice(&oed.get_data())?); - - let mut oed = OED::new(self.aes_key.clone()); - oed.from_stream(stream, 5).await?; - request.set_put(oed.get_data()); - } else { - return Err(Error::from(OblivionException::UnsupportedMethod { - method: request.method, - })); - }; - Ok(request) + if let Err(error) = brd_sess.handshake(1).await { + eprintln!( + "{} -> [{}] \"{}\" {}", + peer.ip().to_string().cyan(), + Local::now().format("%d/%m/%Y %H:%M:%S"), + "CONNECT".yellow(), + "500".red() + ); + return Err(Error::from(error)); } -} - -/// Responser -/// -/// Send response back to client requester. -pub async fn response( - route: &mut Route, - stream: &mut Socket, - request: OblivionRequest, - aes_key: Vec, -) -> Result { - let handler = route.get_handler(); - let mut callback = handler(request).await?; - let mut oed = OED::new(Some(aes_key)); - oed.from_bytes(callback.as_bytes()?)?; - oed.to_stream(stream, 5).await?; + let header = brd_sess.header.as_ref().unwrap().clone(); + let ip_addr = brd_sess.request.as_mut().unwrap().get_ip(); + let aes_key = brd_sess.aes_key.clone().unwrap(); - let mut osc = OSC::from_u32(callback.get_status_code()?); - osc.to_stream(stream).await?; - Ok(callback.get_status_code()?) -} - -async fn _handle( - router: &mut Router, - stream: &mut Socket, - peer: SocketAddr, -) -> Result<(OblivionRequest, u32)> { - stream.set_ttl(20)?; - let mut connection = ServerConnection::new()?; - let mut request = match connection.handshake(stream, peer).await { - Ok(request) => request, - Err(error) => { - eprintln!( - "{} -> [{}] \"{}\" {}", - peer.ip().to_string().cyan(), - Local::now().format("%d/%m/%Y %H:%M:%S"), - "CONNECT".yellow(), - "500".red() - ); - return Err(Error::from(error)); - } - }; - - let mut route = router.get_handler(&request.olps)?; - let status_code = match response( - &mut route, - stream, - request.clone(), - connection.aes_key.unwrap(), - ) - .await - { - Ok(status_code) => status_code, - Err(error) => { - eprintln!( - "{} -> [{}] \"{}\" {}", - request.get_ip().cyan(), - Local::now().format("%d/%m/%Y %H:%M:%S"), - &request.header.yellow(), - "501".red() - ); - return Err(Error::from(error)); + let mut route = router.get_handler(&brd_sess.request.as_ref().unwrap().olps)?; + let handler = route.get_handler(); + let mut callback = handler(&mut brd_sess).await?; + + let status_code = callback.get_status_code()?; + + OSC::from_u32(1).to_stream(&mut brd_sess.socket).await?; + OED::new(Some(aes_key)) + .from_bytes(callback.as_bytes()?)? + .to_stream(&mut brd_sess.socket, 5) + .await?; + OSC::from_u32(callback.get_status_code()?) + .to_stream(&mut brd_sess.socket) + .await?; + + let display = format!( + "{} -> [{}] \"{}\" {}", + ip_addr.cyan(), + Local::now().format("%d/%m/%Y %H:%M:%S"), + header.green(), + if status_code >= 500 { + status_code.to_string().red() + } else if status_code < 500 && status_code >= 400 { + status_code.to_string().yellow() + } else { + status_code.to_string().cyan() } - }; + ); - Ok((request, status_code)) + Ok(display) } pub async fn handle(router: Router, stream: TcpStream, peer: SocketAddr) { - let mut stream = Socket::new(stream); + let socket = Socket::new(stream); let mut router = router; - match _handle(&mut router, &mut stream, peer).await { - Ok((mut request, status_code)) => { - println!( - "{} -> [{}] \"{}\" {}", - request.get_ip().cyan(), - Local::now().format("%d/%m/%Y %H:%M:%S"), - &request.header.green(), - if status_code >= 500 { - status_code.to_string().red() - } else if status_code < 500 && status_code >= 400 { - status_code.to_string().yellow() - } else { - status_code.to_string().cyan() - } - ) + match _handle(&mut router, socket, peer).await { + Ok(display) => { + println!("{}", display) + } + Err(error) => { + eprintln!("{}", error.to_string().bright_red()) } - Err(error) => eprintln!("{}", error.to_string().bright_red()), } } diff --git a/src/models/session.rs b/src/models/session.rs new file mode 100644 index 0000000..aecadfe --- /dev/null +++ b/src/models/session.rs @@ -0,0 +1,105 @@ +use anyhow::{anyhow, Result}; +use chrono::{DateTime, Local}; +use p256::{ecdh::EphemeralSecret, PublicKey}; + +use crate::utils::gear::Socket; +use crate::utils::generator::generate_key_pair; +use crate::utils::parser::{length, OblivionRequest}; + +use super::packet::{OED, OKE, OSC}; + +pub struct Session { + pub header: Option, + pub(crate) private_key: EphemeralSecret, + pub(crate) public_key: PublicKey, + pub(crate) aes_key: Option>, + pub request_time: DateTime, + pub request: Option, + pub socket: Socket, +} + +impl Session { + pub fn new(socket: Socket) -> Result { + let (private_key, public_key) = generate_key_pair()?; + Ok(Self { + header: None, + private_key, + public_key, + aes_key: None, + request_time: Local::now(), + request: None, + socket, + }) + } + + pub fn new_with_header(header: &str, socket: Socket) -> Result { + let (private_key, public_key) = generate_key_pair()?; + Ok(Self { + header: Some(header.to_string()), + private_key, + public_key, + aes_key: None, + request_time: Local::now(), + request: None, + socket, + }) + } + + pub async fn first_hand(&mut self) -> Result<()> { + let socket = &mut self.socket; + let header = self.header.as_ref().unwrap().as_bytes(); + socket + .send(&[&length(&header.to_vec())?, header].concat()) + .await?; + + let mut oke = OKE::new(Some(&self.private_key), Some(self.public_key))?; + oke.from_stream_with_salt(socket).await?; + self.aes_key = Some(oke.get_aes_key()); + oke.to_stream(socket).await?; + Ok(()) + } + + pub async fn second_hand(&mut self) -> Result<()> { + let socket = &mut self.socket; + let peer = socket.peer_addr()?; + let len_header = socket.recv_usize().await?; + let header = socket.recv_str(len_header).await?; + let mut request = OblivionRequest::new(&header)?; + request.set_remote_peer(&peer); + + let mut oke = OKE::new(Some(&self.private_key), Some(self.public_key))?; + oke.to_stream_with_salt(socket).await?; + oke.from_stream(socket).await?; + + request.aes_key = Some(oke.get_aes_key()); + self.aes_key = Some(oke.get_aes_key()); + + self.request = Some(request); + self.header = Some(header); + Ok(()) + } + + pub async fn handshake(&mut self, flag: u8) -> Result<()> { + match flag { + 0 => self.first_hand().await?, + 1 => self.second_hand().await?, + _ => return Err(anyhow!("Unknown handshake flag")), + }; + Ok(()) + } + + pub async fn send( + &mut self, + socket: &mut Socket, + data: Vec, + status_code: u32, + ) -> Result<()> { + OSC::from_u32(0).to_stream(socket).await?; + OED::new(Some(self.aes_key.clone().unwrap())) + .from_bytes(data)? + .to_stream(socket, 5) + .await?; + OSC::from_u32(status_code).to_stream(socket).await?; + Ok(()) + } +} diff --git a/src/sessions.rs b/src/sessions.rs index 15769ca..a1289f2 100644 --- a/src/sessions.rs +++ b/src/sessions.rs @@ -2,7 +2,7 @@ use anyhow::Result; use serde_json::Value; -use crate::models::client::{Request, Response}; +use crate::models::client::{Client, Response}; /// ## Oblivion Abstract Session /// @@ -18,21 +18,11 @@ impl Session { &self, method: String, olps: String, - data: Option, - file: Option>, - tfo: bool, + _data: Option, + _file: Option>, ) -> Result { - let mut req = Request::new(method, olps, data, file, tfo)?; + let mut req = Client::new(method, olps)?; req.prepare().await?; - Ok(self.send(&mut req).await?) - } - - pub async fn send(&self, request: &mut Request) -> Result { - if request.is_prepared() != true { - request.prepare().await?; - } - - request.send().await?; - Ok(request.recv().await?) + Ok(req.recv().await?) } } diff --git a/src/utils/gear.rs b/src/utils/gear.rs index fb9ab59..f47ae58 100644 --- a/src/utils/gear.rs +++ b/src/utils/gear.rs @@ -1,12 +1,14 @@ //! Oblivion Abstract Gear +use std::net::SocketAddr; + use anyhow::Result; -use ring::{ - aead::{Nonce, NonceSequence}, - error::Unspecified, -}; -use tokio::{ - io::{AsyncReadExt, AsyncWriteExt}, - net::TcpStream, +use ring::aead::{Nonce, NonceSequence}; +use ring::error::Unspecified; + +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::{ + tcp::{ReadHalf, WriteHalf}, + TcpStream, }; /// Absolute Nonce Sequence Structure @@ -49,6 +51,10 @@ impl Socket { Ok(()) } + pub fn peer_addr(&mut self) -> Result { + Ok(self.tcp.peer_addr()?) + } + pub async fn recv_usize(&mut self) -> Result { let mut len_bytes = [0; 4]; self.tcp.read_exact(&mut len_bytes).await?; @@ -84,4 +90,8 @@ impl Socket { self.tcp.shutdown().await?; Ok(()) } + + pub async fn split(&mut self) -> Result<(ReadHalf, WriteHalf)> { + Ok(self.tcp.split()) + } }