leptos: Leptos Axum Handle Server Fn unwrapping Context can panic under load

Describe the bug In leptox_axum handle_server_fns_inner there are several call to unwrap() that can evidently be None (though I’m not entirely sure how).

 // If ResponseOptions are set, add the headers and status to the request
                        let res_options = use_context::<ResponseOptions>();

                        // if this is Accept: application/json then send a serialized JSON response
                        let accept_header = headers
                            .get("Accept")
                            .and_then(|value| value.to_str().ok());
                        let mut res = Response::builder();

                        // Add headers from ResponseParts if they exist. These should be added as long
                        // as the server function returns an OK response
                        let res_options_outer = res_options.unwrap().0;  -----> unwrap a none
                        let res_options_inner = res_options_outer.read();
called `Result::unwrap()` on an `Err` value: Canceled
thread '<unnamed>' panicked at .cargo/registry/src/index.crates.io-6f17d22bba15001f/leptos_axum-0.5.4/src/lib.rs:323:61: 

Leptos Dependencies

leptos = { version = "0.5", features = ["nightly"] }
leptos_axum = { version = "0.5", optional = true }
leptos_meta = { version = "0.5", features = ["nightly"] }
leptos_router = { version = "0.5", features = ["nightly"] }

To Reproduce Steps to reproduce the behavior: You’ll have to bear with me here because I can only reproduce this when stress testing.

  1. run your benchmarking tool of choice ./wrk -t 8 -c 2000 -d 30s --latency http://localhost:3000
  2. go anywhere on your app that makes a call to a server fn (and has IO like a database call or sleep) and uses extractors).
  3. start spamming refresh until error reaching server to call server function: TypeError: NetworkError when attempting to fetch resource..

Expected behavior Option::unwrap() call is removed and replaced with a guard. Though I’m not sure what is causing this problem.

Screenshots If applicable, add screenshots to help explain your problem.

Additional context If you look at the handler you can clearly see that provide_context is called early on so there’s no reason to suspect that getting the context for ResponseOptions wouldn’t return Some, but it does). My guess bet is that there’s reads and writes going on at the same time.

About this issue

  • Original URL
  • State: open
  • Created 7 months ago
  • Reactions: 1
  • Comments: 22 (3 by maintainers)

Commits related to this issue

Most upvoted comments

@mjarvis9541 @glademiller I’m concerned that the solution (which I believe is now merged in #2468) mostly hides the problem rather than fixing it: i.e., if someone has a server function that actually sets something in ResponseOptions, unwrap_or_default() (or printing a warning as #2468 does) doesn’t panic but returns a response and fails to set that header/status code/etc. that was supposed to be set in the response. I guess it’s better than the panic.

I will plan to revisit this to make sure it is solved in 0.7, where the reactive system works significantly differently.

@gbj Thank you if I find a way to reproduce the issue consistently I will send it over.

For now for anyone else having this problem if you are not using the ResponseOptions to change a server functions response then the workaround I am using at the moment is to patch leptos-axum by changing this line

https://github.com/leptos-rs/leptos/blob/main/integrations/axum/src/lib.rs#L306 to let res_options = use_context:😦).unwrap_or_default().0;

The root of the problem is that in some scenario I haven’t been able to identify the Runtime is disposed so a default runtime is returned which does not have the ResponseOptions in the Context as leptos-axum expects.

I can confirm this completely resolves the issue. This has been happening on multiple production apps I’ve built and released to a server, I never have been able to reproduce this locally.

@gbj this is the issue I brought up in discord regarding the 502 errors through a load balancer. I never did get to the bottom of why that was happening but the above has resolved that also.

@Panaetius I’m happy to look into this at some point if you can open a new issue with some way of reproducing the problem.

@WIGGLES-dev @pnmadelaine Now that #2158 is merged, the server fn implementation on the main branch is now quite different from the previous implementation. It is available for testing either with a dependency on the main branch or version 0.6.0-beta.

Because the underlying implementation has changed significantly, I’m going to close this issue. If you encounter the same problem with the new version, feel free to open a new issue.