I'm writing a HTTP server using axum
, and am writing a handler. It calls some external program somecommand
, writes some string
to its stdin, and reads stdout. The stdout output is formatted like a HTTP request (i.e. Header: Value\r\nHeader2: Value\r\n\r\nBody
).
I want to turn that stdout output into an axum response.
async fn handle_request() -> impl IntoResponse {
let mut child = Command::new("somecommand")
.stdin(Stdio::piped())
.spawn()
.expect("Failed to spawn");
let mut stdin = child.stdin.take().expect("Failed to open stdin");
stdin
.write_all("some string".as_bytes())
.expect("Could not write to stdin");
let output = child
.wait_with_output()
.expect("Failed to wait with output");
let stdout = String::from_utf8(output.stdout)
.expect("failed to parse stdout as utf8")
.to_string();
let r = stdout.split("\r\n\r\n").collect::<Vec<&str>>();
let mut headers = HeaderMap::new();
let lines: Vec<&str> = r[0].lines().collect();
lines.iter().for_each(|line| {
let parts: Vec<&str> = line.splitn(2, ": ").collect();
headers.insert(
parts[0],
parts[1].parse().expect("Could not parse header value"),
);
});
(headers, r[1].to_string())
}
The error I'm getting is stdout does not live long enough
, which, if I understand things correctly, is because headers.insert(parts[0] ...
, because part[0]
lives only in this function scope, but since I'm returning it (as part of the headers
map) it must have a longer lifetime.
So how do I increase the lifetime of parts[0]
(or stdout
) so that I can return it?
The problem is slightly different from what you think, the keys for a HeaderMap::insert
must implement IntoHeaderName
and only &'static str
, HeaderName
and &HeaderName
do, so you can't use a non static &str
to insert a value irrespective of wether you return that map or not. Here is a minimal example that produces the same error:
use http::HeaderMap;
fn baz() {
let header = String::from("header");
let header = header.as_str();
let mut headers = HeaderMap::new();
headers.insert(header, "value".parse().unwrap());
}
But you can construct a HeaderName
yourself:
use http::{HeaderMap, HeaderName};
fn baz() {
let header = String::from("header");
let header = header.as_str();
let mut headers = HeaderMap::new();
headers.insert(
HeaderName::from_bytes(header.as_bytes()).unwrap(),
"value".parse().unwrap(),
);
}