How do I access HttpRequest data inside a Future in Actix-web? - rust

I'd like to have an Actix Web handler which responds to a POST request by printing the POST body to the console and constructing an HTTP response that contains the current URL from the request object.
When reading the request's POST body, futures seem to need to be involved. The closest I've gotten so far is:
fn handler(req: HttpRequest) -> FutureResponse<HttpResponse> {
req.body()
.from_err()
.and_then(|bytes: Bytes| {
println!("Body: {:?}", bytes);
let url = format!("{scheme}://{host}",
scheme = req.connection_info().scheme(),
host = req.connection_info().host());
Ok(HttpResponse::Ok().body(url).into())
}).responder()
}
This won't compile because the future outlives the handler, so my attempts to read req.connection_info() are illegal. The compiler error suggests I use the move keyword into the closure definition, i.e. .and_then(move |bytes: Bytes| {. This also won't compile because req gets moved on the req.body() call and is then captured after the move in the references constructing url.
What is a reasonable way of constructing a scope in which I have access to data attached to the request object (e.g. the connection_info) at the same time as access to the POST body?

The simplest solution is to not access it inside the future at all:
extern crate actix_web; // 0.6.14
extern crate bytes; // 0.4.8
extern crate futures; // 0.1.21
use actix_web::{AsyncResponder, FutureResponse, HttpMessage, HttpRequest, HttpResponse};
use bytes::Bytes;
use futures::future::Future;
fn handler(req: HttpRequest) -> FutureResponse<HttpResponse> {
let url = format!(
"{scheme}://{host}",
scheme = req.connection_info().scheme(),
host = req.connection_info().host(),
);
req.body()
.from_err()
.and_then(move |bytes: Bytes| {
println!("Body: {:?}", bytes);
Ok(HttpResponse::Ok().body(url).into())
})
.responder()
}
In case this is more than a quick hack for demonstration purposes, constructing URLs by concatenating strings is a terrible idea as it doesn't properly escape the values. You should be using a type that does that for you.

Related

Cannot solve compiler errors while extracting reqwest::Response from a reqwest::Client request

I'm having a hard time trying to figure out how to error handle this piece of code.
This is the endpoint of my actix_web api, up until now everything works:
pub async fn simple_moving_average() -> HttpResponse {
if Client::new()
.get("https://coinranking1.p.rapidapi.com/coins")
.header(
"X-RapidAPI-Key",
"MY_KEY")
.send()
.await
.is_err()
{
HttpResponse::BadRequest().finish();
}
HttpResponse::Ok().finish()
}
It's when I try to extract the data from the response that the compiler starts screaming at me:
[...]
let response = Client::new()
.get("https://coinranking1.p.rapidapi.com/coins")
.header(
"X-RapidAPI-Key",
"MY_KEY")
.send()
.await?
.json::<Root>()
.await?;
HttpResponse::Ok().finish()
}
cargo check ->
he ? operator can only be used in an async function that returns
Result or Option (or another type that implements FromResidual)
the trait FromResidual<Result<Infallible, reqwest::Error>> is not
implemented for HttpResponserustcE0277 simple_moving_average.rs(50,
65): this function should return Result or Option to accept ?
Root in .json::<Root>() is just a struct representing json payload. I generated it from this website, not sure if it's relevant
how do I solve that? My final goal here would be to return a json response containing the data I get from this request. I've tried implementing the ::thiserror crate but I cannot figure out how it works and honestly I would prefer to understand how error handling works before using some fast solution.
I've tried following what compiler suggests but I cannot seem to solve the situation.
I solved by using the dyn trait
correct code:
pub async fn simple_moving_average() ->
Result<HttpResponse, Box<dyn std::error::Error>>{ [...] }

Where does a variable passed to Reqwest's Result::read_to_string get the data from?

I am learning Rust and have been playing around with this example to perform an HTTP GET request and then display the data:
extern crate reqwest;
use std::io::Read;
fn run() -> Result<()> {
let mut res = reqwest::get("http://httpbin.org/get")?;
let mut body = String::new();
res.read_to_string(&mut body)?;
println!("Status: {}", res.status());
println!("Headers:\n{:#?}", res.headers());
println!("Body:\n{}", body);
Ok(())
}
I cannot understand how the variable body is actually ending up with the correct data. For headers and status, I can see the associated functions but for the body data it just uses read_to_string for the whole data?
The res object has a read_to_string() method which stores the response into the String that you pass it in
res.read_to_string(&mut body);
Edit: imported from my comment:
reqwest::Response 0.6.2 documentation states for Read for Response:
Read the body of the Response
which somehow seems missing from the documentation of the current version.

