sark-h2 0.29.0

Fiber-free HTTP/2 protocol primitives and manifold server for sark
use std::ops;

use dope::{core::driver::route::Identity, manifold::network::listener::conn::Id};
use sark_core::{
    decode::request,
    duplex, life,
    manifold::{schedule, schedule::typed, stream::Boundary, task::app::Runtime},
};
use sark_http::{field::metadata::section::rules, payload::response::status::Code};

use crate::{
    conn,
    server::{
        response::{Handoff, Head, Status},
        state,
        task::{
            self, response,
            response::{Progress, input},
        },
    },
    stream,
};

impl<'d, ID: Identity + 'd, R, M, F, P> response::Driver<'_, 'd, ID, R, M, F, P>
where
    R: Runtime<'d, Target = task::Target<'d, ID>>,
    M: state::Transitions<Id<'d, ID>>,
    M::Retention: Handoff,
    F: rules::Policy,
    P: FnMut(Id<'d, ID>, stream::Key, &dyn std::fmt::Debug),
{
    pub(super) fn open(
        &mut self,
        stream: stream::Key,
        request: input::Request<'d, R::Prepared>,
    ) -> Progress<'d, R> {
        let app = self.router.as_ref().get_ref();
        let input = match request.into_body() {
            request::Body::Open(request) => match duplex::Application::try_open(app, request) {
                Ok((session, body)) => input::Input::open(session, input::Buf::new(body)),
                Err((error, ..)) => return self.reject_open(stream, error),
            },
            request::Body::Finished(request) => match duplex::Application::try_open(app, request) {
                Ok((session, body)) => input::Input::finished(session, input::Buf::new(body)),
                Err((error, ..)) => return self.reject_open(stream, error),
            },
        };
        let head = Status::new(Code::OK)
            .prepare(self.connection, stream, Boundary::More)
            .map_err(|error| self.error(stream, error))?;
        self.connection.hold_duplex(stream);
        self.resume_open(stream, head, input)
    }

    pub(super) fn resume_open(
        &mut self,
        stream: stream::Key,
        head: Head<'d>,
        input: input::Input<'d, R::Duplex>,
    ) -> Progress<'d, R> {
        let progress = match head.drive(self.connection) {
            Ok(progress) => progress,
            Err(error) => {
                drop(input);
                self.connection.release_duplex(stream);
                return Err(self.error(stream, error));
            }
        };
        match progress {
            ops::ControlFlow::Break(head) => Ok(typed::Control::Control {
                value: response::Response::DuplexHead { head, input },
                turn: schedule::Turn::Blocked(conn::DataBlock::Egress),
            }),
            ops::ControlFlow::Continue(receipt) => {
                drop(receipt.observe(self.mode, task::identify(self.connection_id, stream)));
                Ok(typed::Control::Data {
                    value: response::Data::Duplex { input },
                    turn: schedule::Turn::Progress,
                })
            }
        }
    }

    fn reject_open(
        &mut self,
        stream: stream::Key,
        error: duplex::OpenError<R::Error<'d>>,
    ) -> Progress<'d, R> {
        let status = match error {
            duplex::OpenError::InvalidRequest(error) => R::reject(error).status(),
            duplex::OpenError::Length => Code::BAD_REQUEST,
            duplex::OpenError::Unsupported => Code::INTERNAL_SERVER_ERROR,
        };
        let head = Status::new(status)
            .prepare(self.connection, stream, Boundary::End)
            .map_err(|error| self.error(stream, error))?;
        match head
            .drive(self.connection)
            .map_err(|error| self.error(stream, error))?
        {
            ops::ControlFlow::Break(head) => Ok(typed::Control::Control {
                value: response::Response::Head {
                    head,
                    state: sark_core::manifold::task::response::State::Complete(
                        life::Outcome::Rejected,
                    ),
                    count: response::Count::Pending,
                },
                turn: schedule::Turn::Blocked(conn::DataBlock::Egress),
            }),
            ops::ControlFlow::Continue(receipt) => {
                let exchange = task::identify(self.connection_id, stream);
                drop(receipt.observe(self.mode, exchange));
                exchange.retire(self.mode, life::Outcome::Rejected);
                Ok(typed::Control::Complete)
            }
        }
    }
}