Cannot set headers of an Iron framework Response - rust

I am looking to set the headers of a Iron Response with the following code:
extern crate iron; // 0.3.0
extern crate hyper; // 0.8.1
use iron::prelude::*;
use iron::status;
use hyper::header::{Headers, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
fn main() {
fn hello_world(_: &mut Request) -> IronResult<Response> {
let mut headers = Headers::new();
let string = getFileAsString("./public/index.html");
headers.set(
ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![]))
);
Ok(Response::with((status::Ok, string, headers)))
}
Iron::new(hello_world).http("localhost:3000").unwrap();
println!("On 3000");
}
fn getFileAsString(fileStr: &str) -> String {
let path = Path::new(fileStr);
let display = path.display();
let mut fileContents = String::new();
let mut file = match File::open(&path) {
Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
Ok(file) => file,
};
match file.read_to_string(&mut fileContents) {
Err(why) => panic!("couldn't read {}: {}", display, Error::description(&why)),
Ok(_) => fileContents
}
}
However I get the error:
error[E0277]: the trait bound `iron::Headers: iron::modifier::Modifier<iron::Response>` is not satisfied
--> src/main.rs:24:12
|
24 | Ok(Response::with((status::Ok, string, headers)))
| ^^^^^^^^^^^^^^ the trait `iron::modifier::Modifier<iron::Response>` is not implemented for `iron::Headers`
|
= note: required because of the requirements on the impl of `iron::modifier::Modifier<iron::Response>` for `(hyper::status::StatusCode, std::string::String, iron::Headers)`
= note: required by `iron::Response::with`
Why am I not able to pass headers into this tuple to be modified by the Request builder?

