diff options
| author | Teddy Wing | 2020-06-29 00:13:28 +0200 | 
|---|---|---|
| committer | Teddy Wing | 2020-06-29 00:13:28 +0200 | 
| commit | 4d24b2ce62f7f011297fe2b61358c9124af129e5 (patch) | |
| tree | dcd7acef1662c6ba6aa3fc7bfd39f4d6f5c64f00 | |
| parent | c2f9adfe96406323e715513444a9d70fc7c4c17d (diff) | |
| download | fastcgi-conduit-4d24b2ce62f7f011297fe2b61358c9124af129e5.tar.bz2 | |
Move request code to `request.rs`
Want to separate request and server code.
| -rw-r--r-- | src/lib.rs | 239 | ||||
| -rw-r--r-- | src/request.rs | 235 | 
2 files changed, 239 insertions, 235 deletions
| @@ -2,251 +2,20 @@ extern crate conduit;  extern crate fastcgi;  extern crate http; +mod request; +  use std::io; -use std::io::{BufReader, Read, Write}; -use std::net::SocketAddr; +use std::io::Write;  use conduit::Handler; -use inflector::cases::traincase::to_train_case; - -use snafu::{ResultExt, Snafu}; - - -#[derive(Debug, Snafu)] -pub enum RequestError { -    #[snafu(display("{}", source))] -    InvalidMethod { source: http::method::InvalidMethod }, - -    #[snafu(display("{}", source))] -    InvalidHeaderName { source: conduit::header::InvalidHeaderName }, - -    #[snafu(display("{}", source))] -    InvalidHeaderValue { source: conduit::header::InvalidHeaderValue }, - -    #[snafu(display("{}", source))] -    InvalidRemoteAddr { source: RemoteAddrError }, -} - -pub type RequestResult<T, E = RequestError> = std::result::Result<T, E>; - -#[derive(Debug, Snafu)] -pub enum RemoteAddrError { -    #[snafu(display("Could not parse address {}: {}", address, source))] -    AddrParseError { -        address: String, -        source: std::net::AddrParseError, -    }, - -    #[snafu(display("Could not parse port {}: {}", port, source))] -    PortParseError { -        port: String, -        source: std::num::ParseIntError -    }, -} - - -struct FastCgiRequest<'a> { -    request: &'a mut fastcgi::Request, -    http_version: conduit::Version, -    host: String, -    method: conduit::Method, -    headers: conduit::HeaderMap, -    path: String, -    query: Option<String>, -    remote_addr: SocketAddr, -    content_length: Option<u64>, -    extensions: conduit::Extensions, -} - -impl<'a> FastCgiRequest<'a> { -    pub fn new(request: &'a mut fastcgi::Request) -> RequestResult<Self> { -        let version = Self::version(request); -        let host = Self::host(request); -        let method = Self::method(request).context(InvalidMethod)?; -        let headers = Self::headers(request.params())?; -        let path = Self::path(request); -        let query = Self::query(request); -        let remote_addr = Self::remote_addr(request).context(InvalidRemoteAddr)?; -        let content_length = Self::content_length(request); - -        Ok(Self { -            request: request, -            http_version: version, -            host: host, -            method: method, -            headers: headers, -            path: path, -            query: query, -            remote_addr: remote_addr, -            content_length: content_length, -            extensions: conduit::TypeMap::new(), -        }) -    } - -    pub fn scheme(&self) -> conduit::Scheme { -        let scheme = self.request.param("REQUEST_SCHEME").unwrap_or_default(); - -        if scheme == "https" { -            conduit::Scheme::Https -        } else { -            conduit::Scheme::Http -        } -    } - -    fn version(request: &fastcgi::Request) -> conduit::Version { -        match request.param("SERVER_PROTOCOL").unwrap_or_default().as_str() { -            "HTTP/0.9" => conduit::Version::HTTP_09, -            "HTTP/1.0" => conduit::Version::HTTP_10, -            "HTTP/1.1" => conduit::Version::HTTP_11, -            "HTTP/2.0" => conduit::Version::HTTP_2, -            "HTTP/3.0" => conduit::Version::HTTP_3, -            _ => conduit::Version::default(), -        } -    } - -    fn host(request: &fastcgi::Request) -> String { -        request.param("HTTP_HOST").unwrap_or_default() -    } - -    fn method( -        request: &fastcgi::Request -    ) -> Result<conduit::Method, http::method::InvalidMethod> { -        conduit::Method::from_bytes( -            request.param("REQUEST_METHOD") -                .unwrap_or_default() -                .as_bytes() -        ) -    } - -    fn headers(params: fastcgi::Params) -> RequestResult<conduit::HeaderMap> { -        let mut map = conduit::HeaderMap::new(); -        let headers = Self::headers_from_params(params); - -        for (name, value) in headers -            .iter() -            .map(|(name, value)| (name.as_bytes(), value.as_bytes())) -        { -            map.append( -                conduit::header::HeaderName::from_bytes(name) -                    .context(InvalidHeaderName)?, -                conduit::header::HeaderValue::from_bytes(value) -                    .context(InvalidHeaderValue)?, -            ); -        } - -        Ok(map) -    } - -    fn headers_from_params(params: fastcgi::Params) -> Vec<(String, String)> { -        return params -            .filter(|(key, _)| key.starts_with("HTTP_")) -            .map(|(key, value)| { -                let key = key.get(5..).unwrap_or_default(); -                let key = &key.replace("_", "-"); -                let key = &to_train_case(&key); - -                (key.to_owned(), value) -            }) -            .collect() -    } - -    fn path(request: &fastcgi::Request) -> String { -        match request.param("SCRIPT_NAME") { -            Some(p) => p, -            None => "/".to_owned(), -        } -    } - -    fn query(request: &fastcgi::Request) -> Option<String> { -        request.param("QUERY_STRING") -    } - -    fn remote_addr(request: &fastcgi::Request) -> Result<SocketAddr, RemoteAddrError> { -        let addr = request.param("REMOTE_ADDR").unwrap_or_default(); -        let port = request.param("REMOTE_PORT").unwrap_or_default(); - -        Ok( -            SocketAddr::new( -                addr.parse().context(AddrParseError { address: addr })?, -                port.parse().context(PortParseError { port })?, -            ) -        ) -    } - -    fn content_length(request: &fastcgi::Request) -> Option<u64> { -        request.param("CONTENT_LENGTH").and_then(|l| l.parse().ok()) -    } -} - -impl<'a> Read for FastCgiRequest<'a> { -    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { -        self.request.stdin().read(buf) -    } -} - -impl<'a> conduit::RequestExt for FastCgiRequest<'a> { -   fn http_version(&self) -> conduit::Version { -       self.http_version -   } - -   fn method(&self) -> &conduit::Method { -       &self.method -   } - -   fn scheme(&self) -> conduit::Scheme { -       self.scheme() -   } - -   fn host(&self) -> conduit::Host<'_> { -       conduit::Host::Name(&self.host) -   } - -   fn virtual_root(&self) -> std::option::Option<&str> { -       None -   } - -   fn path(&self) -> &str { -       &self.path -   } - -   fn query_string(&self) -> std::option::Option<&str> { -       self.query.as_ref() -           .map(|p| p.as_str()) -   } - -   fn remote_addr(&self) -> std::net::SocketAddr { -       self.remote_addr -   } - -   fn content_length(&self) -> std::option::Option<u64> { -       self.content_length -   } - -   fn headers(&self) -> &conduit::HeaderMap { -       &self.headers -   } - -   fn body(&mut self) -> &mut (dyn std::io::Read) { -       self -   } - -   fn extensions(&self) -> &conduit::Extensions { -       &self.extensions -   } - -   fn mut_extensions(&mut self) -> &mut conduit::Extensions { -       &mut self.extensions -   } -} -  pub struct Server;  impl Server {      pub fn start<H: Handler + 'static + Sync>(handler: H) -> io::Result<Server> {          fastcgi::run(move |mut raw_request| { -            let mut request = FastCgiRequest::new(&mut raw_request).unwrap(); +            let mut request = request::FastCgiRequest::new(&mut raw_request).unwrap();              let response = handler.call(&mut request);              let mut stdout = raw_request.stdout(); diff --git a/src/request.rs b/src/request.rs new file mode 100644 index 0000000..8138d47 --- /dev/null +++ b/src/request.rs @@ -0,0 +1,235 @@ +use std::io; +use std::io::Read; +use std::net::SocketAddr; + +use inflector::cases::traincase::to_train_case; + +use snafu::{ResultExt, Snafu}; + + +#[derive(Debug, Snafu)] +pub enum RequestError { +    #[snafu(display("{}", source))] +    InvalidMethod { source: http::method::InvalidMethod }, + +    #[snafu(display("{}", source))] +    InvalidHeaderName { source: conduit::header::InvalidHeaderName }, + +    #[snafu(display("{}", source))] +    InvalidHeaderValue { source: conduit::header::InvalidHeaderValue }, + +    #[snafu(display("{}", source))] +    InvalidRemoteAddr { source: RemoteAddrError }, +} + +pub type RequestResult<T, E = RequestError> = std::result::Result<T, E>; + +#[derive(Debug, Snafu)] +pub enum RemoteAddrError { +    #[snafu(display("Could not parse address {}: {}", address, source))] +    AddrParseError { +        address: String, +        source: std::net::AddrParseError, +    }, + +    #[snafu(display("Could not parse port {}: {}", port, source))] +    PortParseError { +        port: String, +        source: std::num::ParseIntError +    }, +} + + +pub struct FastCgiRequest<'a> { +    request: &'a mut fastcgi::Request, +    http_version: conduit::Version, +    host: String, +    method: conduit::Method, +    headers: conduit::HeaderMap, +    path: String, +    query: Option<String>, +    remote_addr: SocketAddr, +    content_length: Option<u64>, +    extensions: conduit::Extensions, +} + +impl<'a> FastCgiRequest<'a> { +    pub fn new(request: &'a mut fastcgi::Request) -> RequestResult<Self> { +        let version = Self::version(request); +        let host = Self::host(request); +        let method = Self::method(request).context(InvalidMethod)?; +        let headers = Self::headers(request.params())?; +        let path = Self::path(request); +        let query = Self::query(request); +        let remote_addr = Self::remote_addr(request).context(InvalidRemoteAddr)?; +        let content_length = Self::content_length(request); + +        Ok(Self { +            request: request, +            http_version: version, +            host: host, +            method: method, +            headers: headers, +            path: path, +            query: query, +            remote_addr: remote_addr, +            content_length: content_length, +            extensions: conduit::TypeMap::new(), +        }) +    } + +    pub fn scheme(&self) -> conduit::Scheme { +        let scheme = self.request.param("REQUEST_SCHEME").unwrap_or_default(); + +        if scheme == "https" { +            conduit::Scheme::Https +        } else { +            conduit::Scheme::Http +        } +    } + +    fn version(request: &fastcgi::Request) -> conduit::Version { +        match request.param("SERVER_PROTOCOL").unwrap_or_default().as_str() { +            "HTTP/0.9" => conduit::Version::HTTP_09, +            "HTTP/1.0" => conduit::Version::HTTP_10, +            "HTTP/1.1" => conduit::Version::HTTP_11, +            "HTTP/2.0" => conduit::Version::HTTP_2, +            "HTTP/3.0" => conduit::Version::HTTP_3, +            _ => conduit::Version::default(), +        } +    } + +    fn host(request: &fastcgi::Request) -> String { +        request.param("HTTP_HOST").unwrap_or_default() +    } + +    fn method( +        request: &fastcgi::Request +    ) -> Result<conduit::Method, http::method::InvalidMethod> { +        conduit::Method::from_bytes( +            request.param("REQUEST_METHOD") +                .unwrap_or_default() +                .as_bytes() +        ) +    } + +    fn headers(params: fastcgi::Params) -> RequestResult<conduit::HeaderMap> { +        let mut map = conduit::HeaderMap::new(); +        let headers = Self::headers_from_params(params); + +        for (name, value) in headers +            .iter() +            .map(|(name, value)| (name.as_bytes(), value.as_bytes())) +        { +            map.append( +                conduit::header::HeaderName::from_bytes(name) +                    .context(InvalidHeaderName)?, +                conduit::header::HeaderValue::from_bytes(value) +                    .context(InvalidHeaderValue)?, +            ); +        } + +        Ok(map) +    } + +    fn headers_from_params(params: fastcgi::Params) -> Vec<(String, String)> { +        return params +            .filter(|(key, _)| key.starts_with("HTTP_")) +            .map(|(key, value)| { +                let key = key.get(5..).unwrap_or_default(); +                let key = &key.replace("_", "-"); +                let key = &to_train_case(&key); + +                (key.to_owned(), value) +            }) +            .collect() +    } + +    fn path(request: &fastcgi::Request) -> String { +        match request.param("SCRIPT_NAME") { +            Some(p) => p, +            None => "/".to_owned(), +        } +    } + +    fn query(request: &fastcgi::Request) -> Option<String> { +        request.param("QUERY_STRING") +    } + +    fn remote_addr(request: &fastcgi::Request) -> Result<SocketAddr, RemoteAddrError> { +        let addr = request.param("REMOTE_ADDR").unwrap_or_default(); +        let port = request.param("REMOTE_PORT").unwrap_or_default(); + +        Ok( +            SocketAddr::new( +                addr.parse().context(AddrParseError { address: addr })?, +                port.parse().context(PortParseError { port })?, +            ) +        ) +    } + +    fn content_length(request: &fastcgi::Request) -> Option<u64> { +        request.param("CONTENT_LENGTH").and_then(|l| l.parse().ok()) +    } +} + +impl<'a> Read for FastCgiRequest<'a> { +    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { +        self.request.stdin().read(buf) +    } +} + +impl<'a> conduit::RequestExt for FastCgiRequest<'a> { +   fn http_version(&self) -> conduit::Version { +       self.http_version +   } + +   fn method(&self) -> &conduit::Method { +       &self.method +   } + +   fn scheme(&self) -> conduit::Scheme { +       self.scheme() +   } + +   fn host(&self) -> conduit::Host<'_> { +       conduit::Host::Name(&self.host) +   } + +   fn virtual_root(&self) -> std::option::Option<&str> { +       None +   } + +   fn path(&self) -> &str { +       &self.path +   } + +   fn query_string(&self) -> std::option::Option<&str> { +       self.query.as_ref() +           .map(|p| p.as_str()) +   } + +   fn remote_addr(&self) -> std::net::SocketAddr { +       self.remote_addr +   } + +   fn content_length(&self) -> std::option::Option<u64> { +       self.content_length +   } + +   fn headers(&self) -> &conduit::HeaderMap { +       &self.headers +   } + +   fn body(&mut self) -> &mut (dyn std::io::Read) { +       self +   } + +   fn extensions(&self) -> &conduit::Extensions { +       &self.extensions +   } + +   fn mut_extensions(&mut self) -> &mut conduit::Extensions { +       &mut self.extensions +   } +} | 
