use std::cell::RefCell;
use std::net::TcpStream;
use std::sync::{mpsc, Arc, Mutex};
use crate::config::Config;
use crate::connection::local_build_response;
use crate::error::Error;
use crate::macros::*;
use crate::response_end::ResponseEnd;
use crate::status::{Code, Status};
use crate::ReplyEncode;
use crate::Response;
pub type RequestTx<Req> = mpsc::SyncSender<DispatchRequest<Req>>;
pub type RequestRx<Req> = mpsc::Receiver<DispatchRequest<Req>>;
type ResponseTx = mpsc::SyncSender<DispatchResponse>;
type ResponseRx = mpsc::Receiver<DispatchResponse>;
pub struct DispatchRequest<Req> {
pub stream: DispatchStream,
pub request: Req,
}
pub struct DispatchResponse {
stream_id: u32,
response: Response<Box<dyn ReplyEncode>>,
}
pub struct DispatchStream {
stream_id: u32,
resp_tx: ResponseTx,
}
impl DispatchStream {
fn new(stream_id: u32) -> Self {
Self {
stream_id,
resp_tx: RESP_TX.with_borrow(|tx| tx.clone()),
}
}
pub fn response(self, response: Response<Box<dyn ReplyEncode>>) {
let disp_resp = DispatchResponse {
stream_id: self.stream_id,
response,
};
let _ = self.resp_tx.send(disp_resp);
}
}
thread_local! {
static RESP_TX: RefCell<ResponseTx> = panic!();
}
pub fn new_response_routine(c: Arc<Mutex<TcpStream>>, config: &Config) {
let resp_end = ResponseEnd::new(c, config);
let (resp_tx, resp_rx) = mpsc::sync_channel(config.max_concurrent_streams);
RESP_TX.set(resp_tx);
std::thread::Builder::new()
.name(String::from("pajamax-r")) .spawn(move || response_routine(resp_end, resp_rx))
.unwrap();
}
pub fn dispatch<Req>(req_tx: &RequestTx<Req>, request: Req, stream_id: u32) -> Result<(), Error> {
trace!("dispatch request id:{stream_id}");
let stream = DispatchStream::new(stream_id);
let disp_req = DispatchRequest { stream, request };
match req_tx.try_send(disp_req) {
Ok(_) => Ok(()),
Err(err) => {
error!("dispatch fails (stream_id:{stream_id}): {:?}", err);
let status = match err {
mpsc::TrySendError::Full(_) => Status {
code: Code::Unavailable,
message: String::from("dispatch channel is full"),
},
mpsc::TrySendError::Disconnected(_) => Status {
code: Code::Internal,
message: String::from("dispatch channel is closed"),
},
};
let response: Response<()> = Err(status);
local_build_response(stream_id, response)
}
}
}
fn response_routine(mut resp_end: ResponseEnd, resp_rx: ResponseRx) -> Result<(), Error> {
loop {
let resp = response_receive(&mut resp_end, &resp_rx)?;
trace!("receive dispatched response {}", resp.stream_id);
resp_end.build_box(resp.stream_id, resp.response)?;
}
}
fn response_receive(
resp_end: &mut ResponseEnd,
resp_rx: &ResponseRx,
) -> Result<DispatchResponse, Error> {
for i in 0..1000 {
match resp_rx.try_recv() {
Ok(resp) => {
return Ok(resp);
}
Err(mpsc::TryRecvError::Disconnected) => {
return Err(Error::ChannelClosed);
}
Err(mpsc::TryRecvError::Empty) => {
resp_end.flush()?;
std::thread::sleep(std::time::Duration::from_micros(i));
}
}
}
Ok(resp_rx.recv()?)
}
pub fn pending<T>() -> Response<T> {
Err(Status {
code: Code::DispatchPending,
message: String::new(),
})
}
pub fn is_pending(resp: &Response<Box<dyn ReplyEncode>>) -> bool {
if let Err(status) = resp {
status.code == Code::DispatchPending
} else {
false
}
}