kvarn_extensions/
fastcgi.rs

1use crate::*;
2use kvarn_fastcgi_client::{Client, Params};
3use std::borrow::Cow;
4
5pub enum FastcgiError {
6    FailedToConnect(io::Error),
7    FailedToDoRequest(kvarn_fastcgi_client::ClientError),
8    NoStdout,
9}
10#[allow(clippy::too_many_arguments)]
11pub async fn connect(
12    connection: Connection,
13    method: &str,
14    file_name: &str,
15    file_path: &str,
16    path: &str,
17    query: Option<&str>,
18    address: &SocketAddr,
19    content_type: &str,
20    headers: &HeaderMap,
21    body: &[u8],
22) -> Result<Vec<u8>, FastcgiError> {
23    // Create connection to FastCGI server
24    let stream = match connection.establish().await {
25        Ok(stream) => stream,
26        Err(err) => return Err(FastcgiError::FailedToConnect(err)),
27    };
28    let client = Client::new(stream);
29
30    let remote_addr = match address.ip() {
31        IpAddr::V4(addr) => addr.to_string(),
32        IpAddr::V6(addr) => addr.to_string(),
33    };
34    let mut params = Params::default()
35        .request_method(method)
36        .script_name(file_name)
37        .script_filename(file_path)
38        .request_uri(path)
39        .document_uri(path)
40        .remote_addr(&remote_addr)
41        .remote_port(address.port())
42        .server_addr("0.0.0.0")
43        .server_name(extensions::SERVER_NAME_VERSION)
44        .content_type(content_type)
45        .content_length(body.len());
46
47    if let Some(query) = query {
48        params = params.query_string(query);
49    }
50
51    let param_headers: Vec<_> = headers
52        .iter()
53        .filter_map(|(name, value)| {
54            if let Ok(value) = value.to_str() {
55                let mut name = name.as_str().to_uppercase();
56                name.insert_str(0, "HTTP_");
57                Some((name, value))
58            } else {
59                None
60            }
61        })
62        .collect();
63
64    for (name, value) in &param_headers {
65        params.insert(Cow::Borrowed(name), Cow::Borrowed(value));
66    }
67
68    let request = kvarn_fastcgi_client::Request::new(params, body);
69
70    match client.execute_once(request).await {
71        Ok(output) => match output.stdout {
72            Some(output) => Ok(output),
73            None => Err(FastcgiError::NoStdout),
74        },
75        Err(err) => Err(FastcgiError::FailedToDoRequest(err)),
76    }
77}
78pub async fn from_prepare<T>(
79    request: &Request<T>,
80    body: &[u8],
81    path: &Path,
82    address: SocketAddr,
83    connection: Connection,
84) -> Result<Vec<u8>, Cow<'static, str>> {
85    let file_name = match parse::format_file_name(&path) {
86        Some(name) => name,
87        None => {
88            return Err(Cow::Borrowed("Error formatting file name!"));
89        }
90    };
91    let file_path = match parse::format_file_path(&path) {
92        Ok(name) => name,
93        Err(_) => {
94            return Err(Cow::Borrowed("Getting working directory!"));
95        }
96    };
97    let file_path = match file_path.to_str() {
98        Some(path) => path,
99        None => {
100            return Err(Cow::Borrowed("Error formatting file path!"));
101        }
102    };
103
104    // Fetch fastcgi server response.
105    match connect(
106        connection.clone(),
107        request.method().as_str(),
108        file_name,
109        file_path,
110        request.uri().path(),
111        request.uri().query(),
112        &address,
113        request
114            .headers()
115            .get("content-type")
116            .and_then(|header| header.to_str().ok())
117            .unwrap_or(""),
118        request.headers(),
119        body,
120    )
121    .await
122    {
123        Ok(vec) => Ok(vec),
124        Err(err) => match err {
125            FastcgiError::FailedToConnect(err) => Err(Cow::Owned(format!(
126                "Failed to connect to FastCGI server on {connection:?}. IO Err: {err}",
127            ))),
128            FastcgiError::FailedToDoRequest(err) => Err(Cow::Owned(format!(
129                "Failed to request from FastCGI server! Err: {err}",
130            ))),
131            FastcgiError::NoStdout => Err(Cow::Borrowed("No stdout in response from FastCGI!")),
132        },
133    }
134}