#How can I pass a value to hyper service function?

18 messages · Page 1 of 1 (latest)

alpine stirrup
#

Losing my mind a little here.
I'm attempting to pass a set of keys into my hyper service function in order to verify JWTs. I don't want to regenerate a HashMap and call on Google's servers every request.

When I try to do so: I'm getting a temporary value dropped while borrowed error. I've tried to mark the hashmap as static, wrap the function call in a LazyLock, and use join handles to ensure that the HashMap cannot go out of scope, but I can't seem to get anything to work.

Here's a rough outline of what I have.

//in main

tokio::task::spawn(async move {
  google_keys: &HashMap<String, (String, String)> = &jwt::get_google_auth_keys()
  loop{
    g_keys = google_keys;
    ...
    tokio::task::spawn( async move {
      ...
      serve_connection(io, service_fn(|req: Request<hyper::body::Incoming>| async {
                            service(req, g_keys).await
    })
  }
});

I tried using Axum instead of Hyper but I'm doing self-signed TLS and wasn't able to effectively debug it. If you think I'm using totally the wrong tools let me know.

#

I also tried wrapping it in an Arc but that didn't help

bright lake
#

what's the full error and code

bright lake
thorn basaltBOT
#

Run cargo check in a terminal

Note: If using rust analyzer you can click the "click for full compiler diagnostic" link in your editor.

Please post the full output of the above command, including the error title and any help or notes. An example of how this looks is:

error[E0308]: mismatched types
 --> src/main.rs:3:17
  |
3 | let foo: &i32 = bar;
  |          ----   ^^^ expected `&i32`, found integer
  |          |
  |          expected due to this
  |
help: consider borrowing here
  |
3 | let foo: &i32 = &bar;
  |                 +

When posting the error put it in a code block so it has nice formatting:

```rust
// error from cargo check here
```

Please do not post a screenshot. If the output is too long then use a paste tool like https://paste.rs/web

alpine stirrup
#
error[E0597]: `google_keys` does not live long enough
   --> src/main.rs:136:26
    |
132 |           let google_keys: HashMap<String, (String, String)> = jwt::get_google_auth_keys();
    |               ----------- binding `google_keys` declared here
...
136 |               let g_keys = &google_keys;
    |                            ^^^^^^^^^^^^ borrowed value does not live long enough
...
146 | /                 tokio::task::spawn( async move {
147 | |
148 | |                     let tls_scream = match tls_acceptor.accept(stream).await {
149 | |                         Ok(tls_stream) => tls_stream,
...   |
164 | |                     }
165 | |                 });
    | |__________________- argument requires that `google_keys` is borrowed for `'static`
...
168 |       }});
    |        - `google_keys` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.```
#

I'm getting confused because even when I move the keys outof this task so they're dropped at the end of the program, I still run into the same errors

bright lake
#

task::spawn expects 'static because it can't reason if the task will halt

#

the typical answer here would be using Arc and cloning it to the task

alpine stirrup
#

Can try recreating it

hoary crag
#

the async move { /* .. */ } on line 148 means that g_keys is moved into the async block. this is generally the right idea for solving this kind of issue, because it makes sure the async block takes ownership of the values it uses, instead of borrowing...
...except g_keys is itself a borrow. so the &HashMap<String, (String, String)> is moved into the async block, but the HashMap<String, (String, String)> stays put at line 134.

hoary crag
# alpine stirrup

(line numbers based on this version of the code, since it seems to have changed since then)

hoary crag
#

the straightforward fix is```diff

  •        let g_keys = &google_keys;
    
  •        let g_keys = google_keys.clone();
    

anddiff

  •                    .serve_connection(io, service_fn(|req: Request<hyper::body::Incoming>| async move  {
    
  •                        service(req, g_keys).await
    
  •                    .serve_connection(io, service_fn(|req: Request<hyper::body::Incoming>| async {
    
  •                        service(req, &g_keys).await
    

```now g_keys isn't tied to the lifetime of anything else, which means that when it's moved into the async block at line 148, the async block's lifetime isn't tied to anything else either. the async block at 161 doesn't need move; it's perfectly happy to borrow g_keys, because serve_connection doesn't require S: 'static.

#

.
what you probably want instead is```diff

  •    let google_keys: HashMap<String, (String, String)> = jwt::get_google_auth_keys();
    
  •    let google_keys: Arc<HashMap<String, (String, String)>> = Arc::new(jwt::get_google_auth_keys());
    

anddiff

  •        let g_keys = &google_keys;
    
  •        let g_keys = google_keys.clone();
    

anddiff

  •                    .serve_connection(io, service_fn(|req: Request<hyper::body::Incoming>| async move  {
    
  •                        service(req, g_keys).await
    
  •                    .serve_connection(io, service_fn(|req: Request<hyper::body::Incoming>| async {
    
  •                        service(req, &*g_keys).await
    

```now google_keys.clone() isn't cloning the entire HashMap and every String inside, it's just incrementing the Arc's reference count.