Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Sep 27 13:46
    codecov[bot] commented #563
  • Sep 27 13:29

    msrd0 on master

    Make linked-hash-map dependency… (compare)

  • Sep 27 13:29
    msrd0 closed #562
  • Sep 27 13:29
    codecov[bot] commented #562
  • Sep 27 13:18
    msrd0 labeled #563
  • Sep 27 13:18
    msrd0 opened #563
  • Sep 27 13:18
    msrd0 milestoned #563
  • Sep 27 13:11
    msrd0 labeled #562
  • Sep 27 13:11
    msrd0 labeled #562
  • Sep 27 13:11
    msrd0 milestoned #562
  • Sep 27 13:11
    msrd0 opened #562
  • Sep 27 12:59

    msrd0 on master

    Put session middleware behind a… (compare)

  • Sep 27 12:59
    msrd0 closed #560
  • Sep 24 17:55
    msrd0 edited #560
  • Sep 24 14:27
    codecov[bot] commented #560
  • Sep 24 14:12
    codecov[bot] commented #560
  • Sep 24 14:12
    msrd0 synchronize #560
  • Sep 24 14:11

    msrd0 on master

    Remove unneccessary itertools d… (compare)

  • Sep 24 14:11
    msrd0 closed #559
  • Sep 24 13:25
    codecov[bot] commented #560
