-
-
Notifications
You must be signed in to change notification settings - Fork 352
feat(srv): Add HTTP 301 redirects for common URL mistakes #2528
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
Changes from all commits
c293ba3
db58203
d5a0cb5
aa2101e
0fe8868
3eab04b
e317695
be45c8a
ba0b997
22a272a
69aabd9
71062a8
edffef7
f87ccf9
d0236cc
ef8d7c4
7438f1a
3714de7
4823da5
d8efebc
17834d2
20c29ad
5f7d192
f6de020
abc9138
963703a
58513e8
0c9c79d
3d60266
7e5e34b
5f65c70
a4e5fbd
624d608
812475c
4c2392b
3f181d3
82560d2
e8d343f
0eda4fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ use actix_http::header::Quality; | |
| use actix_web::error::{ErrorBadRequest, ErrorNotAcceptable, ErrorNotFound}; | ||
| use actix_web::http::header::{ | ||
| AcceptEncoding, CONTENT_ENCODING, ETAG, Encoding as HeaderEnc, EntityTag, IfNoneMatch, | ||
| Preference, | ||
| LOCATION, Preference, | ||
| }; | ||
| use actix_web::web::{Data, Path, Query}; | ||
| use actix_web::{HttpMessage, HttpRequest, HttpResponse, Result as ActixResult, route}; | ||
|
|
@@ -14,10 +14,12 @@ use martin_tile_utils::{ | |
| encode_gzip, | ||
| }; | ||
| use serde::Deserialize; | ||
| use tracing::warn; | ||
|
|
||
| use crate::config::args::PreferredEncoding; | ||
| use crate::config::file::srv::SrvConfig; | ||
| use crate::source::TileSources; | ||
| use crate::srv::server::DebouncedWarning; | ||
| use crate::srv::server::map_internal_error; | ||
|
|
||
| const SUPPORTED_ENC: &[HeaderEnc] = &[ | ||
|
|
@@ -61,6 +63,74 @@ async fn get_tile( | |
| .await | ||
| } | ||
|
|
||
| #[derive(Deserialize, Clone)] | ||
| pub struct RedirectTileRequest { | ||
| ids: String, | ||
| z: u8, | ||
| x: u32, | ||
| y: u32, | ||
| ext: String, | ||
| } | ||
|
|
||
| /// Redirect `/{source_ids}/{z}/{x}/{y}.{extension}` to `/{source_ids}/{z}/{x}/{y}` (HTTP 301) | ||
| /// Registered before main tile route to match more specific pattern first | ||
| #[route("/{ids}/{z}/{x}/{y}.{ext}", method = "GET", method = "HEAD")] | ||
| pub async fn redirect_tile_ext(req: HttpRequest, path: Path<RedirectTileRequest>) -> HttpResponse { | ||
| static WARNING: DebouncedWarning = DebouncedWarning::new(); | ||
| let RedirectTileRequest { ids, z, x, y, ext } = path.as_ref(); | ||
|
|
||
| WARNING | ||
| .once_per_hour(|| { | ||
| warn!( | ||
| "Request to /{ids}/{z}/{x}/{y}.{ext} caused unnecessary redirect. Use /{ids}/{z}/{x}/{y} to avoid extra round-trip latency." | ||
| ); | ||
| }) | ||
| .await; | ||
|
|
||
| redirect_tile_with_query(ids, *z, *x, *y, req.query_string()) | ||
| } | ||
|
|
||
| /// Redirect `/tiles/{source_ids}/{z}/{x}/{y}` to `/{source_ids}/{z}/{x}/{y}` (HTTP 301) | ||
| #[route("/tiles/{source_ids}/{z}/{x}/{y}", method = "GET", method = "HEAD")] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well.. the other apis (fonts, sprites, ...) also exist. If a prefix exists, this is "pushed to the right" by said prefix. |
||
| pub async fn redirect_tiles(req: HttpRequest, path: Path<TileRequest>) -> HttpResponse { | ||
| static WARNING: DebouncedWarning = DebouncedWarning::new(); | ||
| let TileRequest { | ||
| source_ids, | ||
| z, | ||
| x, | ||
| y, | ||
| } = path.as_ref(); | ||
|
|
||
| WARNING | ||
| .once_per_hour(|| { | ||
| warn!( | ||
| "Request to /tiles/{source_ids}/{z}/{x}/{y} caused unnecessary redirect. Use /{source_ids}/{z}/{x}/{y} to avoid extra round-trip latency." | ||
| ); | ||
| }) | ||
| .await; | ||
|
|
||
| redirect_tile_with_query(source_ids, *z, *x, *y, req.query_string()) | ||
| } | ||
|
|
||
| /// Helper function to create a 301 redirect for tiles with query string preservation | ||
| fn redirect_tile_with_query( | ||
| source_ids: &str, | ||
| z: u8, | ||
| x: u32, | ||
| y: u32, | ||
| query_string: &str, | ||
| ) -> HttpResponse { | ||
| let location = format!("/{source_ids}/{z}/{x}/{y}"); | ||
| let location = if query_string.is_empty() { | ||
| location | ||
| } else { | ||
| format!("{location}?{query_string}") | ||
| }; | ||
| HttpResponse::MovedPermanently() | ||
| .insert_header((LOCATION, location)) | ||
| .finish() | ||
| } | ||
|
|
||
| pub struct DynTileSource<'a> { | ||
| pub sources: Vec<BoxedSource>, | ||
| pub info: TileInfo, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.