blob: 83c1b6a57814fb86d5c8ff4605a5c31dfc30087b [file]
// Copyright 2026 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::io::stdio::PollFor;
use crate::io::{AbstractReader, AbstractSocket, AbstractSocketResult, AbstractWriter};
use std::{
io,
marker::PhantomData,
task::{Context, Poll},
};
#[macro_export]
#[doc(hidden)]
macro_rules! retry_on_interrupt {
($e:ident) => {
if matches!($e.kind(), ::std::io::ErrorKind::Interrupted) {
continue;
} else {
$crate::io::sync_io::translate_stdio_err($e)
}
};
}
/// `std::io::Read` and `std::io::Write` wrapper.
pub struct StdIoWithReactor<Io, Reactor> {
io: Io,
reactor: Reactor,
_p: PhantomData<fn() -> Reactor>,
}
impl<Io, Reactor> StdIoWithReactor<Io, Reactor> {
/// Construct a new I/O wrapper
pub fn new(io: Io, reactor: Reactor) -> Self {
Self {
io,
reactor,
_p: PhantomData,
}
}
}
impl<Io: io::Read + Send, R: PollFor<Io> + Send> AbstractReader for StdIoWithReactor<Io, R> {
fn read(
&mut self,
mut async_ctx: Option<&mut Context<'_>>,
buffer: &mut [u8],
) -> AbstractSocketResult {
loop {
let res = match <Io as io::Read>::read(&mut self.io, buffer) {
Ok(0) if !buffer.is_empty() => return AbstractSocketResult::EndOfStream,
Ok(bytes) => return AbstractSocketResult::Ok(bytes),
Err(e) => retry_on_interrupt!(e),
};
if let Some(async_ctx) = &mut async_ctx
&& matches!(res, AbstractSocketResult::Retry)
{
match self.reactor.poll_read(async_ctx) {
Poll::Ready(Err(e)) => return AbstractSocketResult::Err(Box::new(e)),
Poll::Pending => return AbstractSocketResult::Retry,
Poll::Ready(Ok(())) => continue,
}
}
return res;
}
}
}
impl<Io: io::Write + Send, R: PollFor<Io> + Send> AbstractWriter for StdIoWithReactor<Io, R> {
fn write(
&mut self,
mut async_ctx: Option<&mut Context<'_>>,
buffer: &[u8],
) -> AbstractSocketResult {
loop {
let res = match <Io as io::Write>::write(&mut self.io, buffer) {
Ok(bytes) => return AbstractSocketResult::Ok(bytes),
Err(e) => retry_on_interrupt!(e),
};
if let Some(async_ctx) = &mut async_ctx
&& matches!(res, AbstractSocketResult::Retry)
{
match self.reactor.poll_write(async_ctx) {
Poll::Ready(Err(e)) => return AbstractSocketResult::Err(Box::new(e)),
Poll::Pending => return AbstractSocketResult::Retry,
Poll::Ready(Ok(())) => continue,
}
}
return res;
}
}
fn flush(&mut self, mut async_ctx: Option<&mut Context<'_>>) -> AbstractSocketResult {
loop {
let res = match <Io as io::Write>::flush(&mut self.io) {
Ok(_) => return AbstractSocketResult::Ok(0),
Err(e) => retry_on_interrupt!(e),
};
if let Some(async_ctx) = &mut async_ctx
&& matches!(res, AbstractSocketResult::Retry)
{
match self.reactor.poll_write(async_ctx) {
Poll::Ready(Err(e)) => return AbstractSocketResult::Err(Box::new(e)),
Poll::Pending => return AbstractSocketResult::Retry,
Poll::Ready(Ok(())) => continue,
}
}
return res;
}
}
}
impl<Io: io::Read + io::Write + Send, R: PollFor<Io> + Send> AbstractSocket
for StdIoWithReactor<Io, R>
{
}
/// Translates a `std::io::Error` into an `AbstractSocketResult`.
pub(crate) fn translate_stdio_err(err: io::Error) -> AbstractSocketResult {
match err.kind() {
io::ErrorKind::WouldBlock => AbstractSocketResult::Retry,
io::ErrorKind::ConnectionReset
| io::ErrorKind::ConnectionRefused
| io::ErrorKind::ConnectionAborted
| io::ErrorKind::BrokenPipe
| io::ErrorKind::NotConnected
| io::ErrorKind::UnexpectedEof => AbstractSocketResult::EndOfStream,
_ => AbstractSocketResult::Err(Box::new(err)),
}
}
/// Reactor is absent.
/// Polling returns immediately with success, turning any I/O operation into a blocking call.
///
/// # Warning ⚠️
///
/// If the underlying I/O is non-blocking, this reactor will lead to busy-waiting and excessive
/// CPU usage.
pub struct NoAsync;
impl<Io> PollFor<Io> for NoAsync {
fn poll_read(&mut self, _: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
Poll::Ready(Ok(()))
}
fn poll_write(&mut self, _: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
Poll::Ready(Ok(()))
}
}