Skip to content

Commit

Permalink
Fix: panic on missing trailing / in rocket environment (#645) (#757)
Browse files Browse the repository at this point in the history
The ServeSwagger handler extracts the base path from the route by taking
the beginning of the route until it finds the opening '<' and uses the
extracted &str as base path to resolve incoming requests.

Thus, it removes this base path from the request URI and proceeds
serving with the corresponding response.

Rocket also routes '/url' (note the missing trailing slash) to the
handler which then panics as the base URL is shorter than request URI.

This patch adds a test for the expected base path prefix and response
with a redirection if missing resulting in a successful page load.

Found, status code 302 or 'Moved Temporarily' is not stored in the
browser. Returning this status code aims to prevent conflicts with
similar routes.
  • Loading branch information
reflix authored Sep 22, 2023
1 parent 6dc73eb commit 4e10648
Showing 1 changed file with 25 additions and 15 deletions.
40 changes: 25 additions & 15 deletions utoipa-swagger-ui/src/rocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ use std::{borrow::Cow, io::Cursor, sync::Arc};

use rocket::{
http::{Header, Status},
response::{
status::{self, NotFound},
Responder as RocketResponder,
},
response::{status::NotFound, Responder as RocketResponder},
route::{Handler, Outcome},
serde::json::Json,
Data as RocketData, Request, Response, Route,
Expand Down Expand Up @@ -79,31 +76,44 @@ struct ServeSwagger(Cow<'static, str>, Arc<Config<'static>>);
#[rocket::async_trait]
impl Handler for ServeSwagger {
async fn handle<'r>(&self, request: &'r Request<'_>, _: RocketData<'r>) -> Outcome<'r> {
let mut path = self.0.as_ref();
let mut base_path = self.0.as_ref();
if let Some(index) = self.0.find('<') {
path = &path[..index];
base_path = &base_path[..index];
}

match super::serve(&request.uri().path().as_str()[path.len()..], self.1.clone()) {
let request_path = request.uri().path().as_str();
let request_path = match request_path.strip_prefix(base_path) {
Some(stripped) => stripped,
None => return Outcome::from(request, RedirectResponder(base_path.into())),
};
match super::serve(request_path, self.1.clone()) {
Ok(swagger_file) => swagger_file
.map(|file| Outcome::from(request, file))
.unwrap_or_else(|| Outcome::from(request, NotFound("Swagger UI file not found"))),
Err(error) => Outcome::from(
request,
status::Custom(Status::InternalServerError, error.to_string()),
rocket::response::status::Custom(Status::InternalServerError, error.to_string()),
),
}
}
}

impl<'r, 'o: 'r> RocketResponder<'r, 'o> for SwaggerFile<'o> {
fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'o> {
rocket::response::Result::Ok(
Response::build()
.header(Header::new("Content-Type", self.content_type))
.sized_body(self.bytes.len(), Cursor::new(self.bytes.to_vec()))
.status(Status::Ok)
.finalize(),
)
Ok(Response::build()
.header(Header::new("Content-Type", self.content_type))
.sized_body(self.bytes.len(), Cursor::new(self.bytes.to_vec()))
.status(Status::Ok)
.finalize())
}
}

struct RedirectResponder(String);
impl<'r, 'a: 'r> RocketResponder<'r, 'a> for RedirectResponder {
fn respond_to(self, _request: &'r Request<'_>) -> rocket::response::Result<'a> {
Response::build()
.status(Status::Found)
.raw_header("Location", self.0)
.ok()
}
}

0 comments on commit 4e10648

Please sign in to comment.