dependabot[bot] on cargo
Update cookie requirement from … (compare)
msrd0 on main
Bump linked-hash-version to 0.5… (compare)
dependabot[bot] on cargo
error[E0507]: cannot move out of
state` which is behind a mutable reference*state
has type gotham::state::State
, which does not implement the Copy
trait`
pub fn my_handler(state: State) -> (State, impl IntoResponse) {
create_empty_response
, they might panic and you can't really do anything about it
Hello all! I'm new to Rust (and Gotham!) and am building a server using Gotham - and loving it so far.
I have a small question - I've set up Gotham to use asynchronous handlers and I find that a lot of my await
blocks have code repetition:
async fn next_node(state: State) -> HandlerResult {
let resp = reqwest::get("http://livenode121:8000/successor/").await;
let resp = match resp {
Ok(resp) => resp,
Err(e) => return Err((state, format!(" {}", e))),
};
let result = resp.text().await;
let result = match result {} // same code block as above!
let response = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, result);
Ok((state, response))
}
How do I avoid this repetition? I'd love to use await?
but that doesn't work as Rust cannot figure out how to return the state
from the ?
.
await.unwrap()
but I doubt that's idiomatic and the intended way to do things)
async fn next_node(state: &mut State) -> Result<Response<Body>, HandlerError>
and register them using .to_async_borrowing
. That way you should be able to use the ?
shorthand
Hi, I believe I now have a strong understanding of Gotham's fundamentals. However, with my knowledge, I'm kind of stuck on what I wish to achieve.
I won't go into too many details of what I am trying to do to keep things simple, but basically the gist is that my webserver needs to contact its own APIs as part of the startup process.
So here's a snippet of the code:
fn main() {
let chord = initialize_node();
let addr = format!("0.0.0.0:{}", PORT);
println!("Listening for requests at http://{}", addr);
gotham::start(addr, router(chord));
// now that we know the webserver is up and running, do something else with `chord`.
chord.update_others()
}
Basically, how do I ensure that the update_others()
function is called only when the webserver is up and running? Is this possible?
gotham::start
actually both starts the server and runs it until termination, so running something after it does not match your use case.
thread.sleep
so that by the time the thread starts processing stuff, the server should have started accepting connections. Shitty solution, but looks like it can't be helped.
TestServer
for that :-)
gotham-middleware-basicauth
on crates.io but I'm not sure that's quite what I want (it also seems to want gotham 0.4 so is maybe out of date). I don't need a fancy user management system or anything - it might have up to two users one day, but I'd prefer not having to implement the login flow etc. from scratch if there's something reasonable that already exists. Thanks for any suggestions!
Hi all, I'm struggling using the StateMiddleware
when I use async fn
handler functions.
Here's what works using a regular, synchronous handler function:
#[macro_use]
extern crate gotham_derive;
use gotham::middleware::state::StateMiddleware;
use gotham::pipeline::single_middleware;
use gotham::pipeline::single::single_pipeline;
use gotham::router::builder::*;
use gotham::router::Router;
use gotham::state::{FromState, State};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use std::sync::mpsc::SyncSender;
#[derive(Debug)]
pub struct Record {
field: String,
}
impl Record {
pub fn new(field: String) -> Self {
Self {
field: field
}
}
}
#[derive(Clone, StateData)]
pub struct SenderState {
inner: Arc<Mutex<SyncSender<Record>>>,
}
impl SenderState {
pub fn new(sender: SyncSender<Record>) -> Self {
Self {
inner: Arc::new(Mutex::new(sender)),
}
}
fn send(&self, record: Record) {
let tx = &*(self.inner.lock().unwrap());
tx.send(record).unwrap();
}
}
fn index(state: State) -> (State, String) {
let wc = SenderState::borrow_from(&state);
wc.send(Record::new());
(state, String::from(""))
}
pub fn router(sender_state: SenderState) -> Router {
let middleware = StateMiddleware::new(sender_state);
let pipeline = single_middleware(middleware);
let (chain, pipelines) = single_pipeline(pipeline);
build_router(chain, pipelines, |route| {
route.post("/").to(index);
})
}
#[tokio::main]
pub async fn main() {
let (tx, rx): (SyncSender<Record>, Receiver<Record>) = sync_channel(10);
let wc = SenderState::new(tx);
gotham::start(addr, router(wc));
}
Now, as far as I understand and can tell, doing so is a bad idea, since I'm blocking the event loop.
So, I figured, I'll make the handler function async
:
#[macro_use]
extern crate gotham_derive;
use gotham::middleware::state::StateMiddleware;
use gotham::pipeline::single_middleware;
use gotham::pipeline::single::single_pipeline;
use gotham::router::builder::*;
use gotham::router::Router;
use gotham::state::{FromState, State};
use futures::lock::Mutex;
use std::sync::Arc;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use std::sync::mpsc::SyncSender;
use gotham::hyper::{Body, Response, StatusCode};
#[derive(Debug)]
pub struct Record {
field: String,
}
impl Record {
pub fn new(field: String) -> Self {
Self {
field: field
}
}
}
#[derive(Clone, StateData)]
pub struct SenderState {
inner: Arc<Mutex<SyncSender<Record>>>,
}
impl SenderState {
pub fn new(sender: SyncSender<Record>) -> Self {
Self {
inner: Arc::new(Mutex::new(sender)),
}
}
async fn send(self, record: Record) {
self.inner.lock().await.send(record).await.unwrap();
}
}
async fn index(state: State) -> HandlerResult {
let wc = SenderState::borrow_from(&state);
wc.send(Record::new()).await;
Ok((
state,
Response::builder()
.status(StatusCode::ACCEPTED)
.body(Body::empty())
.unwrap(),
))
}
pub fn router(sender_state: SenderState) -> Router {
let middleware = StateMiddleware::new(sender_state);
let pipeline = single_middleware(middleware);
let (chain, pipelines) = single_pipeline(pipeline);
build_router(chain, pipelines, |route| {
route.post("/").to_async(index);
})
}
#[tokio::main]
pub async fn main() {
let (tx, rx): (SyncSender<Record>, Receiver<Record>) = sync_channel(10);
let wc = SenderState::new(tx);
gotham::start(addr, router(wc));
}
Now, I'm getting several errors in the fn router()
:
the type `std::cell::UnsafeCell<tokio::sync::mpsc::Sender<record::Record>>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
`std::cell::UnsafeCell<tokio::sync::mpsc::Sender<record::Record>>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
help: within `server::SenderState`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<tokio::sync::mpsc::Sender<record::Record>>`
note: required because it appears within the type `futures::lock::Mutex<tokio::sync::mpsc::Sender<record::Record>>`
note: required because it appears within the type `alloc::sync::ArcInner<futures::lock::Mutex<tokio::sync::mpsc::Sender<record::Record>>>`
note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<futures::lock::Mutex<tokio::sync::mpsc::Sender<record::Record>>>>`
note: required because it appears within the type `std::sync::Arc<futures::lock::Mutex<tokio::sync::mpsc::Sender<record::Record>>>`
note: required because it appears within the type `server::SenderState`
note: required by `gotham::middleware::state::StateMiddleware::<T>::new`rustc(E0277)
I think, I have an idea why this is an issue, but I'm fairly new to Rust and don't really understand what's going on.
Any help on this would be greatly appreciated.
UnsafeCell
) must not be used across an unwind bound. This happens whenever one of your handler functions panics, because the gotham server catches and unwinds your panic so that the server doesn't crash but rather sends a 500 response. In other words, when one HTTP request fails (because of a panic), it must not leave the server in an invalid state, so that it can continue handling further requests, and in your case the compiler is unable to prove that
gotham::start
creates its own tokio runtime and does not use the one spawned by #[tokio::main]
. You might run into weird issue when running two runtimes at the same time
IntoResponse
?
askama_gotham
crate? if so, its latest release is using gotham 0.5, so it might implement the wrong IntoResponse
trait if you are also using the latest gotham version.
route.get("/subscribe/:address*")
or similar
init_server
if you want to have a custom tokio runtime, that you can use both for gotham and your custom scheduled task