/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2023 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::time::Duration; use smol::io::{AsyncReadExt, AsyncWriteExt}; use super::jsonrpc::*; use crate::{error::RpcError, net::transport::PtStream, system::io_timeout, Result}; pub(super) const INIT_BUF_SIZE: usize = 4096; // 4K pub(super) const MAX_BUF_SIZE: usize = 1024 * 8192; // 8M pub(super) const READ_TIMEOUT: Duration = Duration::from_secs(30); /// Internal read function that reads from the active stream into a buffer. pub(super) async fn read_from_stream( stream: &mut Box, buf: &mut Vec, with_timeout: bool, ) -> Result { let mut total_read = 0; while total_read < MAX_BUF_SIZE { buf.resize(total_read + INIT_BUF_SIZE, 0); // Lame we have to duplicate this code, but it is what it is. if with_timeout { match io_timeout(READ_TIMEOUT, stream.read(&mut buf[total_read..])).await { Ok(0) if total_read == 0 => { return Err( RpcError::ConnectionClosed("Connection closed cleanly".to_string()).into() ) } Ok(0) => break, // Finished reading Ok(n) => { total_read += n; if buf[total_read - 1] == b'\n' { break } } Err(e) => return Err(RpcError::IoError(e.kind()).into()), } } else { match stream.read(&mut buf[total_read..]).await { Ok(0) if total_read == 0 => { return Err( RpcError::ConnectionClosed("Connection closed cleanly".to_string()).into() ) } Ok(0) => break, // Finished reading Ok(n) => { total_read += n; if buf[total_read - 1] == b'\n' { break } } Err(e) => return Err(RpcError::IoError(e.kind()).into()), } } } // Truncate buffer to actual data size buf.truncate(total_read); Ok(total_read) } /// Internal write function that writes a JSON-RPC object to the active stream. pub(super) async fn write_to_stream( stream: &mut Box, object: &JsonResult, ) -> Result<()> { let object_str = match object { JsonResult::Notification(v) => v.stringify()?, JsonResult::Response(v) => v.stringify()?, JsonResult::Error(v) => v.stringify()?, JsonResult::Request(v) => v.stringify()?, _ => unreachable!(), }; // As we're a line-based protocol, we append the '\n' char at // the end of the JSON string. for i in [object_str.as_bytes(), &[b'\n']] { if let Err(e) = stream.write_all(i).await { return Err(e.into()) } } Ok(()) }