Created
May 12, 2019 19:37
-
-
Save vcapra1/2dc30575f798d9702433a6c57882862b to your computer and use it in GitHub Desktop.
Rocket.rs route by subdomain
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use rocket::{request::FromRequest, Outcome, Request}; | |
pub struct WWWHost; | |
pub struct AAAHost; | |
impl<'a, 'r> FromRequest<'a, 'r> for WWWHost { | |
type Error = (); | |
fn from_request(request: &'a Request<'r>) -> rocket::request::Outcome<Self, Self::Error> { | |
if let Some(hostname) = request.headers().get_one("host") { | |
if hostname == "example.com" { | |
Outcome::Success(WWWHost) | |
} else if hostname == "www.example.com" { | |
Outcome::Success(WWWHost) | |
} else { | |
Outcome::Forward(()) | |
} | |
} else { | |
// There was no host header (this shouldn't happen) | |
Outcome::Forward(()) | |
} | |
} | |
} | |
impl<'a, 'r> FromRequest<'a, 'r> for AAAHost { | |
type Error = (); | |
fn from_request(request: &'a Request<'r>) -> rocket::request::Outcome<Self, Self::Error> { | |
if let Some(hostname) = request.headers().get_one("host") { | |
if hostname == "aaa.example.com" { | |
Outcome::Success(WWWHost) | |
} else { | |
Outcome::Forward(()) | |
} | |
} else { | |
// There was no host header (this shouldn't happen) | |
Outcome::Forward(()) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[macro_use] | |
extern crate rocket; | |
mod hosts; | |
#[get("/")] | |
fn root(host: WWWHost) -> String { | |
String::from("Welcome to www.example.com!"); | |
} | |
#[get("/")] | |
fn sub(host: AAAHost) -> String { | |
String::from("Welcome to aaa.example.com!"); | |
} | |
fn main() { | |
rocket::ignite().mount("/", routes![root, sub]).launch(); | |
} |
Yeah that works great too. My code had rather long functions, however, and I wanted to split the code that handled each subdomain into its own file.
Stumbled across this great gist and found that the trait structure has changed somewhat in 0.5-rc
.
Modified version of hosts.rs
, based on the docs example:
use rocket::http::Status;
use rocket::request::{FromRequest, Outcome, Request};
pub struct WWWHost;
pub struct AAAHost;
#[rocket::async_trait]
impl<'r> FromRequest<'r> for WWWHost {
type Error = ();
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
match req.headers().get_one("host") {
None => Outcome::Failure((Status::BadRequest, ())),
Some(host) if matches!(host, "example.com" | "www.example.com") => Outcome::Success(WWWHost),
Some(_) => Outcome::Forward(()),
}
}
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AAAHost {
type Error = ();
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
match req.headers().get_one("host") {
None => Outcome::Failure((Status::BadRequest, ())),
Some(host) if (host == "aaa.example.com") => Outcome::Success(AAAHost),
Some(_) => Outcome::Forward(()),
}
}
}
Here's my version which uses the duplicate
crate, the one for creating the structs is needless and adds extra lines, but it allows for only one allow macro. This includes a default struct which can be removed.
#[macro_use]
extern crate rocket;
use duplicate::duplicate_item;
use rocket::{
http::Status,
request::{FromRequest, Outcome},
Request,
};
#[allow(clippy::upper_case_acronyms)]
#[duplicate_item(
Name;
[ API ];
[ WEB ];
[ DEF ];
)]
struct Name;
#[duplicate_item(
Host Name;
[ API ] [ "api.shaybox.com" | "127.0.0.1:8000" ];
[ WEB ] [ "www.shaybox.com" | "shaybox.com" ];
[ DEF ] [ _ ];
)]
#[rocket::async_trait]
impl<'r> FromRequest<'r> for Host {
type Error = ();
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
match req.headers().get_one("host") {
None => Outcome::Error((Status::BadRequest, ())),
Some(Name) => Outcome::Success(Host),
#[allow(unreachable_patterns)] // DEF
Some(_) => Outcome::Forward(Status::Accepted),
// Some(host) => { // Debug
// println!("{host}");
// Outcome::Forward(Status::Accepted)
// }
}
}
}
#[get("/", rank = 2)]
fn def(_host: DEF) -> &'static str {
"Hello, DEF!"
}
#[get("/", rank = 1)]
fn api(_host: API) -> &'static str {
"Hello, API!"
}
#[get("/", rank = 0)]
fn web(_host: WEB) -> &'static str {
"Hello, WEB!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![api, web, def])
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@vcapra1 Thanks for sharing! I copied the
impl
from: rwf2/Rocket#823I think it's a little nicer:
https://github.com/olehermanse/dagensquiz/blob/master/src/main.rs#L66
https://github.com/olehermanse/dagensquiz/blob/master/src/main.rs#L315