This is an automated email from the ASF dual-hosted git repository.
pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-grpc.git
The following commit(s) were added to refs/heads/main by this push:
new 5d0a3446 Replace the resolver's assert with an
IllegalArgumentException (#873)
5d0a3446 is described below
commit 5d0a3446ad77639ea050a4828b1ae4abfad4a880
Author: PJ Fanning <[email protected]>
AuthorDate: Tue Sep 1 10:10:08 2026 +0100
Replace the resolver's assert with an IllegalArgumentException (#873)
Motivation:
The pekko-http transport resolver checked the host it was asked to resolve
against the configured `override-authority` with `assert`. Two problems:
`assert` throws `AssertionError`, which is an `Error` and not an
`Exception`, so
it passes straight through a handler that catches `Exception` and is not
what a
caller inspecting a failed connection would expect to match on.
`assert` is also elided entirely under `-Xdisable-assertions`. That is the
calling application's compiler flag, not this project's, so the check
silently
disappears in a build this project does not control.
The message was also just "assertion failed", naming neither the host nor
the
authority it was compared against.
Modification:
Extracted `checkAuthority`, which throws an `IllegalArgumentException`
naming
both hosts and the client it belongs to. Extracting it also gives the check
a
seam a test can reach; it previously lived inside a closure passed to
`ClientTransport.withCustomResolver`.
Result:
The check runs regardless of the caller's compiler flags, fails with an
exception rather than an error, and says what mismatched.
Tests:
- 3 cases in `PekkoHttpClientUtilsSpec`: a matching authority, no configured
authority, and a mismatch asserting the failure is an `Exception` and
names
both hosts
- Confirmed the guard bites: restoring `assert` fails the mismatch case,
since
`AssertionError` is not an `Exception`
- sbt "runtime/testOnly ...PekkoHttpClientUtilsSpec" - 12 passed
- sbt "runtime/mimaReportBinaryIssues" - passed
- sbt scalafmtAll scalafmtSbt - applied
- sbt "runtime/test" - not run locally, left to CI
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
.../pekko/grpc/internal/PekkoHttpClientUtils.scala | 23 ++++++++++++++---
.../grpc/internal/PekkoHttpClientUtilsSpec.scala | 30 +++++++++++++++++++++-
2 files changed, 49 insertions(+), 4 deletions(-)
diff --git
a/runtime/src/main/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtils.scala
b/runtime/src/main/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtils.scala
index 068c8f34..43fe647f 100644
---
a/runtime/src/main/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtils.scala
+++
b/runtime/src/main/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtils.scala
@@ -72,9 +72,7 @@ object PekkoHttpClientUtils {
@volatile var roundRobin: Int = 0
val clientConnectionSettings =
ClientConnectionSettings(sys).withTransport(ClientTransport.withCustomResolver((host,
_) => {
- settings.overrideAuthority.foreach { authority =>
- assert(host == authority)
- }
+ checkAuthority(host, settings)
settings.serviceDiscovery.lookup(settings.serviceName,
settings.resolveTimeout).map { resolved =>
if (resolved.addresses.isEmpty)
throw new IllegalStateException(
@@ -223,6 +221,25 @@ object PekkoHttpClientUtils {
}
}
+ /**
+ * INTERNAL API
+ *
+ * Checks that the host being resolved is the authority this client was
configured for.
+ *
+ * An `IllegalArgumentException` rather than `assert`: an `AssertionError`
is an `Error`, so it
+ * passes through handlers that catch `Exception`, and `assert` is elided
entirely under
+ * `-Xdisable-assertions`, which is the calling application's compiler flag
to set rather than
+ * ours. The message names both hosts, where `assert` reported only
"assertion failed".
+ */
+ @InternalApi
+ private[internal] def checkAuthority(host: String, settings:
GrpcClientSettings): Unit =
+ settings.overrideAuthority.foreach { authority =>
+ if (host != authority)
+ throw new IllegalArgumentException(
+ s"Unexpected host [$host] for gRPC client '${settings.serviceName}',
" +
+ s"expected the configured override-authority [$authority]")
+ }
+
/**
* INTERNAL API
*
diff --git
a/runtime/src/test/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtilsSpec.scala
b/runtime/src/test/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtilsSpec.scala
index 26c15539..b3dfe42c 100644
---
a/runtime/src/test/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtilsSpec.scala
+++
b/runtime/src/test/scala/org/apache/pekko/grpc/internal/PekkoHttpClientUtilsSpec.scala
@@ -18,7 +18,7 @@ import scala.concurrent.duration._
import org.apache.pekko
import pekko.actor.ActorSystem
-import pekko.grpc.GrpcResponseMetadata
+import pekko.grpc.{ GrpcClientSettings, GrpcResponseMetadata }
import pekko.grpc.scaladsl.headers.PercentEncoding
import pekko.http.scaladsl.model.HttpEntity.Strict
import pekko.http.scaladsl.model._
@@ -38,6 +38,34 @@ class PekkoHttpClientUtilsSpec extends
TestKit(ActorSystem()) with AnyWordSpecLi
implicit val patience: PatienceConfig =
PatienceConfig(5.seconds, Span(100, org.scalatest.time.Millis))
+ "checkAuthority" should {
+
+ "accept the configured override-authority" in {
+ val settings = GrpcClientSettings.connectToServiceAt("example.com",
443).withOverrideAuthority("auth.example")
+
+ noException should be thrownBy
PekkoHttpClientUtils.checkAuthority("auth.example", settings)
+ }
+
+ "accept any host when no authority is configured" in {
+ val settings = GrpcClientSettings.connectToServiceAt("example.com", 443)
+
+ noException should be thrownBy
PekkoHttpClientUtils.checkAuthority("anything.example", settings)
+ }
+
+ "reject a mismatched host with an Exception, not an Error" in {
+ // `assert` threw AssertionError, which is an Error and so passes
straight through a
+ // `catch NonFatal` or `catch Exception` handler
+ val settings = GrpcClientSettings.connectToServiceAt("example.com",
443).withOverrideAuthority("auth.example")
+
+ val thrown = the[IllegalArgumentException] thrownBy
+ PekkoHttpClientUtils.checkAuthority("other.example", settings)
+
+ thrown shouldBe a[Exception]
+ thrown.getMessage should include("other.example")
+ thrown.getMessage should include("auth.example")
+ }
+ }
+
"The conversion from HttpResponse to Source" should {
"map a strict 404 response to a failed stream" in {
val response =
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]