From c78bcd45e8100a7e30d5e0b3798527ed86d0624a Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Thu, 14 Mar 2024 12:44:00 -0700 Subject: [PATCH] Add fallback route --- Cargo.lock | 7 ++++ Cargo.toml | 1 + src/subcommand/server.rs | 78 +++++++++++++++++++++++++++++++++++----- 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 891bdfcf54..f14f43dfaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2266,6 +2266,7 @@ dependencies = [ "tokio-util 0.7.10", "tower-http", "unindent", + "urlencoding", ] [[package]] @@ -3653,6 +3654,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" diff --git a/Cargo.toml b/Cargo.toml index a3cb953593..62ebd27696 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ tokio = { version = "1.17.0", features = ["rt-multi-thread"] } tokio-stream = "0.1.9" tokio-util = {version = "0.7.3", features = ["compat"] } tower-http = { version = "0.4.0", features = ["auth", "compression-br", "compression-gzip", "cors", "set-header"] } +urlencoding = "2.1.3" [dev-dependencies] criterion = "0.5.1" diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 3b4155ec7e..ae3814453a 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -254,6 +254,7 @@ impl Server { .route("/static/*path", get(Self::static_asset)) .route("/status", get(Self::status)) .route("/tx/:txid", get(Self::transaction)) + .fallback(Self::fallback) .layer(Extension(index)) .layer(Extension(server_config.clone())) .layer(Extension(settings.clone())) @@ -483,6 +484,29 @@ impl Server { }) } + async fn fallback(Extension(index): Extension>, uri: Uri) -> ServerResult { + let path = urlencoding::decode(uri.path().trim_matches('/')) + .map_err(|err| ServerError::BadRequest(err.to_string()))?; + + let prefix = if re::INSCRIPTION_ID.is_match(&path) || re::INSCRIPTION_NUMBER.is_match(&path) { + "inscription" + } else if re::RUNE_ID.is_match(&path) || re::SPACED_RUNE.is_match(&path) { + "rune" + } else if re::OUTPOINT.is_match(&path) { + "output" + } else if re::HASH.is_match(&path) { + if index.block_header(path.parse().unwrap())?.is_some() { + "block" + } else { + "tx" + } + } else { + return Ok(StatusCode::NOT_FOUND.into_response()); + }; + + Ok(Redirect::to(&format!("/{prefix}/{path}")).into_response()) + } + async fn sat( Extension(server_config): Extension>, Extension(index): Extension>, @@ -950,7 +974,7 @@ impl Server { } } else if re::OUTPOINT.is_match(query) { Ok(Redirect::to(&format!("/output/{query}"))) - } else if re::INSCRIPTION_ID.is_match(query) { + } else if re::INSCRIPTION_ID.is_match(query) || re::INSCRIPTION_NUMBER.is_match(query) { Ok(Redirect::to(&format!("/inscription/{query}"))) } else if re::SPACED_RUNE.is_match(query) { Ok(Redirect::to(&format!("/rune/{query}"))) @@ -2303,11 +2327,6 @@ mod tests { TestServer::new().assert_redirect("/faq", "https://docs.ordinals.com/faq/"); } - #[test] - fn search_by_query_returns_sat() { - TestServer::new().assert_redirect("/search?query=0", "/sat/0"); - } - #[test] fn search_by_query_returns_rune() { TestServer::new().assert_redirect("/search?query=ABCD", "/rune/ABCD"); @@ -2326,14 +2345,19 @@ mod tests { ); } + #[test] + fn search_by_query_returns_inscription_by_number() { + TestServer::new().assert_redirect("/search?query=0", "/inscription/0"); + } + #[test] fn search_is_whitespace_insensitive() { - TestServer::new().assert_redirect("/search/ 0 ", "/sat/0"); + TestServer::new().assert_redirect("/search/ abc ", "/sat/abc"); } #[test] fn search_by_path_returns_sat() { - TestServer::new().assert_redirect("/search/0", "/sat/0"); + TestServer::new().assert_redirect("/search/abc", "/sat/abc"); } #[test] @@ -2423,6 +2447,44 @@ mod tests { ); } + #[test] + fn fallback() { + let server = TestServer::new(); + + server.assert_redirect("/0", "/inscription/0"); + server.assert_redirect("/0/", "/inscription/0"); + server.assert_redirect("/0//", "/inscription/0"); + server.assert_redirect( + "/521f8eccffa4c41a3a7728dd012ea5a4a02feed81f41159231251ecf1e5c79dai0", + "/inscription/521f8eccffa4c41a3a7728dd012ea5a4a02feed81f41159231251ecf1e5c79dai0", + ); + server.assert_redirect("/-1", "/inscription/-1"); + server.assert_redirect("/FOO", "/rune/FOO"); + server.assert_redirect("/FO.O", "/rune/FO.O"); + server.assert_redirect("/FO•O", "/rune/FO•O"); + server.assert_redirect("/0:0", "/rune/0:0"); + server.assert_redirect( + "/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0", + "/output/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0", + ); + server.assert_redirect( + "/search/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", + "/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", + ); + server.assert_redirect( + "/search/0000000000000000000000000000000000000000000000000000000000000000", + "/tx/0000000000000000000000000000000000000000000000000000000000000000", + ); + + server.assert_response_regex("/hello", StatusCode::NOT_FOUND, ""); + + server.assert_response_regex( + "/%C3%28", + StatusCode::BAD_REQUEST, + "invalid utf-8 sequence of 1 bytes from index 0", + ); + } + #[test] fn runes_can_be_queried_by_rune_id() { let server = TestServer::builder()