What's the easiest way to get the HTML output of an actix-web endpoint handler to be rendered properly?

I've defined an endpoint with actix-web like so:
#[derive(Deserialize)]
struct RenderInfo {
filename: String,
}
fn render(info: actix_web::Path<RenderInfo>) -> Result<String> {
// ...
}
App::new()
.middleware(middleware::Logger::Default())
.resource("/{filename}", |r| r.get().with(render))
The problem I've run into is that the raw HTML gets displayed in the browser rather than being rendered. I assume the content-type is not being set properly.
Most of the actix-web examples I saw used impl Responder for the return type, but I wasn't able to figure out how to fix the type inference issues that created. The reason seems to have something to do with file operations returning a standard failure::Error-based type. It looks like actix_web requires implementation of a special WebError to block unintended propagation of errors. For this particular instance, I don't really care, because it's more of an internal tool.
From the actix-web examples, use HttpResponse:
fn welcome(req: &HttpRequest) -> Result<HttpResponse> {
println!("{:?}", req);
// session
let mut counter = 1;
if let Some(count) = req.session().get::<i32>("counter")? {
println!("SESSION value: {}", count);
counter = count + 1;
}
// set counter to session
req.session().set("counter", counter)?;
// response
Ok(HttpResponse::build(StatusCode::OK)
.content_type("text/html; charset=utf-8")
.body(include_str!("../static/welcome.html")))
}

Use precomputed big object in actix-web route handler

Is there a way to make an actix-web route handler aware of a pre-computed heavy object, that is needed for the computation of result?
What I intend to do, in the end, is to avoid having to recompute my_big_heavy_object each time a request comes along, and instead compute it once and for all in main, there access it from the index method.
extern crate actix_web;
use actix_web::{server, App, HttpRequest};
fn index(_req: HttpRequest) -> HttpResponse {
// how can I access my_big_heavy_object from here?
let result = do_something_with(my_big_heavy_object);
HttpResponse::Ok()
.content_type("application/json")
.body(result)
}
fn main() {
let my_big_heavy_object = compute_big_heavy_object();
server::new(|| App::new().resource("/", |r| r.f(index)))
.bind("127.0.0.1:8088")
.unwrap()
.run();
}
First, create a struct which is the shared state for your application:
struct AppState {
my_big_heavy_object: HeavyObject,
}
It's better to create a context wrapper like this, rather than just using HeavyObject, so you can add other fields to it later if necessary.
A few objects in Actix will now need to interact with this, so you can make them aware of it by overriding the application state parameter in those types, for example HttpRequest<AppState>.
Your index handler can access the state through the HttpRequest's state property, and now looks like this:
fn index(req: HttpRequest<AppState>) -> HttpResponse {
let result = do_something_with(req.state.my_big_heavy_object);
HttpResponse::Ok()
.content_type("application/json")
.body(result)
}
When building the App, use the with_state constructor instead of new:
server::new(|| {
let app_state = AppState { my_big_heavy_object: compute_big_heavy_object() };
App::with_state(app_state).resource("/", |r| r.f(index))
})
.bind("127.0.0.1:8088")
.unwrap()
.run();
Note that the application state is assumed to be immutable. It sounds like you don't need any handlers to mutate it but if you did then you would have to use something like Cell or RefCell for interior mutability.

How do I start a web server in Rust with hyper?

I want to learn Rust by writing a reverse proxy with the hyper framework. My complete project is on GitHub. I'm stuck at starting a listener as explained in the documentation:
extern crate hyper;
use hyper::Client;
use hyper::server::{Server, Request, Response};
use std::io::Read;
fn pipe_through(req: Request, res: Response) {
let client = Client::new();
// Why does the response have to be mutable here? We never need to modify it, so we should be
// able to remove "mut"?
let mut response = client.get("http://drupal-8.localhost/").send().unwrap();
// Print out all the headers first.
for header in response.headers.iter() {
println!("{}", header);
}
// Now the body. This is ugly, why do I have to create an intermediary string variable? I want
// to push the response directly to stdout.
let mut body = String::new();
response.read_to_string(&mut body).unwrap();
print!("{}", body);
}
Server::http("127.0.0.1:9090").unwrap().handle(pipe_through).unwrap();
That does not work and fails with the following compile error:
error: expected one of `!` or `::`, found `(`
--> src/main.rs:23:13
|
23 | Server::http("127.0.0.1:9090").unwrap().handle(pipe_through).unwrap();
| ^
Why is my call to http() not correct? Shouldn't it create a new server as indicated in the documentation?
All expressions in Rust must be inside a function, so I need to start my server in fn main(). Then it works!

Resources