This is an automated email from the ASF dual-hosted git repository.
fanningpj pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-pekko-grpc.git
The following commit(s) were added to refs/heads/main by this push:
new d6503802 make sure trailers are present in StatusRuntimeException
(#230)
d6503802 is described below
commit d65038020103c6d48aff3ed5a5085fb584e88611
Author: João Ferreira <[email protected]>
AuthorDate: Sat Feb 24 16:44:51 2024 +0000
make sure trailers are present in StatusRuntimeException (#230)
---
.../pekko/grpc/internal/PekkoHttpClientUtils.scala | 23 +++++++++---------
.../grpc/internal/PekkoHttpClientUtilsSpec.scala | 27 +++++++++++++++++++---
2 files changed, 36 insertions(+), 14 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 01215c2b..b78bc7f5 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
@@ -24,16 +24,10 @@ import pekko.annotation.InternalApi
import pekko.event.LoggingAdapter
import pekko.grpc.GrpcProtocol.GrpcProtocolReader
import pekko.grpc.{ GrpcClientSettings, GrpcResponseMetadata,
GrpcSingleResponse, ProtobufSerializer }
+import pekko.grpc.scaladsl.StringEntry
import pekko.http.scaladsl.model.HttpEntity.{ Chunk, Chunked, LastChunk,
Strict }
import pekko.http.scaladsl.{ ClientTransport, ConnectionContext, Http }
-import pekko.http.scaladsl.model.{
- AttributeKey,
- HttpHeader,
- HttpRequest,
- HttpResponse,
- RequestResponseAssociation,
- Uri
-}
+import pekko.http.scaladsl.model._
import pekko.http.scaladsl.settings.ClientConnectionSettings
import pekko.stream.{ Materializer, OverflowStrategy }
import pekko.stream.scaladsl.{ Keep, Sink, Source }
@@ -232,7 +226,12 @@ object PekkoHttpClientUtils {
.watchTermination()((_, done) =>
done.onComplete(_ =>
trailerPromise.trySuccess(immutable.Seq.empty)))
case Strict(_, data) =>
- trailerPromise.success(immutable.Seq.empty)
+ val rawTrailers =
+
response.attribute(AttributeKeys.trailer).map(_.headers).getOrElse(immutable.Seq.empty)
+ val trailers = rawTrailers.map(h =>
HttpHeader.parse(h._1, h._2)).collect {
+ case HttpHeader.ParsingResult.Ok(header, _) => header
+ }
+ trailerPromise.success(trailers)
Source.single[ByteString](data)
case _ =>
response.entity.discardBytes()
@@ -286,12 +285,14 @@ object PekkoHttpClientUtils {
private def mapToStatusException(response: HttpResponse, trailers:
Seq[HttpHeader]): StatusRuntimeException = {
val allHeaders = response.headers ++ trailers
+ val metadata: io.grpc.Metadata =
+ new MetadataImpl(allHeaders.map(h => (h.name,
StringEntry(h.value))).toList).toGoogleGrpcMetadata()
allHeaders.find(_.name == "grpc-status").map(_.value) match {
case None =>
- new StatusRuntimeException(mapHttpStatus(response).withDescription("No
grpc-status found"))
+ new StatusRuntimeException(mapHttpStatus(response).withDescription("No
grpc-status found"), metadata)
case Some(statusCode) =>
val description = allHeaders.find(_.name ==
"grpc-message").map(_.value)
- new
StatusRuntimeException(Status.fromCodeValue(statusCode.toInt).withDescription(description.orNull))
+ new
StatusRuntimeException(Status.fromCodeValue(statusCode.toInt).withDescription(description.orNull),
metadata)
}
}
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 389b9d82..0915b20d 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
@@ -19,12 +19,12 @@ import scala.concurrent.duration._
import org.apache.pekko
import pekko.actor.ActorSystem
import pekko.http.scaladsl.model.HttpEntity.Strict
-import pekko.http.scaladsl.model.HttpResponse
+import pekko.http.scaladsl.model._
import pekko.http.scaladsl.model.StatusCodes._
import pekko.http.scaladsl.model.headers.RawHeader
import pekko.testkit.TestKit
import pekko.util.ByteString
-import io.grpc.{ Status, StatusRuntimeException }
+import io.grpc.{ Metadata, Status, StatusRuntimeException }
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.time.Span
@@ -47,12 +47,33 @@ class PekkoHttpClientUtilsSpec extends
TestKit(ActorSystem()) with AnyWordSpecLi
}
"map a strict 200 response with non-0 gRPC error code to a failed stream"
in {
+ val responseHeaders = List(RawHeader("grpc-status", "9"),
RawHeader("custom-key", "custom-value-in-header"))
+ val response =
+ Future.successful(HttpResponse(OK, responseHeaders,
Strict(GrpcProtocolNative.contentType, ByteString.empty)))
+ val source = PekkoHttpClientUtils.responseToSource(response, null)
+
+ val failure = source.run().failed.futureValue
+ failure.asInstanceOf[StatusRuntimeException].getStatus.getCode should
be(Status.Code.FAILED_PRECONDITION)
+ failure.asInstanceOf[StatusRuntimeException].getTrailers.get(key) should
be("custom-value-in-header")
+ }
+
+ "map a strict 200 response with non-0 gRPC error code with a trailer to a
failed stream with trailer metadata" in {
+ val responseHeaders = List(RawHeader("grpc-status", "9"))
+ val responseTrailers = Trailer(RawHeader("custom-key",
"custom-trailer-value") :: Nil)
val response = Future.successful(
- HttpResponse(OK, List(RawHeader("grpc-status", "9")),
Strict(GrpcProtocolNative.contentType, ByteString.empty)))
+ new HttpResponse(
+ OK,
+ responseHeaders,
+ Map.empty[AttributeKey[_], Any].updated(AttributeKeys.trailer,
responseTrailers),
+ Strict(GrpcProtocolNative.contentType, ByteString.empty),
+ HttpProtocols.`HTTP/1.1`))
val source = PekkoHttpClientUtils.responseToSource(response, null)
val failure = source.run().failed.futureValue
failure.asInstanceOf[StatusRuntimeException].getStatus.getCode should
be(Status.Code.FAILED_PRECONDITION)
+ failure.asInstanceOf[StatusRuntimeException].getTrailers.get(key) should
be("custom-trailer-value")
}
+
+ lazy val key = Metadata.Key.of("custom-key",
Metadata.ASCII_STRING_MARSHALLER)
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]