Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offer to run JS code at the rewrite phase #770

Closed
TPXP opened this issue Aug 2, 2024 · 2 comments
Closed

Offer to run JS code at the rewrite phase #770

TPXP opened this issue Aug 2, 2024 · 2 comments
Labels

Comments

@TPXP
Copy link
Contributor

TPXP commented Aug 2, 2024

Is your feature request related to a problem? Please describe

I'm trying to implement a reverse proxy with NJS, however each backend I can send the request to needs a different header value. So far, I can define a backend and get nginx to forward requests to it with proxy_pass like this (I need to use a proxy directive since the backend server can return large files):

js_set $upstream myCode.getUpstream;
js_set $freshHeader myCode.getFreshHeader;

proxy_pass $upstream;
proxy_set_header X-Custom-Fresh-Header $freshHeader;

I'd like to handle upstream server errors and get Javascript code to run when that happens, to determine a new upstream to try (and compute the new header). To that extent, I'm performing an internal redirect on error. (Cannot use upstream since headers need to change, also I need to have control on the order in which servers are tried).

location / {
  js_set $upstream myCode.getUpstream;

  proxy_pass $upstream;

  # If something goes wrong, start over
  proxy_intercept_errors on;
  recursive_error_pages on;
  error_page 502 503 504 = /;
}

I hoped to be able to use a variable like upstream_addr to determine the amount of servers tried and adapt the value returned by js_set. However, since js_set and js_var return a cached variable, the get handler will never be called again and nginx will reuse its value for every round of processing. So $upstream does not change for every attempt.

Describe the solution you'd like

I'd like to be able to explicitely state I want some JS code to run at a certain phase (here, I think the rewrite would be great) to be able to run code in a more predictable way.

Describe alternatives you've considered

OpenResty/nginx-lua seems to handle this with the rewrite_by_lua directive. We can possibly take some inspiration.

Additional context

Nothing special, thanks for bringing JS to nginx. It makes it quite fun to script nginx behaviour!

@TPXP TPXP added the feature label Aug 2, 2024
@TPXP
Copy link
Contributor Author

TPXP commented Aug 2, 2024

After browsing deeper through the request, I found out that the recommended way to achieve this is to rely on an auth_request with js_content. This doesn't work well in my case because the subrequest used by auth_request hides a few variables from the context (for example $upstream_code, location capture groups, or even the original request URI!).

Also, being able to call JS code in the middle of an if block would be super cool!

@TPXP
Copy link
Contributor Author

TPXP commented Aug 25, 2024

With #771 I'm able to workaround this by calling my variable in a set $target $js_variable; directive (evaluated at rewrite phase) so this is less of a problem now. Also, I see there are some similar issues in #475 and #474 so I'll close this issue.

In case someone has a similar problem, here's how I achieve it on my side

js_set $upstream myCode.getUpstream nocache;
# myCode.getUpstream can update otherVar through `r.variables`
js_var $otherVar;

location / {
  set $cached_upstream $upstream; # avoid duplicate calls since the variable is not cached
  if ($upstream = "") {
    return 503 "Not available";
  }
  proxy_set_header X-custom $otherVar;
  proxy_pass $upstream;
}

@TPXP TPXP closed this as completed Aug 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant