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

Support for proxying rocket in a subdirectory #828

Open
jonhoo opened this issue Nov 16, 2018 · 6 comments
Open

Support for proxying rocket in a subdirectory #828

jonhoo opened this issue Nov 16, 2018 · 6 comments
Labels
enhancement A minor feature request request Request for new functionality

Comments

@jonhoo
Copy link

jonhoo commented Nov 16, 2018

I recently built a small web app using rocket, and want to host it in a subdirectory of my regular website. My website runs nginx, so I basically want this rule:

location /live-coding/ {
  proxy_pass http://localhost:8000/;
}

Unfortunately, this doesn't work very well. Absolute URLs in my templates now go to the root of my main website, as opposed to being relative to Rocket. I can mostly get around this by using <base href>, but that falls down when it comes to redirects, as they need to be absolute URLs and do not take the <base href> into account.

Ideally, I'd like to be able to inform Rocket of where it is running (a ROOT_URL field if you will), and have that automatically be integrated with redirects. It'd be awesome if there was also a link helper I could use in templates and elsewhere to produce the right absolute URLs so I wouldn't need <base>. Alternatively, for nginx specifically, you can use <base href> + proxy_redirect, and for Apache you can use ProxyPassReverse, but this seems brittle (maybe it could be documented somewhere?).

There is precedence for this kind of feature in other frameworks. For example, Rails supports this through relative_url_root. Django supports it with FORCE_SCRIPT_NAME.t

@jebrosen
Copy link
Collaborator

Unfortunately, this doesn't work very well.

Another workaround might be to mount everything in the rocket application starting at "/live-coding/" (thus matching the client's perceived path), and proxy_pass http://localhost:8000/live-coding/.

Ideally, I'd like to be able to inform Rocket of where it is running (a ROOT_URL field if you will), and have that automatically be integrated with redirects.

I agree that support for an "alternative root" would be convenient, but it can be very tricky to get right. I think only mounting and uri generation care about this, but it is a pretty significant change that will likely be difficult to test.

It'd be awesome if there was also a link helper I could use in templates and elsewhere to produce the right absolute URLs so I wouldn't need <base>.

That's an interesting idea. You should be able to do this yourself now with Template::custom, maybe pulling from your own environment variable or config. Such a helper would have to be available as a free function to remain engine-agnostic, but I suppose we could expose it in handlebars/tera.

Alternatively, for nginx specifically, you can use <base href> + proxy_redirect, and for Apache you can use ProxyPassReverse, but this seems brittle (maybe it could be documented somewhere?).

It looks like those only rewrite various headers coming from the server, so I'm not understanding how they help in this case.

@jebrosen jebrosen added enhancement A minor feature request suggestion A suggestion to change functionality request Request for new functionality and removed suggestion A suggestion to change functionality labels Nov 17, 2018
@SergioBenitez
Copy link
Member

Another workaround might be to mount everything in the rocket application starting at "/live-coding/" (thus matching the client's perceived path), and proxy_pass http://localhost:8000/live-coding/.

You'd still have to manually pass /live-coding to every call of uri!, which is a bit unfortunate.

What I would recommend for now is to:

  1. Define the PREFIX in some const somewhere.

  2. Define a wrapper for uri! that adds the prefix you want as a mount path:

     macro_rules my_uri {
          ($($token:tt)*) => { uri!(PREFIX, $($token)*) }
     }

    You can even use concat! to add the prefix to another supplied mount path.

  3. Define a template helper (as @jebrosen suggests) that effectively does the same thing.

This would replicate what we'd do internally were we to add support for this feature.

I think this is a fairly reasonable requests, but like @jebrosen, I'm concerned about getting it right everywhere, and moreover, about the maintainability of the feature. There are some upcoming features that might make this easier to implement, so I'll leave this open until at least then.

@jonhoo
Copy link
Author

jonhoo commented Nov 19, 2018

The one thing I'd add to this is that it should ideally support the prefix being configurable (in Rocket.toml perhaps). For the site I linked above, it is totally reasonable for someone else to also spin up that site with different things to vote on somewhere on their own domain, and it'd be unfortunate if they were also forced into /live-coding and had to change a const in the Rust code + recompile to get things to work.

@SergioBenitez
Copy link
Member

SergioBenitez commented Nov 22, 2018

@jonhoo To accomplish that, instead of defining a const, use something like a global state::Storage coupled with an attach fairing that reads the configuration parameter at launch and sets the Storage variable appropriately. In the macro you define and in the template helper, you'd then do something like STORAGE.try_get().map(|s| &s).unwrap_or("/") to retrieve the prefix. I don't believe that uri! currently allows non-static mount paths, but that can easily be changed.

One big issue with supporting this in Rocket proper is that it'll be the only piece of global state that Rocket "maintains". Because uri! can be called in non-request contexts, and I can't see that changing, there doesn't appear to be another option. A consequence of this is that, for instance, you wouldn't be able to test multiple configurations with different root prefixes in different threads.

Because of this, I'd have a really tough time justifying the feature in core. I'd be more inclined to offer this in contrib as a module that 1) exposes a fairing that reads the appropriate configuration parameter and stores it somewhere, 2) exposes a uri macro itself that forwards to Rocket's uri!, adding the prefix along the way, and 3) exposes alternative instantiations of template fairings with some helper added for every engine we support. A full use of such a library would be:

[global]
uri_root = "/live-coding"
#[macro_use] extern crate rocket;
#[macro_use] extern crate rocket_contrib;

// presumably `Template` would need a `rooted_templates` feature or something.
use rocket_contrib::rooted::{Rooted, Template};

#[get("/foo")]
fn foo() { ... }

fn main() {
    rocket::ignite()
        .attach(Rooted::fairing())
        .attach(Template::fairing() /* or Template::custom(..) */)
}

// uri!(foo) returns "/live-coding/foo".
// uri_root() in templates returns "/live-coding"
// uri_rooted(path) in templates returns "/live-coding/$path"

@jebrosen
Copy link
Collaborator

jebrosen commented Dec 9, 2018

Define a wrapper for uri! that adds the prefix you want as a mount path:

   macro_rules my_uri {
        ($($token:tt)*) => { uri!(PREFIX, $($token)*) }
    }

You can even use concat! to add the prefix to another supplied mount path.

Note that this doesn't actually work, since uri! requires a string literal where PREFIX is being passed. format!("{}{}", PREFIX, uri!($($token)*)) would, but PREFIX needs to be checked for correctness manually somewhere.

@GREsau
Copy link

GREsau commented Mar 7, 2021

My preference would be to allow setting the prefix/base path on Request or Origin, rather than requiring it to be set in some global config. This is similar to ASP.NET Core's HttpRequest.PathBase property.

This prefix would then be included in the request URI (and typically in any generated URIs in the response), but crucially would be ignored for routing purposes.

This would support use-cases where the application doesn't know/care about the prefix ahead of time, so you can e.g. add a fairing to read the prefix from an x-forwarded-prefix request header set by a reverse proxy.

This would also be very useful for me in rocket-lamb which adapts rocket webservers to run in AWS lambda, typically under a base path configured in AWS API Gateway. Currently it hackily achieves this by cloning all routes and re-mounting them under the base path when the first request is received, which is only possible because rocket isn't handling the actual HTTP request in this case, so the Rocket can be lazily initialised.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A minor feature request request Request for new functionality
Projects
None yet
Development

No branches or pull requests

4 participants