Pavan Kumar Sunkara
@pksunkara
@tanriol:matrix.org No
I think this was even before that
Pavan Kumar Sunkara
@pksunkara
But yeah, I guess people were looking for that to implement the to_async_borrowing
I wasn't following 0.5.0 release since I gave up on gotham
Marwan Rabbâa
@waghanza
congrats for new release @msrd0
1 reply
Rohan Gautam
@RohanGautam
hey guys, I'm a little confused on how to pre-populate the state before handing it to a response handler. For example, use case would be if i have a mpsc channel and want to pass over a clone of the transmitter to the handler via it's state. Any thoughts/ resources you guys could point me to?
So far, seems like i would have to use middleware for it, but not sure if that's the simplest way to do it
Dominic
@msrd0:msrd0.de
[m]
if you want to put a type into the state after the router and before the request handler, you'll have to use a middleware. The easiest middleware you can use is StateMiddleware that just puts a clone of the value you initialize it with into the state for every request
tanriol
@tanriol:matrix.org
[m]
@alsuren: Thank you for the link :-)
Brian Moelk
@bmoelk
I'm hacking on my first gotham project (and new to rust), is there an example on how to unit test handlers directly?
Dominic
@msrd0:msrd0.de
[m]
@bmoelk: I think there's no such example as the recommended way to test handlers is through the test server. However, if you really want to test the handlers directly, you can use State::with_new (note that this method is public but hidden from documentation), similar to how we test route matchers: https://github.com/gotham-rs/gotham/blob/master/gotham/src/router/route/matcher/content_type.rs#L150
Note However that this means no middlewares will run (so no cookie parsing etc) and you will have to place all the required parts into the state yourself (like headers, request body, etc)
Brian Moelk
@bmoelk
ok, I attempted to use State::with_new but ran into some core Rust newbie issues trying to get it all to work properly. I gave up and started testing the full request, which I have working. But that seemed somewhat lacking because I wanted to test the actual changes made to the DB however with_test_transactions it seems that's not possible. I'll give it another go and come back with specific issues. Thanks!
Brian Moelk
@bmoelk
I don't know how to work through this issue: error[E0507]: cannot move out ofstate` which is behind a mutable reference
--> src/handlers.rs:194:36
|
194 | my_handler(
state);
| ^^^^^^ move occurs because *state has type gotham::state::State, which does not implement the Copy trait`
handler declaration like: pub fn my_handler(state: State) -> (State, impl IntoResponse) {
Dominic
@msrd0:msrd0.de
[m]
@bmoelk: Sorry, that signature is actually incompatible. This is a short example how you could do it if you really want to: https://gist.github.com/msrd0/5c6ecbfaf2934b90a3cbb0706486eb6f
Do note however that this way of testing wasn't designed for handlers, so if you for example try to use helper functions like create_empty_response, they might panic and you can't really do anything about it
If at all possible, I'd recommend you place the logic for your database or whatever part you want to test into a separate function, test that independent of gotham, and use that tested function for your handler which you can then test using the test server provided by gotham
Brian Moelk
@bmoelk
So you're saying I should write clean code that can be easily tested? :)
Dominic
@msrd0:msrd0.de
[m]
basically xD
Brian Moelk
@bmoelk
Thanks for the example, that works for me.
Soham Dongargaonkar
@a3y3

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 ?.

(Also, I could use await.unwrap() but I doubt that's idiomatic and the intended way to do things)
Dominic
@msrd0:msrd0.de
[m]
@a3y3: Try changing your method signature to 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
Soham Dongargaonkar
@a3y3
Seems to work, thanks @msrd0:msrd0.de!
Soham Dongargaonkar
@a3y3

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?

tanriol
@tanriol:matrix.org
[m]
@a3y3: Note that gotham::start actually both starts the server and runs it until termination, so running something after it does not match your use case.
By "up and running" do you mean "the sockets are ready to accept connections" or something different?
Dominic
@msrd0:msrd0.de
[m]
This is what gotham ultimately calls to start the server, and it is guaranteed to never return: https://github.com/gotham-rs/gotham/blob/master/gotham/src/lib.rs#L100
Soham Dongargaonkar
@a3y3
Yes, by up and running, I mean that my APIs can be called from anywhere (including by the server itself)
So there's no way to achieve what I want? I might look into using thread::spawn() then, with a small 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.
tanriol
@tanriol:matrix.org
[m]
You aren't doing that just for testing, are you?
If this is for testing, note that Gotham has a TestServer for that :-)
jman
@jman:kotze.city17.xyz
[m]
@a3y3: as a side-thought, you might consider for your use case wrapping the server startup with a service management (example docker, systemd, ...) and chain a call to the server itself by the service that handles the startup
Soham Dongargaonkar
@a3y3
Thanks for the suggestions everyone. No, this isn't for testing (I'm basically trying to model Chord, a distributed hash table protocol, using webservers). Might consider using the docker approach if the thread idea doesn't work out.
Chris Emerson
@jugglerchris
Hi! Are there any ready-made libraries for authentication with Gotham? I would like to have a restricted part of my site, including at least an endpoint for a script to post data to, and a page showing the data. I've found 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!
Dominic
@msrd0:msrd0.de
[m]
gotham has an "official" middleware for authentication using JWT: https://crates.io/crates/gotham_middleware_jwt (it's compatible with gotham 0.6, eventhough the readme says 0.3 ...)
Chris Emerson
@jugglerchris
Thanks. I had seen that but don't currently understand what JWTs are and how they're supposed to be used... If the docs.rs front page describes what the middleware does (return 400 or 401 for missing/invalid token) then that doesn't sound like what you'd want for a page for a user to visit in a browser. (Though maybe it's more for endpoints for programs to talk to?)
I'll at least try to learn what JWTs are, anyway!
Dominic
@msrd0:msrd0.de
[m]
basically a JWT is just a session id that contains information and a signature, so when the user logs in you'd return such a JWT to the user, and the middleware can then check if a valid JWT was included in the request, or return a 401 if not
Markus Holtermann
@MarkusH

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.

Dominic
@msrd0:msrd0.de
[m]
I have no idea why this happens with your async handler but not your sync one. I'm definitely not an expert in this topic, but I think what the error message is telling you is that some type (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
other than 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
Markus Holtermann
@MarkusH
Good to know about the second runtime. I switched to gotham::init_server(addr, router(tx)).await; instead
rushsteve1
@rushsteve1:matrix.org
[m]
Anybody here using Askama and might know why it says my template structs don't implement IntoResponse?
Dominic
@msrd0:msrd0.de
[m]
rushsteve1: are you using the 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.
rushsteve1
@rushsteve1:matrix.org
[m]
Oh hmm
Dominic
@msrd0:msrd0.de
[m]
The version is already updated in their git repo (https://github.com/djc/askama/blob/e1d607f99349aa98dcf7720e0d3e74f8a412c255/askama_gotham/Cargo.toml), maybe nicely asks in their gitter chat if they could publish that to crates.io
rushsteve1
@rushsteve1:matrix.org
[m]
Ah, yep that was it, thanks!
Building with Gotham 0.5.0 works fine. As does switching to the Askama Git repo, which is what I'll do for now.