I need a way to make important data read-accessible, thread-safe throughout my application.
My web application in Rust with Actix-Web. Permission data is prefetched when the application starts. To be useful, the request cycle must be able to access this data and test if the current user.
Example:
#[get("/forums/{forum}/")]
pub async fn view_forum(client: ClientCtx) -> Result<impl Responder, Error> {
if !client.can_view_forum() {
// return error
}
// ...
The permission data is made available to the Actix-Web web server as app_data, so this might work.
#[get("/forums/{forum}/")]
pub async fn view_forum(
client: ClientCtx,
permissions: web::Data<PermissionData>,
) -> Result<impl Responder, Error> {
if !permissions.can_view_forum(client) {
// return error
}
// ...
I would like to avoid increasing the size of my route function signatures as much as possible. I will need permissions accessible everywhere the user is also accessible.
PermissionData is quite large and ClientCtx's user data is pulled on every request. Refetching data directly to the client every request is not practical.
Actix-Web's middleware system creates these structs during the request cycle. It works in such a way that lifetimes cannot be attached to the ClientUser.
pub struct ClientCtxInner<'a> {
pub client: Option<ClientUser>,
pub permission: &PermissionData, // Doesn't work.
pub request_start: Instant,
}
In short:
The user never needs to write to permissions. It just needs to read them.
Many users on all threads at all times will be reading this same data.
I can't use normal lifetimes.
I would very much prefer if the data is accessible directly through the Client struct for organization reasons.
This is where the middleware assembles the client.
fn call(&self, req: ServiceRequest) -> Self::Future {
let (httpreq, payload) = req.into_parts();
let cookies = Session::extract(&httpreq).into_inner();
let req = ServiceRequest::from_parts(httpreq, payload);
let permissions: Option<&PermissionData> = req.app_data::<PermissionData>(); // My data!
let ctx = ClientCtx::get_client_ctx(&mut *req.extensions_mut());
let fut = self.service.call(req);
async move {
match cookies {
Ok(cookies) => {
let result = authenticate_client_ctx(&cookies).await;
// Data needs to be attached here.
// ctx.0.borrow_mut().permissions = ???;
// Assign the user to our ClientCtx struct.
if let Some(user) = result {
ctx.0.borrow_mut().client = Some(user);
}
}
Err(e) => {
log::error!("ClientCtxMiddleware: Session::extract(): {}", e);
}
};
let result = fut.await?;
Ok(result)
}
.boxed_local()
}
Related
I am using actix-web to run a webserver and want to be able to mutate state through websocket messages.
My current way of using websockets is through implementing the handle method from actix::StreamHandler. However this limits my ability of passing data to it. How can I access the data (actix_web::web::Data) in my handle method?
The only way I can think of solving this issue is to somehow overwrite the function signature of handle, however that doesn't seem possible
Hers is some important code snippets, we have app_name and nonces in app_data:
// main.rs
let nonces = Arc::new(Mutex::new(nonces::Nonces::new()));
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_data::AppData {
app_name: String::from("Actix Web"),
nonces: Arc::clone(&nonces),
}))
...
// app_data.rs
pub struct AppData {
pub app_name: String,
pub nonces: Arc<Mutex<nonces::Nonces>>,
}
// ws.rs
struct Ws {
app_data: web::Data<app_data::AppData>,
}
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Ws {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
let app_name = &self.app_data.app_name;
let mut nonces = self.app_data.nonces.lock().unwrap();
println!(">>> {app_name}");
println!(">>> {:?}", nonces.nonces); // I have a nonces data in nonces
...
}
}
async fn index(
req: HttpRequest,
stream: web::Payload,
app_data: web::Data<app_data::AppData>,
) -> Result<HttpResponse, Error> {
ws::start(Ws { app_data: app_data.clone() }, &req, stream)
}
I'm currently writing a language server in rust and have the following problem: I want to add code completion sensitive to the code already been written. I followed the the example for the crate lsp-server I'm using. How can I get the text of the file currently in edit?
Sadly there is not much documentation. I've already tried to look my way through the GitHub repository of rust-analyzer but it is to overwhelming and I can't find a solution. I know how to get the file path but when I open the file I only get the saved version not the edited one.
Currently my code does not much differ from the example, but I add it anyway:
mod completion_data;
use std::error::Error;
use lsp_server::{Connection, ExtractError, Message, RequestId, Response};
use lsp_types::notification::{Initialized, Notification};
use lsp_types::request::{
CodeLensRequest, Completion, DocumentColor, DocumentHighlightRequest, HoverRequest, Initialize,
Request, ShowDocument,
};
use lsp_types::{
CompletionItem, CompletionList, CompletionOptions, TextDocumentChangeRegistrationOptions,
TextDocumentIdentifier, TextDocumentSyncCapability, TextDocumentSyncOptions, WorkspaceEdit,
};
use lsp_types::{InitializeParams, ServerCapabilities};
use serde::de::DeserializeOwned;
use crate::completion_data::get_completion_items;
fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
// Note that we must have our logging only write out to stderr.
eprintln!("starting generic LSP server");
// Create the transport. Includes the stdio (stdin and stdout) versions but this could
// also be implemented to use sockets or HTTP.
let (connection, io_threads) = Connection::stdio();
// Run the server and wait for the two threads to end (typically by trigger LSP Exit event).
let server_capabilities = serde_json::to_value(&ServerCapabilities {
completion_provider: Some(CompletionOptions {
trigger_characters: Some(vec!["&".to_string()]),
..Default::default()
}),
..Default::default()
})
.unwrap();
let initialization_params = connection.initialize(server_capabilities)?;
main_loop(connection, initialization_params)?;
io_threads.join()?;
// Shut down gracefully.
eprintln!("shutting down server");
Ok(())
}
fn main_loop(
connection: Connection,
params: serde_json::Value,
) -> Result<(), Box<dyn Error + Sync + Send>> {
let _params: InitializeParams = serde_json::from_value(params).unwrap();
for msg in &connection.receiver {
match msg {
Message::Request(request) => {
if connection.handle_shutdown(&request)? {
return Ok(());
}
match request.method.as_str() {
Completion::METHOD => {
let (id, compilation) = cast::<Completion>(request)?;
//TODO Get the current text and based on it suggest completion
}
_ => {}
}
}
Message::Response(resp) => {
eprintln!("got response: {:?}", resp);
}
Message::Notification(not) => {
eprintln!("got notification: {:?}", not);
}
}
}
Ok(())
}
fn cast<R>(
req: lsp_server::Request,
) -> Result<(RequestId, R::Params), ExtractError<lsp_server::Request>>
where
R: lsp_types::request::Request,
R::Params: serde::de::DeserializeOwned,
{
req.extract(R::METHOD)
}
I am trying to make two async api call and eventually get error E0597.
Here is a code:
async fn make_request() -> Result<()> {
.........
.........
.........
let mut result = client.get(uri).await?;
let some_key = result.headers().get("some_key");
let next_url = match some_key {
Some(url) => {
let some_result = client.get(Uri::from_static(url.to_str().unwrap())).await?
}
None => println!("....")
};
Ok(())
}
When I run this code the error "borrowed value does not live long enough argument requires that result is borrowed for `'static"
I have created a compile-able example based on your snipped to reproduce the error in the playground, and if you are able to do something like this in your question (for future reference), it usually helps you get more specific answers.
The Request passed into the function has no lifetime guarantees, so this will fail with the error you mentioned:
use http::{Request, Uri};
async fn make_request(result: &Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// `url` is a reference to the string in the "some_key" header
Some(url) => {
let some_result = Uri::from_static(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}
You can add that lifetime requirement, but that probably isn't what you need, and will likely give you the same error message, just in a different place:
async fn make_request_static(result: &'static Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// because the request is static, so can be `url`
Some(url) => {
let some_result = Uri::from_static(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}
Uri implements the FromStr trait, though, so you would be best off using that. There is no longer a lifetime requirement, so it can work with any string you pass in, even one which is currently borrowed:
// need to import the trait to use its methods
use std::str::FromStr;
async fn make_request_3(result: &Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// because the request is static, so can be `url`
Some(url) => {
let some_result = Uri::from_str(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}
I found in the docs an example of how to create global state, protected by Mutex, shared among processing threads that is made available to all your route handlers. Perfect! However, I prefer to use attributes attached to my functions to wire up my route handlers. I do not know the syntax (if permitted) to use attributed functions and also pass in the global state.
Here is the example from the actix-web docs, from https://docs.rs/actix-web/1.0.2/actix_web/web/struct.Data.html
use std::sync::Mutex;
use actix_web::{web, App};
struct MyData {
counter: usize,
}
/// Use `Data<T>` extractor to access data in handler.
fn index(data: web::Data<Mutex<MyData>>) {
let mut data = data.lock().unwrap();
data.counter += 1;
}
fn main() {
let data = web::Data::new(Mutex::new(MyData{ counter: 0 }));
let app = App::new()
// Store `MyData` in application storage.
.register_data(data.clone())
.service(
web::resource("/index.html").route(
web::get().to(index)));
}
Notice how the route handler named index is being passed the web::Data.
Now here are some snippets of my code.
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
pub mod request;
pub mod routes;
const SERVICE_NAME : &str = "Shy Rules Engine";
const SERVICE_VERSION : &str = "0.1";
#[get("/")]
fn index() -> impl Responder {
HttpResponse::Ok().body(format!("{} version {}", SERVICE_NAME, SERVICE_VERSION))
}
mod expression_execute {
#[post("/expression/execute")]
fn route(req: web::Json<ExpressionExecuteRequest>) -> HttpResponse {
// ... lots of code omitted ...
if response.has_error() {
HttpResponse::Ok().json(response)
}
else {
HttpResponse::BadRequest().json(response)
}
}
}
pub fn shy_service(ip : &str, port : &str) {
HttpServer::new(|| {
App::new()
.service(index)
.service(expression_execute::route)
})
.bind(format!("{}:{}", ip, port))
.unwrap()
.run()
.unwrap();
}
Notice how I am calling method App::service to wire up my route handlers.
Also notice how my route handler does not receive global state (because I have not yet added it to my app). If I used a similar pattern as the docs using register_data to create global App data, what changes do I make to my method signature, the get and post attributes and anything else so that I can pass that global state to the handler?
Or is it not possible using get and post attributes to gain access to global state?
The two cases you listed really don't have much difference:
//# actix-web = "1.0.8"
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use std::sync::Mutex;
const SERVICE_NAME : &str = "Shy Rules Engine";
const SERVICE_VERSION : &str = "0.1";
struct MyData {
counter: usize,
}
#[get("/")]
fn index(data: web::Data<Mutex<MyData>>) -> impl Responder {
let mut data = data.lock().unwrap();
data.counter += 1;
println!("Endpoint visited: {}", data.counter);
HttpResponse::Ok().body(format!("{} version {}", SERVICE_NAME, SERVICE_VERSION))
}
pub fn shy_service(ip : &str, port : &str) {
let data = web::Data::new(Mutex::new(MyData{ counter: 0 }));
HttpServer::new(move || {
App::new()
.register_data(data.clone())
.service(index)
})
.bind(format!("{}:{}", ip, port))
.unwrap()
.run()
.unwrap();
}
fn main() {
shy_service("127.0.0.1", "8080");
}
You can verify that it works by simply curl the http endpoint. For multiple extractors, you'll have to use tuple:
#[post("/expression/execute")]
fn route((req, data): (web::Json<ExpressionExecuteRequest>, web::Data<Mutex<MyData>>)) -> HttpResponse {
unimplemented!()
}
The Rouille hello world example shows how to use the router! macro for a fixed set of routes.
The following example code illustrates the need to be able to "bootstrap" routes from a database or from pluggable code - which I've currently been able to do with the Iron web framework:
pub struct Route {
pub http_method: String,
pub url_path: String,
pub callback_func: fn(_: &mut Request) -> IronResult<Response>,
}
impl Route {
pub fn new(m: String, u: String, f: fn(_: &mut Request) -> IronResult<Response>) -> Route {
Route {
http_method: m,
url_path: u,
callback_func: f,
}
}
}
fn main() {
// router is an Iron middleware
let mut router = Router::new();
// prepare routes for bootstrapping into the Iron router
let r1 = Route::new("get".to_string(), "/*".to_string(), my_callback_func);
let r2 = Route::new("get".to_string(), "/".to_string(), my_callback_func);
let mut routes = Vec::new();
routes.push(r1);
routes.push(r2);
for route in routes.iter_mut() {
if route.http_method == "get" {
// passes each route to the Iron router
router.get(&route.url_path, (&*route).callback_func);
} // else if, put, post, delete...
}
Iron::new(router).http("localhost:3000").unwrap();
}
fn my_callback_func(_: &mut Request) -> IronResult<Response> {
//...
}
(Playground)
Although I'm reading up on macros in Rust, I do not have a good enough understanding of Rouille's router! macro, Rust or macros in general, to figure out how to achieve the equivalent with Rouille.
If you examine the main source of the router macro, it's long-ish but not supremely complicated:
($request:expr, $(($method:ident) ($($pat:tt)+) => $value:block,)* _ => $def:expr) => {
{
let request = &$request;
// ignoring the GET parameters (everything after `?`)
let request_url = request.url();
let request_url = {
let pos = request_url.find('?').unwrap_or(request_url.len());
&request_url[..pos]
};
let mut ret = None;
$({
if ret.is_none() && request.method() == stringify!($method) {
ret = router!(__check_pattern request_url $value $($pat)+);
}
})+
if let Some(ret) = ret {
ret
} else {
$def
}
}
};
In words, it takes a request, zero-or-more patterns to match, and a default. It gets ahold of the URL, then dispatches to the other arms of the macro to see if the URL matches the path and does some recursive trickery to define some variables with components of the path. Whichever arm matches first will set the return value, and if nothing matches, the default will be used.
Unfortunately, the macro expects idents for the methods and the paths, so basically you cannot use it with expressions. This means we can't pass in variables or literals like "foo". This makes it very difficult for you.
So, we do what all good programmers do: copy and paste the code. Lifting chunks out of the macro and repurpose them:
#[macro_use]
extern crate rouille;
use rouille::Request;
use rouille::Response;
struct Route(&'static str, &'static str, fn(&Request) -> Response);
fn main() {
let routes = [
Route("GET", "/one", do_one),
Route("GET", "/two", do_two),
];
rouille::start_server("0.0.0.0:9080", move |request| {
let mut result = None;
let request = &request;
// ignoring the GET parameters (everything after `?`)
let request_url = request.url();
let request_url = {
let pos = request_url.find('?').unwrap_or(request_url.len());
&request_url[..pos]
};
for &Route(method, path, f) in &routes {
if result.is_none() {
// This checking of the path is terrible, limited, and hacky
if request.method() == method && request_url.ends_with(path) {
result = Some(f(request));
}
}
}
result.unwrap_or_else(|| Response::text("Default!"))
});
}
fn do_one(_: &Request) -> Response {
Response::text("hello world one")
}
fn do_two(_: &Request) -> Response {
Response::text("hello world two")
}
This runs the various handlers for /one, /two and everything else.
I'm no expert in Rouille, in fact I've never used it before today, but it certainly seems like you are trying to use it for something beyond what it is currently designed for. Perhaps this is deliberate and the authors are attempting to present a very opinionated tool. Perhaps it is accidental and they haven't thought of this use case. Perhaps it is temporary, and they just haven't gotten around to it.
Either way, I'd suggest asking the authors. If it's not an intended use-case, they can update the project docs to clearly state so. Otherwise they might create issues to implement the feature, and you could be instrumental in helping design it.