#![allow(dead_code)]
mod fastcgi;
mod http;
pub use http::{Request, Response};
use std::collections::HashMap;
use std::iter::Iterator;
use std::net::{TcpListener, TcpStream, ToSocketAddrs};
use std::io::Write;
#[cfg(feature="spawn")]
use std::thread;
pub struct Client
{
listener: TcpListener,
}
impl Client
{
pub fn new<A: ToSocketAddrs>(addr: A) -> Client
{
Client {
listener: TcpListener::bind(addr).expect("Bind address"),
}
}
#[cfg(feature="spawn")]
pub fn run<T: Handler + Send + Clone + 'static>(&self, handler: T)
{
let listener = self.listener.try_clone().expect("Clone listener");
let handler = handler.clone();
thread::spawn(move || {
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let reader = StreamSyntax::new(&stream);
for mut pair in reader {
handler.process(&mut pair);
pair.response().flush().unwrap();
}
}
Err(e) => panic!("{}", e),
}
}
});
}
#[cfg(not(feature="spawn"))]
pub fn run<T: Handler>(&self, handler: T)
{
for stream in self.listener.incoming() {
match stream {
Ok(stream) => {
let reader = StreamSyntax::new(&stream);
for mut pair in reader {
handler.process(&mut pair);
pair.response().flush().unwrap();
}
}
Err(e) => panic!("{}", e),
}
}
}
}
pub struct HttpPair<'s>(http::Request<'s>, http::Response<'s>);
impl<'s> HttpPair<'s>
{
pub fn request(&mut self) -> &mut http::Request<'s>
{
&mut self.0
}
pub fn response(&mut self) -> &mut http::Response<'s>
{
&mut self.1
}
}
pub struct StreamSyntax<'s>
{
born: bool,
pair: HashMap<u16, HttpPair<'s>>,
stream: &'s TcpStream,
}
impl<'s> StreamSyntax<'s>
{
fn new(stream: &'s TcpStream) -> StreamSyntax
{
StreamSyntax {
born: true,
pair: HashMap::new(),
stream: stream,
}
}
}
impl<'s> Iterator for StreamSyntax<'s>
{
type Item = HttpPair<'s>;
fn next(&mut self) -> Option<Self::Item>
{
while !self.pair.is_empty() || self.born {
let h = http::Request::fcgi_header(self.stream);
let body = http::Request::fcgi_body(self.stream, &h);
self.pair.entry(h.request_id)
.or_insert(HttpPair(
http::Request::new(self.stream, h.request_id),
http::Response::new(self.stream, h.request_id),
));
match h.type_ {
fastcgi::ABORT_REQUEST => {
self.pair.remove(&h.request_id).unwrap()
.response()
.flush()
.expect("Send end request on abort")
}
fastcgi::PARAMS if h.content_length == 0 => {
self.born = false;
return self.pair.remove(&h.request_id);
}
_ => {
self.pair.get_mut(&h.request_id).unwrap()
.request().fcgi_record(h, body)
}
}
}
None
}
}
pub trait Handler
{
fn process(&self, &mut HttpPair);
}