You can modify the headers on the Response object:
fn hello_world(_: &mut Request) -> IronResult<Response> {
let string = get_file_as_string("./public/index.html");
let mut resp = Response::with((status::Ok, string));
resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
Ok(resp)
}
To figure out the original error, let's check the error message:
error[E0277]: the trait bound `iron::Headers: iron::modifier::Modifier<iron::Response>` is not satisfied
--> src/main.rs:24:12
|
24 | Ok(Response::with((status::Ok, string, headers)))
| ^^^^^^^^^^^^^^ the trait `iron::modifier::Modifier<iron::Response>` is not implemented for `iron::Headers`
|
= note: required because of the requirements on the impl of `iron::modifier::Modifier<iron::Response>` for `(hyper::status::StatusCode, std::string::String, iron::Headers)`
= note: required by `iron::Response::with`
The first line tells us the immediate issue: iron::Headers does not implement the trait iron::modifier::Modifier<iron::Response>. If we check the documentation for Headers, we can see under the Trait Implementations section that it indeed does not implement Modifier.
We can then look at the problem from the other end: what does implement Modifier? The docs for Modifier, when built in conjunction with Iron, answer that question. One thing we can see is:
impl<H> Modifier<Response> for Header<H>
where
H: Header + HeaderFormat,
This leads to an alternate possibility:
use iron::modifiers::Header;
fn hello_world(_: &mut Request) -> IronResult<Response> {
let string = get_file_as_string("./public/index.html");
let content_type = Header(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
Ok(Response::with((status::Ok, string, content_type)))
}
And if we look at the implementation of Modifier for Header:
fn modify(self, res: &mut Response) {
res.headers.set(self.0);
}
It just sets the headers as we did above.
FYI, Rust style is snake_case for variables and methods and Error::description(&why) is normally written why.description().

Related

Share state between actix-web server and async closure

I want to periodically fetch data (using asynchronous reqwest), which is then served at an http endpoint using actix-web as a server.
(I have a data source that has a fixed format, that I want to have read by a service that require a different format, so I need to transform the data.)
I've tried to combine actix concepts with the thread sharing state example from the Rust book, but I don't understand the error or how to solve it.
This is the code minified as much as I was able:
use actix_web::{get, http, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Mutex<AppState>>) -> impl Responder {
let state = data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.to_owned())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let status_string = get_state().await.unwrap();
let app_data = Arc::new(Mutex::new(web::Data::new(AppState {
status: status_string,
})));
let app_data1 = Arc::clone(&app_data);
actix_web::rt::spawn(async move {
loop {
println!("I get executed every 2-ish seconds!");
sleep(Duration::from_millis(2000)).await;
let res = get_state().await;
let mut app_data = app_data1.lock().unwrap();
// Edit 2: this line is not accepted by the compiler
// Edit 2: *app_data.status = res.unwrap();
// Edit 2: but this line is accepted
*app_data = web::Data::new(AppState { status: res });
}
});
let app_data2 = Arc::clone(&app_data);
// Edit 2: but I get an error here now
HttpServer::new(move || App::new().app_data(app_data2).service(index))
.bind(("127.0.0.1", 9090))?
.run()
.await
}
async fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new().get("http://ipecho.net/plain".to_string());
let status = client.send().await?.text().await?;
println!("got status: {status}");
Ok(status)
}
But I get the following error:
error[E0308]: mismatched types
--> src/main.rs:33:32
|
33 | *app_data.status = res.unwrap();
| ---------------- ^^^^^^^^^^^^ expected `str`, found struct `String`
| |
| expected due to the type of this binding
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> src/main.rs:33:13
|
33 | *app_data.status = res.unwrap();
| ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: the left-hand-side of an assignment must have a statically known size
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
Why do I suddenly get a str? Is there an easy fix or is my approach to solving this wrong?
Edit: Maybe removing the * is the right way to go, as Peter Hall suggests, but that gives me the following error instead:
error[E0594]: cannot assign to data in an `Arc`
--> src/main.rs:33:13
|
33 | app_data.status = res.unwrap();
| ^^^^^^^^^^^^^^^ cannot assign
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<AppState>`
error[E0507]: cannot move out of `app_data2`, a captured variable in an `Fn` closure
--> src/main.rs:38:49
|
37 | let app_data2 = Arc::clone(&app_data);
| --------- captured outer variable
38 | HttpServer::new(move || App::new().app_data(app_data2).service(index))
| ------- ^^^^^^^^^ move occurs because `app_data2` has type `Arc<std::sync::Mutex<Data<AppState>>>`, which does not implement the `Copy` trait
| |
| captured by this `Fn` closure
Some errors have detailed explanations: E0507, E0594.
For more information about an error, try `rustc --explain E0507`.
Edit 2: I now get the following error (code changes commented with 'Edit 2' above):
error[E0507]: cannot move out of `app_data2`, a captured variable in an `Fn` closure
--> src/main.rs:46:49
|
45 | let app_data2 = app_data.clone();
| --------- captured outer variable
46 | HttpServer::new(move || App::new().app_data(app_data2).service(index))
| ------- ^^^^^^^^^ move occurs because `app_data2` has type `Arc<Mutex<Data<AppState>>>`, which does not implement the `Copy` trait
| |
| captured by this `Fn` closure
For more information about this error, try `rustc --explain E0507`.
My Cargo.toml dependencies:
[dependencies]
actix-web = "4.2.1"
reqwest = "0.11.12"
tokio = "1.21.2"
async solution
I had my types mixed up a bit, having the app state as Arc<Mutex<T>> seemed to be the way to go, maybe it would be better with Arc<RwLock<T>>.
use actix_web::{get, http, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Arc<Mutex<AppState>>>) -> impl Responder {
let state = data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.to_owned())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let status_string = get_state().await.unwrap();
let app_data = Arc::new(Mutex::new(AppState {
status: status_string,
}));
let app_data1 = app_data.clone();
actix_web::rt::spawn(async move {
loop {
println!("I get executed every 2-ish seconds!");
sleep(Duration::from_millis(2000)).await;
let res = get_state().await.unwrap();
let mut app_data = app_data1.lock().unwrap();
*app_data = AppState { status: res };
}
});
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_data.clone()))
.service(index)
})
.bind(("127.0.0.1", 9090))?
.run()
.await
}
async fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new().get("http://ipecho.net/plain".to_string());
let status = client.send().await?.text().await?;
println!("got status: {status}");
Ok(status)
}
async/sync solution
Instead of doing the async get with reqwest I have a solution with the synchronous crate minreq (that I found after a lot of searching). I also chose to not use the #[actix_web::main] macro, and instead start the runtime explicitly at the end of my main function.
use actix_web::{get, http, rt, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Arc<Mutex<AppState>>>) -> impl Responder {
let state = &data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.clone())
}
fn main() -> std::io::Result<()> {
let status_string = get_state().unwrap();
let app_data = Arc::new(Mutex::new(AppState {
status: status_string,
}));
let app_data1 = Arc::clone(&app_data);
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(2000));
let res = get_state().unwrap();
let mut app_data = app_data1.lock().unwrap();
*app_data = AppState { status: res };
});
rt::System::new().block_on(
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_data.clone()))
.service(index)
})
.bind(("127.0.0.1", 9090))?
.run(),
)
}
fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let resp = minreq::get("http://ipecho.net/plain").send().unwrap();
let state = resp.as_str().unwrap();
Ok(state.to_string())
}

Clashing types, crossterm::Result and core::Result error[E0107]:

I know the issue is that I have two Result types from different libraries but can't find how to fix it.
[dependencies]
crossterm = "0.23"
time = "0.3.9"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["blocking", "json"] }
use time::Instant;
use std::collections::HashMap;
use crossterm::{
event::{self, Event, KeyCode, KeyEvent},
Result,
};
pub fn read_char() -> Result<char> {
loop {
if let Event::Key(KeyEvent {
code: KeyCode::Char(c),
..
}) = event::read()?
{
return Ok(c);
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let instant = Instant::now();
let response = reqwest::blocking::get("https://httpbin.org/ip")?
.json::<HashMap<String, String>>()?;
let duration = instant.elapsed();
println!("ns = {:?}, response: {:#?}, ", duration.whole_nanoseconds(), response);
// Any key to continue
println!("Press any key to continue:");
println!("{:?}", read_char());
Ok(())
}
Gives the error:
error[E0107]: this type alias takes 1 generic argument but 2 generic arguments were supplied
--> src\main.rs:20:14
|
20 | fn main() -> Result<(), Box<dyn std::error::Error>> {
| ^^^^^^ -------------------------- help: remove this generic argument
| |
| expected 1 generic argument
How do I fix this? I have searched but am likely looking for incorrect terms e.g. namespace alias and core::Result error[E0107] is not really helping.
I have tried this without success:
fn main() -> core::Result<(), Box<dyn std::error::Error>> {
You have crossterm ::Result in scope, so you would have to disambiguate the result you want to return, otherwise it just thinks you want to return the crossterm type:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
...
Ok(())
}

Cannot call read on std::net::TcpStream due to unsatisfied trait bounds

SOLUTION:
I needed to add "use std::io::prelude::*;" to my code. I do not know why.
I am trying to read from an std::net::TcpStream but I recieve this error when calling stream.read(&buf).unwrap;
the method read exists for struct std::net::TcpStream, but its
trait bounds were not satisfied method cannot be called on
std::net::TcpStream due to unsatisfied trait bounds note: the
following trait bounds were not satisfied:
std::net::TcpStream: futures::AsyncRead
which is required by std::net::TcpStream: futures::AsyncReadExt help: items from traits can only be used if the
trait is in scoperustc(E0599) main.rs(31, 16): method cannot be called
on std::net::TcpStream due to unsatisfied trait bounds tcp.rs(49,
1): doesn't satisfy std::net::TcpStream: futures::AsyncReadExt
tcp.rs(49, 1): doesn't satisfy std::net::TcpStream: futures::AsyncRead mod.rs(580, 8): the method is available for
std::boxed::Box<std::net::TcpStream> here
Code:
use irc::client::prelude::*;
use futures::prelude::*;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
use std::io;
use futures::{AsyncRead, AsyncReadExt};
const NAME: &str = "nickname";
#[derive(Debug)]
struct DCC {
ip: IpAddr,
port: u16,
}
impl DCC {
fn from_msg(msg: &str) -> Result<DCC, std::num::ParseIntError> {
let msg_split: Vec<&str> = msg.split_whitespace().collect();
let ip: u32 = msg_split[3].parse()?;
let ip_addr: IpAddr = IpAddr::V4(Ipv4Addr::from(ip));
let port_num: u16 = msg_split[4].parse()?;
let dcc = DCC{
ip: ip_addr,
port: port_num,
};
return Ok(dcc);
}
async fn connect(&self) -> Result<(), io::Error>{
let socket_addr = SocketAddr::new(self.ip, self.port);
let mut socket = TcpStream::connect(socket_addr)?;
let mut buf = vec![];
socket.read(&buf).unwrap();
return Err(io::Error::new(io::ErrorKind::Other, "oh no!"));
}
}
#[tokio::main]
async fn irc_get(name: &str) -> Result<String, irc::error::Error>{
let config = Config {
nickname: Some(NAME.to_owned()),
server: Some("irc.irchighway.net".to_owned()),
port: Some(6667),
use_tls: Some(false),
channels: vec!["#ebooks".to_owned()],
..Config::default()
};
let mut client = Client::from_config(config).await?;
client.identify()?;
let mut stream = client.stream()?;
//waits for server to log us in and then sends the search request
loop{
let m = match stream.next().await{
Some(v) => v,
None => panic!("at the disco")
};
let message = match &m {
Ok(message) => match &message.command {Command::NOTICE(_s1, s2)=> {print!("{:?} \n", s2); message}, _ => message},
Err(_e) => panic!("at the disco")};
match &message.command{
Command::NOTICE(_s, msg) => { if msg.contains("Welcome to #ebooks"){break}},
_=> ()
}
}
client.send_privmsg("#ebooks", format!["#Search {}", name])?;
loop{
let m = match stream.next().await.transpose()?{
Some(m) => m,
None => panic!("at the disco")
};
match &m.command{
Command::PRIVMSG(nm, msg) => if nm == NAME {println!("{:?}",m); return Ok(String::from(msg))},
_ => ()
}
}
}
fn main() {
let dcc = DCC::from_msg(&irc_get(&"romeo and juliet").unwrap()[..]);
println!("{:?}", dcc);
}
I'm fairly new at rust and based on all of the examples in the documentation I think I'm using .read correctly. My only thought is that maybe it's because I'm trying to write the code in the impl, but I don't know if rust treats that differently. It also fails with "async fn connect..." and with "fn connect...".
The compiler was telling you the solution:
help: items from traits can only be used if the trait is in scope
You need to import the traits in order to use them:
use futures::{AsyncRead, AsyncReadExt};
Also you would probably want to use tokio::TcpStream which is async and not the std one.

How do I use PickleDB with Rocket/Juniper Context?

I'm trying to write a Rocket / Juniper / Rust based GraphQL Server using PickleDB - an in-memory key/value store.
The pickle db is created / loaded at the start and given to rocket to manage:
fn rocket() -> Rocket {
let pickle_path = var_os(String::from("PICKLE_PATH")).unwrap_or(OsString::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Bin;
let pickle_db: PickleDb = match Path::new(&pickle_path).exists() {
false => PickleDb::new(pickle_path, pickle_db_dump_policy, pickle_serialization_method),
true => PickleDb::load(pickle_path, pickle_db_dump_policy, pickle_serialization_method).unwrap(),
};
rocket::ignite()
.manage(Schema::new(Query, Mutation))
.manage(pickle_db)
.mount(
"/",
routes![graphiql, get_graphql_handler, post_graphql_handler],
)
}
And I want to retrieve the PickleDb instance from the Rocket State in my Guard:
pub struct Context {
pickle_db: PickleDb,
}
impl juniper::Context for Context {}
impl<'a, 'r> FromRequest<'a, 'r> for Context {
type Error = ();
fn from_request(_request: &'a Request<'r>) -> request::Outcome<Context, ()> {
let pickle_db = _request.guard::<State<PickleDb>>()?.inner();
Outcome::Success(Context { pickle_db })
}
}
This does not work because the State only gives me a reference:
26 | Outcome::Success(Context { pickle_db })
| ^^^^^^^^^ expected struct `pickledb::pickledb::PickleDb`, found `&pickledb::pickledb::PickleDb`
When I change my Context struct to contain a reference I get lifetime issues which I'm not yet familiar with:
15 | pickle_db: &PickleDb,
| ^ expected named lifetime parameter
I tried using 'static which does make rust quite unhappy and I tried to use the request lifetime (?) 'r of the FromRequest, but that does not really work either...
How do I get this to work? As I'm quite new in rust, is this the right way to do things?
I finally have a solution, although the need for unsafe indicates it is sub-optimal :)
#![allow(unsafe_code)]
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::env;
use std::path::Path;
use std::time::Duration;
pub static mut PICKLE_DB: Option<PickleDb> = None;
pub fn cache_init() {
let pickle_path = env::var(String::from("PICKLE_PATH")).unwrap_or(String::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Json;
let pickle_db = match Path::new(&pickle_path).exists() {
false => PickleDb::new(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
),
true => PickleDb::load(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
)
.unwrap(),
};
unsafe {
PICKLE_DB = Some(pickle_db);
}
}
pub fn cache_get<V>(key: &str) -> Option<V>
where
V: DeserializeOwned + std::fmt::Debug,
{
unsafe {
let pickle_db = PICKLE_DB
.as_ref()
.expect("cache uninitialized - call cache_init()");
pickle_db.get::<V>(key)
}
}
pub fn cache_set<V>(key: &str, value: &V) -> Result<(), pickledb::error::Error>
where
V: Serialize,
{
unsafe {
let pickle_db = PICKLE_DB
.as_mut()
.expect("cache uninitialized - call cache_init()");
pickle_db.set::<V>(key, value)?;
Ok(())
}
}
This can be simply imported and used as expected, but I think I'll run into issues when the load gets to high...

How to write an asynchronous recursive walkdir function with an asynchronous callback

I'm trying to write an async function that will traverse the filesystem tree, recursively, and calls an asynchronous callback for each file found.
This is for a learning effort, I have no real use case.
Here is what I have so far:
use async_std::{
fs::{self, *},
path::*,
prelude::*,
}; // 1.5.0, features = ["unstable"]
use futures::{
executor::block_on,
future::{BoxFuture, FutureExt},
}; // 0.3.4
use std::{marker::Sync, pin::Pin};
fn main() {
fn walkdir<F>(path: String, cb: &'static F) -> BoxFuture<'static, ()>
where
F: Fn(&DirEntry) -> BoxFuture<()> + Sync + Send,
{
async move {
let mut entries = fs::read_dir(&path).await.unwrap();
while let Some(path) = entries.next().await {
let entry = path.unwrap();
let path = entry.path().to_str().unwrap().to_string();
if entry.path().is_file().await {
cb(&entry).await
} else {
walkdir(path, cb).await
}
}
}
.boxed()
}
let foo = async {
walkdir(".".to_string(), &|entry: &DirEntry| async {
async_std::println!(">> {}\n", &entry.path().to_str().unwrap()).await
})
.await
};
block_on(foo);
}
I get this far by some sort of trial and error, but now I'm stuck on async closure callback with this error
warning: unused import: `path::*`
--> src/main.rs:3:5
|
3 | path::*,
| ^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: unused import: `pin::Pin`
--> src/main.rs:10:25
|
10 | use std::{marker::Sync, pin::Pin};
| ^^^^^^^^
error[E0308]: mismatched types
--> src/main.rs:33:54
|
33 | walkdir(".".to_string(), &|entry: &DirEntry| async {
| ______________________________________________________^
34 | | async_std::println!(">> {}\n", &entry.path().to_str().unwrap()).await
35 | | })
| |_________^ expected struct `std::pin::Pin`, found opaque type
|
= note: expected struct `std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = ()> + std::marker::Send>>`
found opaque type `impl core::future::future::Future`
use async_std::{
fs::{self, *},
path::*,
prelude::*,
}; // 1.5.0
use futures::{future::{Future, FutureExt, LocalBoxFuture}, executor}; // 0.3.4
fn main() {
async fn walkdir<R>(path: impl AsRef<Path>, mut cb: impl FnMut(DirEntry) -> R)
where
R: Future<Output = ()>,
{
fn walkdir_inner<'a, R>(path: &'a Path, cb: &'a mut dyn FnMut(DirEntry) -> R) -> LocalBoxFuture<'a, ()>
where
R: Future<Output = ()>,
{
async move {
let mut entries = fs::read_dir(path).await.unwrap();
while let Some(path) = entries.next().await {
let entry = path.unwrap();
let path = entry.path();
if path.is_file().await {
cb(entry).await
} else {
walkdir_inner(&path, cb).await
}
}
}.boxed_local()
}
walkdir_inner(path.as_ref(), &mut cb).await
}
executor::block_on({
walkdir(".", |entry| async move {
async_std::println!(">> {}", entry.path().display()).await
})
});
}
Notable changes:
Take in AsRef<Path> instead of a String and a generic closure instead of a trait object reference
Change the closure type to be FnMut as it's more permissive
The closure returns any type that is a future.
There's an inner implementation function that hides the ugly API required for recursive async functions.
The callback takes the DirEntry by value instead of by reference.
See also:
How to asynchronously explore a directory and its sub-directories?
How to using async fn callback in rust

Resources