kvarn_extensions/
fastcgi.rs1use 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 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 ¶m_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 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}