From 63a5dbccc8b346cc8cebe4b9738efc7ba994ae9f Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Sun, 28 Jun 2026 04:55:06 +0300 Subject: [PATCH] chore: simplify auth challenge parser and Digest qop helper Behavior-preserving cleanups in the http/auth package, all confined to private members (no public-API change): - recoverToNextChallenge now reuses readQuotedString() instead of carrying a second, near-identical quoted-string skip that had to stay byte-for-byte in step with it. On an unterminated string the reader consumes through to EOF, exactly where recovery wants to land. - parseAuthParamOrToken68 hoists the repeated "rewind to saved, read a token68, yield token68" sequence into one local function, so the three disambiguation branches read as the decision they make. - Back the token / token68 character sets with String constants rather than Set. The `c in ...` membership test resolves to CharSequence.contains, dropping per-character boxing, the HashSet lookup on the hot parse path, and the one-time set allocation. - Collapse qopSupportsAuth's accumulate-and-return loop to a single short-circuiting any { } expression, keeping the legacy null guard. The existing AuthChallengeParserTest / DigestChallengeHandlerTest suites cover these paths. --- .../sdk/core/http/auth/AuthChallengeParser.kt | 36 ++++++++----------- .../core/http/auth/DigestChallengeHandler.kt | 5 +-- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/auth/AuthChallengeParser.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/auth/AuthChallengeParser.kt index 789beb54..4dd2e6a9 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/auth/AuthChallengeParser.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/auth/AuthChallengeParser.kt @@ -142,13 +142,18 @@ public object AuthChallengeParser { @Suppress("ReturnCount") private fun parseAuthParamOrToken68(cursor: Cursor): Pair? { val saved = cursor.position + + fun rewindAsToken68(): Pair? { + cursor.position = saved + val token68 = cursor.readToken68() ?: return null + return "token68" to token68 + } + val name = cursor.readToken() ?: return null cursor.skipOws() if (!cursor.hasMore() || cursor.peek() != '=') { // No `=` after the token — it's a token68. - cursor.position = saved - val token68 = cursor.readToken68() ?: return null - return "token68" to token68 + return rewindAsToken68() } cursor.advance() // consume the first `=` @@ -159,9 +164,7 @@ public object AuthChallengeParser { // entire `cmVhbA==` is recovered. Without this branch a doubly-padded // base64 token would be silently dropped. if (cursor.hasMore() && cursor.peek() == '=') { - cursor.position = saved - val token68 = cursor.readToken68() ?: return null - return "token68" to token68 + return rewindAsToken68() } cursor.skipOws() @@ -169,9 +172,7 @@ public object AuthChallengeParser { if (!cursor.hasMore() || cursor.peek() == ',') { // looked like `key=` with nothing after — try to treat the whole // thing as token68 (rewind and read it as such). - cursor.position = saved - val token68 = cursor.readToken68() ?: return null - return "token68" to token68 + return rewindAsToken68() } val value = cursor.readTokenOrQuotedString() ?: return null return name.lowercase(Locale.US) to value @@ -274,25 +275,18 @@ public object AuthChallengeParser { while (position < len) { when (src[position]) { ',' -> return - '"' -> { - // skip the quoted string — but if it's unterminated, just - // jump to EOF. - position++ - while (position < len && src[position] != '"') { - if (src[position] == '\\' && position + 1 < len) position++ - position++ - } - if (position < len) position++ // closing quote - } + // Reuse the escape-aware reader; on an unterminated string it + // consumes through to EOF, exactly where recovery wants to land. + '"' -> readQuotedString() else -> position++ } } } } - private val TOKEN_PUNCTUATION: Set = "!#$%&'*+-.^_`|~".toSet() + private const val TOKEN_PUNCTUATION = "!#$%&'*+-.^_`|~" - private val TOKEN68_PUNCTUATION: Set = "-._~+/".toSet() + private const val TOKEN68_PUNCTUATION = "-._~+/" /** RFC 7230 token char: ALPHA / DIGIT / one of the punctuation set. */ private fun isTokenChar(c: Char): Boolean = diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/auth/DigestChallengeHandler.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/auth/DigestChallengeHandler.kt index 6285015e..0ebb93e1 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/auth/DigestChallengeHandler.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/auth/DigestChallengeHandler.kt @@ -374,10 +374,7 @@ public class DigestChallengeHandler /** True if [qop] is absent (legacy) or contains the `auth` token (case-insensitive). */ private fun qopSupportsAuth(qop: String?): Boolean { if (qop == null) return true - for (token in qop.split(',')) { - if (token.trim().equals("auth", ignoreCase = true)) return true - } - return false + return qop.split(',').any { it.trim().equals("auth", ignoreCase = true) } } /** Lower-case hex of a byte array — minimal allocation, no intermediate strings. */