http-rs / tide

Fast and friendly HTTP server framework for async Rust
https://docs.rs/tide
Apache License 2.0
5.04k stars 322 forks source link

Add support for routing on subdomains #690

Open AlecDivito opened 4 years ago

AlecDivito commented 4 years ago

Feature Request

Not all of the url routing is complete. I'm just wondering if support for routing on subdomains could be added into the framework.

Detailed Description

Add new methods to the router.rs and server.rs to support users to route on subdomains as well. An example of the resulting api I am thinking of would look like so:

let mut app = tide::new();
app.on_subdomain(":sub1.:sub2.:sub3").at("/").with(validate_subdomain).all(handle_user);

Context

I have an application that can have multiple tenets that can upload content to the server and would like to configure my webserver to be able to route on subdomains. This will benefit other users of tide who want to add the same functionality to their own application.

Possible Implementation

I haven't dove into the code base that much but i would assume that I would need to add functionality to server.rs and router.rs. I know this maybe a big addition so I wouldn't mind taking a chance and implementing it and putting in a PR.

Just wondering if this project is also interested in adding support for it and want to start a discussion.

yoshuawuyts commented 4 years ago

We had a bit of a chat about this earlier today, I think this is a fairly promising direction. Earlier art for subdomain routing includes:

The Rails subrouting API seems quite interesting; if we were to translate that to Tide it could look like:

let mut app = tide::new();
app.subdomain("blog").at("/").get(|_| async { Ok("welcome to my blog") });
app.listen("localhost:8080").await?;

I'm leaning towards app.subdomain rather than abbreviating it as app.sub to prevent confusion with app.nest. Also we should only add subdomain to Server but not Route. Because subdomains feel like they should be declared at the root of the app, not nested within routes. As such we should perhaps also guard that when nesting apps we disallow subdomains on the app we're nesting.


That's my opinion, @jbr had some different ideas that I'm hoping he'll share!

AlecDivito commented 3 years ago

Happy to hear the advice. I really enjoyed looking into the Rails subdomain routing and have an approach I think would work. I liked your proposal and I think the API design should look like the following:

let mut app = tide::new();
app.subdomain("example").at("/").get(|_| async { Ok("example subdomain") });
app.subdomain("portal").with(authentication).at("/").get(|_| async { Ok("Secure portal") });
app.subdomain(":user.blog").at("/").get(|req| async { Ok(req.param::<String>("user").unwrap()) });
app.listen("example.com").await?;

You may have noticed but I am making one prediction which is that the user treats their apex domain as their base url. I'm not sure 100% sure of a good way to allow the user to state the base url.

In terms of the design of additional code needed, I believe it would be best to do the same thing that route-recognizer is doing and by having a Router container around the Route struct. I propose wrapping a Subdomain struct around a Namespace container:

struct Server<State> {
    ...
    namespace: Namespace<State>;
    ...
}

struct Namespace<State> {
    router: SubdomainRouter<Subdomain<State>>
}

struct Subdomain<State> {
    subdomain: String,
    router: Router<State>,
    middleware: Vec<Arc<dyn Middleware<State>>>,
}

The SubdomainRouter<T> would just be like route-recognizer but dumber.

I wouldn't mind hearing your thoughts about this this design. I am working on an implementation and hopping for it to be finished soon.

maxcountryman commented 3 years ago

Is this still being considered? Would be nice to e.g. partition app routes from api routes.

timtom-dev commented 2 years ago

This is a feature that I'd really like as well. Any update on this? I'm willing to contribute if needed, I'll just need a little guidance.

Fishrock123 commented 2 years ago

I no longer recall the conversation well enough on what @jbr's ideas for this were, only that they were interesting.

jbr commented 2 years ago

I think I stalled out on that idea for tide because it would work better if there were a way to modularly experiment with alternative routers without having to opt everyone into a new experimental router. It might be worth exploring it in trillium first and then proposing it over here once the dust has settled, since adding a new router to trillium is just adding a new opt-in crate.

The core idea was to allow "route specifications" to look like a full url, with matchers in any position, so "https://$domain/users/$user_id" or ""$subdomain.example.com/users/$user_id" or "$scheme://example.com/users/$user_id" would all be valid route specs. Note that I typed this out with $ instead of : to represent named route params, in order to allow for $scheme://. It would still be valid to describe a route as "/users/$user_id", which would be functionally equivalent to "$scheme://$domain/users/$user_id" but without capturing scheme and domain (probably?).