Merge pull request #2866 from greenart7c3/claude/fix-blossom-cache-url-M9Pzk

Fix Blossom bridge to only rewrite last path segment as SHA256
This commit is contained in:
Vitor Pamplona
2026-05-13 08:13:35 -04:00
committed by GitHub
4 changed files with 90 additions and 9 deletions
@@ -72,13 +72,18 @@ class LocalBlossomCacheRedirectInterceptor(
}
private fun findSha256AndExtensionInPath(url: HttpUrl): Triple<Int, String, String>? {
url.pathSegments.forEachIndexed { index, segment ->
val match = SHA256_SEGMENT_REGEX.find(segment) ?: return@forEachIndexed
val sha = match.value.lowercase()
val ext = guessExtensionFrom(segment, sha) ?: "bin"
return Triple(index, sha, ext)
}
return null
// Per Blossom (BUD-01) the blob is always the last path segment. If the
// last segment isn't a sha256, this isn't a Blossom URL and the bridge
// must leave it alone — even if an earlier path segment happens to be
// a 64-char hex (e.g. a per-user cache prefix). The prefix segments
// are preserved verbatim via `buildServerBase`.
val lastIndex = url.pathSegments.lastIndex
if (lastIndex < 0) return null
val segment = url.pathSegments[lastIndex]
val match = SHA256_SEGMENT_REGEX.find(segment) ?: return null
val sha = match.value.lowercase()
val ext = guessExtensionFrom(segment, sha) ?: "bin"
return Triple(lastIndex, sha, ext)
}
/**
@@ -132,6 +132,37 @@ class LocalBlossomCacheRedirectInterceptorTest {
response.close()
}
@Test
fun bridgeOnRewritesShaInLastPathSegmentWithHexPrefix() {
// share.yabu.me layout: <cache-prefix-sha>/<blob-sha>.<ext>
val interceptor = LocalBlossomCacheRedirectInterceptor { true }
val captured = mutableListOf<String>()
val response =
interceptor.intercept(
fakeChain(
"https://share.yabu.me/84b0c46ab699ac35eb2ca286470b85e081db2087cdef63932236c397417782f5/28fa4d999af6ae3e4e11bfc2727130ef1b3a13cc0f981e5a93c3996cb2f524e5.webp",
captured,
),
)
assertEquals(
"http://127.0.0.1:24242/28fa4d999af6ae3e4e11bfc2727130ef1b3a13cc0f981e5a93c3996cb2f524e5.webp?xs=https%3A%2F%2Fshare.yabu.me%2F84b0c46ab699ac35eb2ca286470b85e081db2087cdef63932236c397417782f5",
captured.single(),
)
response.close()
}
@Test
fun bridgeOnSkipsWhenLastSegmentIsNotSha() {
// Per BUD-01 the last segment is the blob; if it isn't a sha256, the
// URL isn't a Blossom blob even if an earlier segment is hex.
val interceptor = LocalBlossomCacheRedirectInterceptor { true }
val captured = mutableListOf<String>()
val url = "https://example.com/$sha/avatar.jpg"
val response = interceptor.intercept(fakeChain(url, captured))
assertEquals(url, captured.single())
response.close()
}
private fun fakeChain(
url: String,
captured: MutableList<String>,