io.grpc.Status Scala Examples

The following examples show how to use io.grpc.Status. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: ApiLedgerIdentityService.scala    From daml   with Apache License 2.0 6 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.apiserver.services

import com.daml.dec.DirectExecutionContext
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.ledger_identity_service.LedgerIdentityServiceGrpc.{
  LedgerIdentityService => GrpcLedgerIdentityService
}
import com.daml.ledger.api.v1.ledger_identity_service.{
  GetLedgerIdentityRequest,
  GetLedgerIdentityResponse,
  LedgerIdentityServiceGrpc
}
import com.daml.logging.{ContextualizedLogger, LoggingContext}
import com.daml.platform.api.grpc.GrpcApiService
import com.daml.platform.server.api.ApiException
import io.grpc.{BindableService, ServerServiceDefinition, Status}
import scalaz.syntax.tag._

import scala.concurrent.Future

final class ApiLedgerIdentityService private (getLedgerId: () => Future[LedgerId])(
    implicit logCtx: LoggingContext)
    extends GrpcLedgerIdentityService
    with GrpcApiService {

  @volatile var closed = false

  private val logger = ContextualizedLogger.get(this.getClass)

  override def getLedgerIdentity(
      request: GetLedgerIdentityRequest): Future[GetLedgerIdentityResponse] =
    if (closed)
      Future.failed(
        new ApiException(
          Status.UNAVAILABLE
            .withDescription("Ledger Identity Service closed.")))
    else {
      getLedgerId()
        .map(ledgerId => GetLedgerIdentityResponse(ledgerId.unwrap))(DirectExecutionContext)
        .andThen(logger.logErrorsOnCall[GetLedgerIdentityResponse])(DirectExecutionContext)
    }

  override def close(): Unit = closed = true

  override def bindService(): ServerServiceDefinition =
    LedgerIdentityServiceGrpc.bindService(this, DirectExecutionContext)
}

object ApiLedgerIdentityService {
  def create(getLedgerId: () => Future[LedgerId])(
      implicit logCtx: LoggingContext): ApiLedgerIdentityService with BindableService = {
    new ApiLedgerIdentityService(getLedgerId)
  }
} 
Example 2
Source File: AkkaDiscoveryNameResolver.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.internal

import java.net.{ InetAddress, InetSocketAddress, UnknownHostException }

import akka.discovery.ServiceDiscovery.ResolvedTarget
import akka.discovery.{ Lookup, ServiceDiscovery }
import akka.grpc.GrpcClientSettings
import io.grpc.{ Attributes, EquivalentAddressGroup, NameResolver, Status }
import io.grpc.NameResolver.Listener

import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ ExecutionContext, Promise }
import scala.util.{ Failure, Success }

class AkkaDiscoveryNameResolver(
    discovery: ServiceDiscovery,
    defaultPort: Int,
    serviceName: String,
    portName: Option[String],
    protocol: Option[String],
    resolveTimeout: FiniteDuration)(implicit val ec: ExecutionContext)
    extends NameResolver {
  override def getServiceAuthority: String = serviceName

  val listener: Promise[Listener] = Promise()

  override def start(l: Listener): Unit = {
    listener.trySuccess(l)
    lookup(l)
  }

  override def refresh(): Unit =
    listener.future.onComplete {
      case Success(l) => lookup(l)
      case Failure(_) => // We never fail this promise
    }

  def lookup(listener: Listener): Unit = {
    discovery.lookup(Lookup(serviceName, portName, protocol), resolveTimeout).onComplete {
      case Success(result) =>
        try {
          listener.onAddresses(addresses(result.addresses), Attributes.EMPTY)
        } catch {
          case e: UnknownHostException =>
            // TODO at least log
            listener.onError(Status.UNKNOWN.withDescription(e.getMessage))
        }
      case Failure(e) =>
        // TODO at least log
        listener.onError(Status.UNKNOWN.withDescription(e.getMessage))
    }
  }

  @throws[UnknownHostException]
  private def addresses(addresses: Seq[ResolvedTarget]) = {
    import scala.collection.JavaConverters._
    addresses
      .map(target => {
        val port = target.port.getOrElse(defaultPort)
        val address = target.address.getOrElse(InetAddress.getByName(target.host))
        new EquivalentAddressGroup(new InetSocketAddress(address, port))
      })
      .asJava
  }

  override def shutdown(): Unit = ()
}

object AkkaDiscoveryNameResolver {
  def apply(settings: GrpcClientSettings)(implicit ec: ExecutionContext): AkkaDiscoveryNameResolver =
    new AkkaDiscoveryNameResolver(
      settings.serviceDiscovery,
      settings.defaultPort,
      settings.serviceName,
      settings.servicePortName,
      settings.serviceProtocol,
      settings.resolveTimeout)
} 
Example 3
Source File: PlayScalaTestSpec.scala    From play-grpc   with Apache License 2.0 5 votes vote down vote up
package play.grpc.scalatest

import io.grpc.Status

import org.scalatest.concurrent.IntegrationPatience
import org.scalatest.concurrent.ScalaFutures
import org.scalatestplus.play.PlaySpec
import org.scalatestplus.play.guice.GuiceOneServerPerTest

import play.api.Application
import play.api.inject.bind
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.libs.ws.WSClient
import play.api.routing.Router

import akka.grpc.internal.GrpcProtocolNative

import example.myapp.helloworld.grpc.helloworld._


class PlayScalaTestSpec
    extends PlaySpec
    with GuiceOneServerPerTest
    with ServerGrpcClient
    with ScalaFutures
    with IntegrationPatience {

  override def fakeApplication(): Application = {
    GuiceApplicationBuilder()
      .overrides(bind[Router].to[GreeterServiceImpl])
      .build()
  }

  implicit def ws: WSClient = app.injector.instanceOf(classOf[WSClient])

  "A Play server bound to a gRPC router" must {
    "give a 404 when routing a non-gRPC request" in {
      val result = wsUrl("/").get.futureValue
      result.status must be(404) // Maybe should be a 426, see #396
    }
    "give a 415 error when not using a gRPC content-type" in {
      val result = wsUrl(s"/${GreeterService.name}/FooBar").get.futureValue
      result.status must be(415)
    }
    "give a grpc 'unimplemented' error when routing a non-existent gRPC method" in {
      val result = wsUrl(s"/${GreeterService.name}/FooBar")
        .addHttpHeaders("Content-Type" -> GrpcProtocolNative.contentType.toString)
        .get
        .futureValue
      result.status must be(200) // Maybe should be a 426, see #396
      result.header("grpc-status") mustEqual Some(Status.Code.UNIMPLEMENTED.value().toString)
    }
    "give a grpc 'invalid argument' error when routing an empty request to a gRPC method" in {
      val result = wsUrl(s"/${GreeterService.name}/SayHello")
        .addHttpHeaders("Content-Type" -> GrpcProtocolNative.contentType.toString)
        .get
        .futureValue
      result.status must be(200)
      result.header("grpc-status") mustEqual Some(Status.Code.INVALID_ARGUMENT.value().toString)
    }
    "work with a gRPC client" in withGrpcClient[GreeterServiceClient] { client: GreeterServiceClient =>
      val reply = client.sayHello(HelloRequest("Alice")).futureValue
      reply.message must be("Hello, Alice!")
    }
  }
} 
Example 4
Source File: GrpcHandler.scala    From haystack-traces   with Apache License 2.0 5 votes vote down vote up
package com.expedia.www.haystack.trace.reader.services

import com.expedia.www.haystack.commons.metrics.MetricsSupport
import com.google.protobuf.GeneratedMessageV3
import io.grpc.Status
import io.grpc.stub.StreamObserver
import org.slf4j.{Logger, LoggerFactory}

import scala.concurrent.{ExecutionContextExecutor, Future}
import scala.util.{Failure, Success}

object GrpcHandler {
  protected val LOGGER: Logger = LoggerFactory.getLogger(classOf[GrpcHandler])
}



class GrpcHandler(operationName: String)(implicit val executor: ExecutionContextExecutor) extends MetricsSupport {
  private val metricFriendlyOperationName = operationName.replace('/', '.')
  private val timer = metricRegistry.timer(metricFriendlyOperationName)
  private val failureMeter = metricRegistry.meter(s"$metricFriendlyOperationName.failures")

  import GrpcHandler._

  def handle[Rs](request: GeneratedMessageV3, responseObserver: StreamObserver[Rs])(op: => Future[Rs]): Unit = {
    val time = timer.time()
    op onComplete {
      case Success(response) =>
        responseObserver.onNext(response)
        responseObserver.onCompleted()
        time.stop()
        LOGGER.debug(s"service invocation for operation=$operationName and request=${request.toString} completed successfully")

      case Failure(ex) =>
        responseObserver.onError(Status.fromThrowable(ex).asRuntimeException())
        failureMeter.mark()
        time.stop()
        LOGGER.error(s"service invocation for operation=$operationName and request=${request.toString} failed with error", ex)
    }
  }
} 
Example 5
Source File: GrpcHandler.scala    From haystack-traces   with Apache License 2.0 5 votes vote down vote up
package com.expedia.www.haystack.trace.storage.backends.cassandra.services

import com.expedia.www.haystack.commons.metrics.MetricsSupport
import com.expedia.www.haystack.trace.storage.backends.cassandra.services.GrpcHandler._
import com.google.protobuf.GeneratedMessageV3
import io.grpc.Status
import io.grpc.stub.StreamObserver
import org.slf4j.{Logger, LoggerFactory}

import scala.concurrent.{ExecutionContextExecutor, Future}
import scala.util.{Failure, Success}

object GrpcHandler {
  protected val LOGGER: Logger = LoggerFactory.getLogger(classOf[GrpcHandler])
}



class GrpcHandler(operationName: String)(implicit val executor: ExecutionContextExecutor) extends MetricsSupport {
  private val metricFriendlyOperationName = operationName.replace('/', '.')
  private val timer = metricRegistry.timer(metricFriendlyOperationName)
  private val failureMeter = metricRegistry.meter(s"$metricFriendlyOperationName.failures")

  def handle[Rs](request: GeneratedMessageV3, responseObserver: StreamObserver[Rs])(op: => Future[Rs]): Unit = {
    val time = timer.time()
    op onComplete {
      case Success(response) =>
        responseObserver.onNext(response)
        responseObserver.onCompleted()
        time.stop()
        LOGGER.debug(s"service invocation for operation=$operationName and request=${request.toString} completed successfully")

      case Failure(ex) =>
        responseObserver.onError(Status.fromThrowable(ex).asRuntimeException())
        failureMeter.mark()
        time.stop()
        LOGGER.debug(s"service invocation for operation=$operationName and request=${request.toString} failed with error", ex)
    }
  }
} 
Example 6
Source File: TestServiceImpl.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.interop

import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.reflect.ClassTag
import scala.collection.immutable

import akka.grpc.scaladsl.{GrpcMarshalling}

import akka.NotUsed
import akka.actor.ActorSystem
import akka.grpc._
import akka.stream.scaladsl.{Flow, Source}
import akka.stream.{ Materializer, SystemMaterializer }

import com.google.protobuf.ByteString
import io.grpc.{ Status, StatusRuntimeException }

// Generated by our plugin
import io.grpc.testing.integration.test.TestService
import io.grpc.testing.integration.messages._
import io.grpc.testing.integration.empty.Empty

object TestServiceImpl {
  val parametersToResponseFlow: Flow[ResponseParameters, StreamingOutputCallResponse, NotUsed] =
    Flow[ResponseParameters]
      .map { parameters =>
        StreamingOutputCallResponse(
          Some(Payload(body = ByteString.copyFrom(new Array[Byte](parameters.size)))))
      }
}


class TestServiceImpl(implicit sys: ActorSystem) extends TestService {
  import TestServiceImpl._

  implicit val mat: Materializer = SystemMaterializer(sys).materializer
  implicit val ec: ExecutionContext = sys.dispatcher
  
  override def emptyCall(req: Empty) =
    Future.successful(Empty())

  override def unaryCall(req: SimpleRequest): Future[SimpleResponse] = {
    req.responseStatus match {
      case None =>
        Future.successful(SimpleResponse(Some(Payload(ByteString.copyFrom(new Array[Byte](req.responseSize))))))
      case Some(requestStatus) =>
        val responseStatus = Status.fromCodeValue(requestStatus.code).withDescription(requestStatus.message)
        //  - Either one of the following works
        Future.failed(new GrpcServiceException(responseStatus))
        // throw new GrpcServiceException(responseStatus)
    }
  }

  override def cacheableUnaryCall(in: SimpleRequest): Future[SimpleResponse] = ???

  override def fullDuplexCall(in: Source[StreamingOutputCallRequest, NotUsed]): Source[StreamingOutputCallResponse, NotUsed] =
    in.map(req => {
      req.responseStatus.foreach(reqStatus =>
        throw new GrpcServiceException(
          Status.fromCodeValue(reqStatus.code).withDescription(reqStatus.message)))
      req
    }).mapConcat(
      _.responseParameters.to[immutable.Seq]).via(parametersToResponseFlow)

  override def halfDuplexCall(in: Source[StreamingOutputCallRequest, NotUsed]): Source[StreamingOutputCallResponse, NotUsed] = ???

  override def streamingInputCall(in: Source[StreamingInputCallRequest, NotUsed]): Future[StreamingInputCallResponse] = {
    in
      .map(_.payload.map(_.body.size).getOrElse(0))
      .runFold(0)(_ + _)
      .map { sum =>
        StreamingInputCallResponse(sum)
      }
  }

  override def streamingOutputCall(in: StreamingOutputCallRequest): Source[StreamingOutputCallResponse, NotUsed] =
    Source(in.responseParameters.to[immutable.Seq]).via(parametersToResponseFlow)

  override def unimplementedCall(in: Empty): Future[Empty] = ???
} 
Example 7
Source File: ErrorReportingSpec.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package example.myapp.helloworld

import akka.actor.{ ActorSystem, ClassicActorSystemProvider }
import akka.grpc.internal.GrpcProtocolNative
import akka.http.scaladsl.model.HttpEntity.{ Chunked, LastChunk }
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers.RawHeader
import akka.http.scaladsl.{ Http, HttpConnectionContext }
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Sink
import example.myapp.helloworld.grpc.{ GreeterService, GreeterServiceHandler }
import io.grpc.Status
import org.junit.runner.RunWith
import org.scalatest.BeforeAndAfterAll
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.time.Span
import org.scalatest.wordspec.AnyWordSpec
import org.scalatestplus.junit.JUnitRunner

import scala.concurrent.Await
import scala.concurrent.duration._

@RunWith(classOf[JUnitRunner])
class ErrorReportingSpec extends AnyWordSpec with Matchers with ScalaFutures with BeforeAndAfterAll {

  override implicit val patienceConfig = PatienceConfig(5.seconds, Span(100, org.scalatest.time.Millis))

  implicit val system: ActorSystem = ActorSystem()

  implicit val mat = ActorMaterializer()

  "A gRPC server" should {

    val binding = Http()
      .bindAndHandleAsync(
        GreeterServiceHandler(new GreeterServiceImpl())(system.asInstanceOf[ClassicActorSystemProvider]),
        interface = "127.0.0.1",
        port = 0,
        connectionContext = HttpConnectionContext())
      .futureValue

    "respond with an 'unimplemented' gRPC error status when calling an unknown method" in {
      val request = HttpRequest(
        method = HttpMethods.POST,
        entity = HttpEntity.empty(GrpcProtocolNative.contentType),
        uri = s"http://localhost:${binding.localAddress.getPort}/${GreeterService.name}/UnknownMethod")
      val response = Http().singleRequest(request).futureValue

      response.status should be(StatusCodes.OK)
      allHeaders(response) should contain(RawHeader("grpc-status", Status.Code.UNIMPLEMENTED.value().toString))
    }

    "respond with an 'invalid argument' gRPC error status when calling an method without a request body" in {
      val request = HttpRequest(
        method = HttpMethods.POST,
        entity = HttpEntity.empty(GrpcProtocolNative.contentType),
        uri = s"http://localhost:${binding.localAddress.getPort}/${GreeterService.name}/SayHello")
      val response = Http().singleRequest(request).futureValue

      response.status should be(StatusCodes.OK)
      allHeaders(response) should contain(RawHeader("grpc-status", Status.Code.INVALID_ARGUMENT.value().toString))
    }

    def allHeaders(response: HttpResponse) =
      response.entity match {
        case Chunked(_, chunks) =>
          chunks.runWith(Sink.last).futureValue match {
            case LastChunk(_, trailingHeaders) => response.headers ++ trailingHeaders
            case _                             => response.headers
          }
        case _ =>
          response.headers
      }
  }

  override def afterAll: Unit =
    Await.result(system.terminate(), 5.seconds)
} 
Example 8
Source File: ErrorReportingSpec.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package example.myapp.helloworld

import akka.actor.ActorSystem
import akka.grpc.internal.GrpcProtocolNative
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.HttpEntity.{ Chunked, LastChunk }
import akka.http.scaladsl.model.headers.RawHeader
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Sink
import example.myapp.helloworld.grpc.{ GreeterService, GreeterServiceHandlerFactory }
import io.grpc.Status
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.time.Span
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

import scala.concurrent.Await
import scala.concurrent.duration._

class ErrorReportingSpec extends AnyWordSpec with Matchers with ScalaFutures with BeforeAndAfterAll {
  implicit val sys = ActorSystem()
  override implicit val patienceConfig = PatienceConfig(5.seconds, Span(100, org.scalatest.time.Millis))

  "A gRPC server" should {
    implicit val mat = ActorMaterializer()

    val handler = GreeterServiceHandlerFactory.create(new GreeterServiceImpl(mat), sys)
    val binding = {
      import akka.http.javadsl.{ ConnectHttp, Http }

      Http(sys).bindAndHandleAsync(handler, ConnectHttp.toHost("127.0.0.1", 0), mat).toCompletableFuture.get
    }

    "respond with an 'unimplemented' gRPC error status when calling an unknown method" in {
      val request = HttpRequest(
        method = HttpMethods.POST,
        entity = HttpEntity.empty(GrpcProtocolNative.contentType),
        uri = s"http://localhost:${binding.localAddress.getPort}/${GreeterService.name}/UnknownMethod")
      val response = Http().singleRequest(request).futureValue

      response.status should be(StatusCodes.OK)
      allHeaders(response) should contain(RawHeader("grpc-status", Status.Code.UNIMPLEMENTED.value().toString))
    }

    "respond with an 'invalid argument' gRPC error status when calling an method without a request body" in {
      val request = HttpRequest(
        method = HttpMethods.POST,
        entity = HttpEntity.empty(GrpcProtocolNative.contentType),
        uri = s"http://localhost:${binding.localAddress.getPort}/${GreeterService.name}/SayHello")
      val response = Http().singleRequest(request).futureValue

      response.status should be(StatusCodes.OK)
      allHeaders(response) should contain(RawHeader("grpc-status", Status.Code.INVALID_ARGUMENT.value().toString))
    }

    def allHeaders(response: HttpResponse) =
      response.entity match {
        case Chunked(_, chunks) =>
          chunks.runWith(Sink.last).futureValue match {
            case LastChunk(_, trailingHeaders) => response.headers ++ trailingHeaders
            case _                             => response.headers
          }
        case _ =>
          response.headers
      }
  }

  override def afterAll: Unit =
    Await.result(sys.terminate(), 5.seconds)
} 
Example 9
Source File: GrpcExceptionHandler.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.javadsl

import java.util.concurrent.CompletionException

import akka.actor.ActorSystem
import akka.actor.ClassicActorSystemProvider
import akka.annotation.ApiMayChange
import akka.annotation.InternalApi
import akka.grpc.{ GrpcServiceException, Trailers }
import akka.grpc.GrpcProtocol.GrpcProtocolWriter
import akka.grpc.internal.{ GrpcResponseHelpers, MissingParameterException }
import akka.http.javadsl.model.HttpResponse
import akka.japi.{ Function => jFunction }
import io.grpc.Status

import scala.concurrent.ExecutionException

@ApiMayChange
object GrpcExceptionHandler {
  private val INTERNAL = Trailers(Status.INTERNAL)
  private val INVALID_ARGUMENT = Trailers(Status.INVALID_ARGUMENT)
  private val UNIMPLEMENTED = Trailers(Status.UNIMPLEMENTED)

  def defaultMapper: jFunction[ActorSystem, jFunction[Throwable, Trailers]] =
    new jFunction[ActorSystem, jFunction[Throwable, Trailers]] {
      override def apply(system: ActorSystem): jFunction[Throwable, Trailers] =
        default(system)
    }

  
  @InternalApi
  private def default(system: ActorSystem): jFunction[Throwable, Trailers] =
    new jFunction[Throwable, Trailers] {
      override def apply(param: Throwable): Trailers =
        param match {
          case e: ExecutionException =>
            if (e.getCause == null) INTERNAL
            else default(system)(e.getCause)
          case e: CompletionException =>
            if (e.getCause == null) INTERNAL
            else default(system)(e.getCause)
          case grpcException: GrpcServiceException => Trailers(grpcException.status, grpcException.metadata)
          case _: MissingParameterException        => INVALID_ARGUMENT
          case _: NotImplementedError              => UNIMPLEMENTED
          case _: UnsupportedOperationException    => UNIMPLEMENTED
          case other =>
            system.log.error(other, "Unhandled error: [" + other.getMessage + "]")
            INTERNAL
        }
    }

  def standard(t: Throwable, writer: GrpcProtocolWriter, system: ClassicActorSystemProvider): HttpResponse =
    standard(t, default, writer, system)

  def standard(
      t: Throwable,
      mapper: jFunction[ActorSystem, jFunction[Throwable, Trailers]],
      writer: GrpcProtocolWriter,
      system: ClassicActorSystemProvider): HttpResponse =
    GrpcResponseHelpers.status(mapper(system.classicSystem)(t))(writer)
} 
Example 10
Source File: GrpcExceptionHandler.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.scaladsl

import akka.actor.ActorSystem
import akka.actor.ClassicActorSystemProvider
import akka.annotation.{ ApiMayChange, InternalStableApi }
import akka.grpc.{ GrpcServiceException, Trailers }
import akka.grpc.GrpcProtocol.GrpcProtocolWriter
import akka.grpc.internal.{ GrpcResponseHelpers, MissingParameterException }
import akka.http.scaladsl.model.HttpResponse
import io.grpc.Status

import scala.concurrent.{ ExecutionException, Future }

@ApiMayChange
object GrpcExceptionHandler {
  private val INTERNAL = Trailers(Status.INTERNAL)
  private val INVALID_ARGUMENT = Trailers(Status.INVALID_ARGUMENT)
  private val UNIMPLEMENTED = Trailers(Status.UNIMPLEMENTED)

  def defaultMapper(system: ActorSystem): PartialFunction[Throwable, Trailers] = {
    case e: ExecutionException =>
      if (e.getCause == null) INTERNAL
      else defaultMapper(system)(e.getCause)
    case grpcException: GrpcServiceException => Trailers(grpcException.status, grpcException.metadata)
    case _: NotImplementedError              => UNIMPLEMENTED
    case _: UnsupportedOperationException    => UNIMPLEMENTED
    case _: MissingParameterException        => INVALID_ARGUMENT
    case other =>
      system.log.error(other, s"Unhandled error: [${other.getMessage}].")
      INTERNAL
  }

  @InternalStableApi
  def default(
      implicit system: ClassicActorSystemProvider,
      writer: GrpcProtocolWriter): PartialFunction[Throwable, Future[HttpResponse]] =
    from(defaultMapper(system.classicSystem))

  @InternalStableApi
  def from(mapper: PartialFunction[Throwable, Trailers])(
      implicit system: ClassicActorSystemProvider,
      writer: GrpcProtocolWriter): PartialFunction[Throwable, Future[HttpResponse]] =
    mapper.orElse(defaultMapper(system.classicSystem)).andThen(s => Future.successful(GrpcResponseHelpers.status(s)))

} 
Example 11
Source File: GrpcProtocolWeb.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.internal

import akka.grpc.GrpcProtocol._
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.HttpEntity.{ Chunk, ChunkStreamPart }
import akka.stream.scaladsl.Flow
import akka.util.ByteString
import io.grpc.{ Status, StatusException }

abstract class GrpcProtocolWebBase(subType: String) extends AbstractGrpcProtocol(subType) {
  protected def postEncode(frame: ByteString): ByteString
  protected def preDecode(frame: ByteString): ByteString

  override protected def writer(codec: Codec): GrpcProtocolWriter =
    AbstractGrpcProtocol.writer(this, codec, frame => encodeFrame(codec, frame))

  override protected def reader(codec: Codec): GrpcProtocolReader =
    AbstractGrpcProtocol.reader(codec, decodeFrame, flow => Flow[ByteString].map(preDecode).via(flow))

  @inline
  private def encodeFrame(codec: Codec, frame: Frame): ChunkStreamPart = {
    val dataFrameType = AbstractGrpcProtocol.fieldType(codec)
    val (frameType, data) = frame match {
      case DataFrame(data)       => (dataFrameType, data)
      case TrailerFrame(trailer) => (ByteString(dataFrameType(0) | 0x80), encodeTrailer(trailer))
    }
    val framed = AbstractGrpcProtocol.encodeFrameData(frameType, codec.compress(data))
    Chunk(postEncode(framed))
  }

  @inline
  private final def decodeFrame(frameHeader: Int, data: ByteString): Frame = {
    (frameHeader & 80) match {
      case 0 => DataFrame(data)
      case 1 => TrailerFrame(decodeTrailer(data))
      case f => throw new StatusException(Status.INTERNAL.withDescription(s"Unknown frame type [$f]"))
    }
  }

  @inline
  private final def encodeTrailer(trailer: Seq[HttpHeader]): ByteString =
    ByteString(trailer.mkString("", "\r\n", "\r\n"))

  @inline
  private final def decodeTrailer(data: ByteString): List[HttpHeader] = ???

}


object GrpcProtocolWebText extends GrpcProtocolWebBase("grpc-web-text") {

  override final def postEncode(framed: ByteString): ByteString = framed.encodeBase64

  override final def preDecode(frame: ByteString): ByteString = frame.decodeBase64
} 
Example 12
Source File: GrpcRequestHelpers.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.internal

import akka.actor.ActorSystem
import akka.actor.ClassicActorSystemProvider
import akka.grpc.{ ProtobufSerializer, Trailers }
import akka.grpc.GrpcProtocol.GrpcProtocolWriter
import akka.http.scaladsl.model.HttpEntity.ChunkStreamPart
import akka.stream.scaladsl.Source
import akka.NotUsed
import akka.annotation.InternalApi
import akka.grpc.scaladsl.{ headers, GrpcExceptionHandler }
import akka.http.scaladsl.model.{ HttpEntity, HttpMethods, HttpRequest, Uri }
import io.grpc.Status

import scala.collection.immutable

@InternalApi
object GrpcRequestHelpers {

  def apply[T](
      uri: Uri,
      e: Source[T, NotUsed],
      eHandler: ActorSystem => PartialFunction[Throwable, Trailers] = GrpcExceptionHandler.defaultMapper)(
      implicit m: ProtobufSerializer[T],
      writer: GrpcProtocolWriter,
      system: ClassicActorSystemProvider): HttpRequest =
    request(uri, GrpcEntityHelpers(e, Source.single(GrpcEntityHelpers.trailer(Status.OK)), eHandler))

  private def request[T](uri: Uri, entity: Source[ChunkStreamPart, NotUsed])(
      implicit writer: GrpcProtocolWriter): HttpRequest = {
    HttpRequest(
      uri = uri,
      method = HttpMethods.POST,
      headers = immutable.Seq(
        headers.`Message-Encoding`(writer.messageEncoding.name),
        headers.`Message-Accept-Encoding`(Codecs.supportedCodecs.map(_.name).mkString(","))),
      entity = HttpEntity.Chunked(writer.contentType, entity))
  }

} 
Example 13
Source File: GrpcEntityHelpers.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.internal

import akka.NotUsed
import akka.actor.{ ActorSystem, ClassicActorSystemProvider }
import akka.annotation.InternalApi
import akka.grpc.{ GrpcServiceException, ProtobufSerializer, Trailers }
import akka.grpc.GrpcProtocol.{ DataFrame, Frame, GrpcProtocolWriter, TrailerFrame }
import akka.grpc.scaladsl.{ headers, BytesEntry, Metadata, StringEntry }
import akka.http.scaladsl.model.HttpEntity.ChunkStreamPart
import akka.http.scaladsl.model.HttpHeader
import akka.http.scaladsl.model.headers.RawHeader
import akka.stream.scaladsl.Source
import io.grpc.Status


@InternalApi
object GrpcEntityHelpers {
  def apply[T](
      e: Source[T, NotUsed],
      trail: Source[TrailerFrame, NotUsed],
      eHandler: ActorSystem => PartialFunction[Throwable, Trailers])(
      implicit m: ProtobufSerializer[T],
      writer: GrpcProtocolWriter,
      system: ClassicActorSystemProvider): Source[ChunkStreamPart, NotUsed] = {
    chunks(e, trail).recover {
      case t =>
        val e = eHandler(system.classicSystem).orElse[Throwable, Trailers] {
          case e: GrpcServiceException => Trailers(e.status, e.metadata)
          case e: Exception            => Trailers(Status.UNKNOWN.withCause(e).withDescription("Stream failed"))
        }(t)
        writer.encodeFrame(trailer(e.status, e.metadata))
    }
  }

  def apply[T](e: T)(implicit m: ProtobufSerializer[T], writer: GrpcProtocolWriter): Source[ChunkStreamPart, NotUsed] =
    chunks(Source.single(e), Source.empty)

  private def chunks[T](e: Source[T, NotUsed], trail: Source[Frame, NotUsed])(
      implicit m: ProtobufSerializer[T],
      writer: GrpcProtocolWriter): Source[ChunkStreamPart, NotUsed] =
    e.map { msg => DataFrame(m.serialize(msg)) }.concat(trail).via(writer.frameEncoder)

  def trailer(status: Status): TrailerFrame =
    TrailerFrame(trailers = statusHeaders(status))

  def trailer(status: Status, metadata: Metadata): TrailerFrame =
    TrailerFrame(trailers = statusHeaders(status) ++ metadataHeaders(metadata))

  def statusHeaders(status: Status): List[HttpHeader] =
    List(headers.`Status`(status.getCode.value.toString)) ++ Option(status.getDescription).map(d =>
      headers.`Status-Message`(d))

  def metadataHeaders(metadata: Metadata): List[HttpHeader] =
    metadata.asList.map {
      case (key, StringEntry(value)) => RawHeader(key, value)
      case (key, BytesEntry(value))  => RawHeader(key, MetadataImpl.encodeBinaryHeader(value))
    }
} 
Example 14
Source File: package.scala    From gatling-grpc   with Apache License 2.0 5 votes vote down vote up
package com.github.phisgr.gatling.grpc

import java.lang.{StringBuilder => JStringBuilder}

import com.google.common.base.Charsets.US_ASCII
import io.gatling.commons.util.StringHelper.Eol
import io.grpc.{InternalMetadata, Metadata, Status}

package object util {

  implicit private[gatling] class GrpcStringBuilder(val buff: JStringBuilder) extends AnyVal {
    def appendWithEol(s: String): JStringBuilder =
      buff.append(s).append(Eol)

    def appendWithEol(o: Object): JStringBuilder =
      buff.append(o).append(Eol)

    def appendRequest(payload: Any, headers: Metadata): JStringBuilder = {
      val payloadString = payload match {
        case scalaPbObject: scalapb.GeneratedMessage =>
          scalaPbObject.toProtoString
        case _ => payload.toString // for Java Proto messages, this is the proto string
      }
      appendHeaders(headers)
      appendWithEol("payload=")
      appendWithEol(payloadString)
    }

    def appendResponse(body: Any, status: Status, trailers: Metadata): JStringBuilder = {
      appendStatus(status)
      appendTrailers(trailers)

      val bodyString = body match {
        case _ if null == body || !status.isOk =>
          null
        case scalaPbObject: scalapb.GeneratedMessage =>
          scalaPbObject.toProtoString
        case _ => body.toString // for Java Proto messages, this is the proto string
      }
      if (bodyString ne null) {
        buff
          .appendWithEol("body=")
          .appendWithEol(bodyString)
      }
      buff
    }

    private def appendStatus(s: Status): JStringBuilder = {
      buff.append("status=").append(Eol)
        .append(s.getCode)
      val description = s.getDescription
      if (description ne null) {
        buff.append(", description: ").append(description)
      }
      buff.append(Eol)

      val cause = s.getCause
      if (cause ne null) {
        buff.append("cause: ").appendWithEol(cause.toString)
      }
      buff
    }

    private def appendHeaders(headers: Metadata): JStringBuilder =
      appendMetadata(headers, "headers")

    private def appendTrailers(trailers: Metadata): JStringBuilder =
      appendMetadata(trailers, "trailers")

    private def appendMetadata(metadata: Metadata, headersOrTrailers: String): JStringBuilder = {
      val size = InternalMetadata.headerCount(metadata)
      if (size != 0) {
        buff.append(headersOrTrailers).appendWithEol("=")

        var i = 0
        while (i < size) {
          val headerName = new String(Reflections.name(metadata, i), US_ASCII)
          buff.append(headerName).append(": ")

          val valueBytes = Reflections.value(metadata, i)
          val valueString = if (headerName.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
            InternalMetadata.BASE64_ENCODING_OMIT_PADDING.encode(valueBytes)
          } else {
            new String(valueBytes, US_ASCII)
          }
          buff.appendWithEol(valueString)

          i += 1
        }
      }
      buff
    }
  }

} 
Example 15
Source File: GrpcResponseHelpers.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.internal

import akka.NotUsed
import akka.actor.ActorSystem
import akka.actor.ClassicActorSystemProvider
import akka.annotation.InternalApi
import akka.grpc.{ ProtobufSerializer, Trailers }
import akka.grpc.GrpcProtocol.{ GrpcProtocolWriter, TrailerFrame }
import akka.grpc.scaladsl.{ headers, GrpcExceptionHandler }
import akka.http.scaladsl.model.{ HttpEntity, HttpResponse }
import akka.http.scaladsl.model.HttpEntity.ChunkStreamPart
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import io.grpc.Status

import scala.collection.immutable
import scala.concurrent.{ ExecutionContext, Future }


@InternalApi // consumed from generated classes so cannot be private
object GrpcResponseHelpers {
  def apply[T](e: Source[T, NotUsed])(
      implicit m: ProtobufSerializer[T],
      writer: GrpcProtocolWriter,
      system: ClassicActorSystemProvider): HttpResponse =
    GrpcResponseHelpers(e, Source.single(GrpcEntityHelpers.trailer(Status.OK)))

  def apply[T](e: Source[T, NotUsed], eHandler: ActorSystem => PartialFunction[Throwable, Trailers])(
      implicit m: ProtobufSerializer[T],
      writer: GrpcProtocolWriter,
      system: ClassicActorSystemProvider): HttpResponse =
    GrpcResponseHelpers(e, Source.single(GrpcEntityHelpers.trailer(Status.OK)), eHandler)

  def apply[T](e: Source[T, NotUsed], status: Future[Status])(
      implicit m: ProtobufSerializer[T],
      mat: Materializer,
      writer: GrpcProtocolWriter,
      system: ClassicActorSystemProvider): HttpResponse =
    GrpcResponseHelpers(e, status, GrpcExceptionHandler.defaultMapper _)

  def apply[T](
      e: Source[T, NotUsed],
      status: Future[Status],
      eHandler: ActorSystem => PartialFunction[Throwable, Trailers])(
      implicit m: ProtobufSerializer[T],
      mat: Materializer,
      writer: GrpcProtocolWriter,
      system: ClassicActorSystemProvider): HttpResponse = {
    implicit val ec: ExecutionContext = mat.executionContext
    GrpcResponseHelpers(
      e,
      Source.lazilyAsync(() => status.map(GrpcEntityHelpers.trailer)).mapMaterializedValue(_ => NotUsed),
      eHandler)
  }

  def apply[T](
      e: Source[T, NotUsed],
      trail: Source[TrailerFrame, NotUsed],
      eHandler: ActorSystem => PartialFunction[Throwable, Trailers] = GrpcExceptionHandler.defaultMapper)(
      implicit m: ProtobufSerializer[T],
      writer: GrpcProtocolWriter,
      system: ClassicActorSystemProvider): HttpResponse = {
    response(GrpcEntityHelpers(e, trail, eHandler))
  }

  private def response[T](entity: Source[ChunkStreamPart, NotUsed])(implicit writer: GrpcProtocolWriter) = {
    HttpResponse(
      headers = immutable.Seq(headers.`Message-Encoding`(writer.messageEncoding.name)),
      entity = HttpEntity.Chunked(writer.contentType, entity))
  }

  def status(trailer: Trailers)(implicit writer: GrpcProtocolWriter): HttpResponse =
    response(Source.single(writer.encodeFrame(GrpcEntityHelpers.trailer(trailer.status, trailer.metadata))))
} 
Example 16
Source File: Codecs.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.internal

import akka.http.javadsl.{ model => jm }
import akka.grpc.GrpcServiceException
import akka.grpc.scaladsl.headers.{ `Message-Accept-Encoding`, `Message-Encoding` }
import io.grpc.Status

import scala.collection.immutable
import scala.util.{ Failure, Success, Try }

object Codecs {
  // TODO should this list be made user-extensible?
  val supportedCodecs = immutable.Seq(Gzip, Identity)

  private val supported = supportedCodecs.map(_.name)
  private val byName = supportedCodecs.map(c => c.name -> c).toMap

  
  def detect(encoding: Option[String]): Try[Codec] =
    encoding
      .map { codec =>
        byName
          .get(codec)
          .map(Success(_))
          .getOrElse(Failure(new GrpcServiceException(
            Status.UNIMPLEMENTED.withDescription(s"Message Encoding $encoding is not supported"))))
      }
      .getOrElse(Success(Identity))
} 
Example 17
Source File: Trailers.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc

import akka.annotation.ApiMayChange
import io.grpc.Status
import akka.grpc.internal.JavaMetadataImpl
import akka.grpc.scaladsl.{ Metadata, MetadataBuilder }
import akka.grpc.javadsl.{ Metadata => jMetadata }

@ApiMayChange
class Trailers(val status: Status, val metadata: Metadata) {
  def this(status: Status) = {
    this(status, MetadataBuilder.empty)
  }

  def this(status: Status, metadata: jMetadata) = {
    this(status, metadata.asScala)
  }

  
  def getMetadata: jMetadata =
    new JavaMetadataImpl(metadata)
}

object Trailers {
  def apply(status: Status): Trailers = new Trailers(status)
  def apply(status: Status, metadata: Metadata): Trailers = new Trailers(status, metadata)
} 
Example 18
Source File: CodecsSpec.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc
import akka.grpc.internal.{ Codecs, Gzip, Identity }
import akka.grpc.scaladsl.headers
import akka.http.scaladsl.model.HttpRequest
import io.grpc.Status
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import org.scalatest.TryValues

import scala.collection.immutable

class CodecsSpec extends AnyWordSpec with Matchers with TryValues {

  private def accept(encodings: String*): HttpRequest =
    HttpRequest(headers = immutable.Seq(headers.`Message-Accept-Encoding`(encodings.mkString(","))))

  private def enc(encodings: String*): HttpRequest =
    HttpRequest(headers = immutable.Seq(headers.`Message-Encoding`(encodings.mkString(","))))

  "Negotiating message encoding with remote client" should {

    "default to Identity if no encoding provided" in {
      Codecs.negotiate(HttpRequest()) should be(Identity)
    }

    "accept explicit Identity" in {
      Codecs.negotiate(accept(Identity.name)) should be(Identity)
    }

    "accept explicit Gzip" in {
      Codecs.negotiate(accept(Gzip.name)) should be(Gzip)
    }

    "use client preference with multiple known encodings" in {
      Codecs.negotiate(accept(Gzip.name, Identity.name)) should be(Gzip)
      Codecs.negotiate(accept(Identity.name, Gzip.name)) should be(Identity)
    }

    "use first known encoding" in {
      Codecs.negotiate(accept("xxxxx", Gzip.name, Identity.name)) should be(Gzip)
    }

    "use default encoding if unknown encodings specified" in {
      Codecs.negotiate(accept("xxxxx")) should be(Identity)
    }

  }

  "Detecting message encoding from remote" should {

    "default to Identity if not specified" in {
      Codecs.detect(HttpRequest()).success.value should be(Identity)
    }

    "accept explicit Identity" in {
      Codecs.detect(enc(Identity.name)).success.value should be(Identity)
    }

    "accept explicit Gzip" in {
      Codecs.detect(enc(Gzip.name)).success.value should be(Gzip)
    }

    "fail with unknown encoding" in {
      val detected = Codecs.detect(enc("xxxxxxx"))
      detected.failure.exception shouldBe a[GrpcServiceException]
      detected.failure.exception.asInstanceOf[GrpcServiceException].status.getCode should be(
        Status.UNIMPLEMENTED.getCode)
    }
  }

} 
Example 19
Source File: GrpcExceptionHandlerSpec.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.scaladsl

import akka.actor.ActorSystem
import akka.grpc.GrpcServiceException
import akka.grpc.internal.{ GrpcProtocolNative, GrpcResponseHelpers, Identity }
import akka.grpc.scaladsl.GrpcExceptionHandler.defaultMapper
import akka.http.scaladsl.model.HttpEntity._
import akka.http.scaladsl.model.HttpResponse
import akka.stream.ActorMaterializer
import io.grpc.Status
import org.scalatest._
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.time.{ Millis, Seconds, Span }
import org.scalatest.wordspec.AnyWordSpec

import scala.concurrent.{ ExecutionException, Future }

class GrpcExceptionHandlerSpec extends AnyWordSpec with Matchers with ScalaFutures with BeforeAndAfterAll {
  implicit val system = ActorSystem("Test")
  implicit val materializer = ActorMaterializer()
  implicit override val patienceConfig =
    PatienceConfig(timeout = scaled(Span(2, Seconds)), interval = scaled(Span(5, Millis)))
  implicit val writer = GrpcProtocolNative.newWriter(Identity)

  val expected: Function[Throwable, Status] = {
    case e: ExecutionException =>
      if (e.getCause == null) Status.INTERNAL
      else expected(e.getCause)
    case grpcException: GrpcServiceException => grpcException.status
    case _: NotImplementedError              => Status.UNIMPLEMENTED
    case _: UnsupportedOperationException    => Status.UNIMPLEMENTED
    case _                                   => Status.INTERNAL
  }

  val otherTypes: Seq[Throwable] = Seq(
    new GrpcServiceException(status = Status.DEADLINE_EXCEEDED),
    new NotImplementedError,
    new UnsupportedOperationException,
    new NullPointerException,
    new RuntimeException)

  val executionExceptions: Seq[Throwable] =
    otherTypes.map(new ExecutionException(_)) :+ new ExecutionException("doh", null)

  "defaultMapper" should {
    (otherTypes ++ executionExceptions).foreach { e =>
      val exp = expected(e)
      s"Map $e to $exp" in {
        defaultMapper(system)(e).status shouldBe exp
      }
    }
  }

  "default(defaultMapper)" should {
    (otherTypes ++ executionExceptions).foreach { e =>
      s"Correctly map $e" in {
        val exp = GrpcResponseHelpers.status(defaultMapper(system)(e))
        val expChunks = getChunks(exp)
        val act = GrpcExceptionHandler.from(defaultMapper(system))(system, writer)(e).futureValue
        val actChunks = getChunks(act)
        // Following is because aren't equal
        act.status shouldBe exp.status
        actChunks.toString shouldEqual expChunks.toString
      }
    }
  }

  def getChunks(resp: HttpResponse): Seq[ChunkStreamPart] =
    (resp.entity match {
      case Chunked(_, chunks) =>
        chunks.runFold(Seq.empty[ChunkStreamPart]) { case (seq, chunk) => seq :+ chunk }
      case _ => Future.successful(Seq.empty[ChunkStreamPart])
    }).futureValue

  override def afterAll(): Unit = {
    super.afterAll()
    system.terminate()
  }
} 
Example 20
Source File: AkkaDiscoveryNameResolverSpec.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.internal

import java.net.InetSocketAddress

import akka.actor.ActorSystem
import akka.grpc.{ GrpcClientSettings, GrpcServiceException }
import akka.testkit.TestKit
import io.grpc.Status
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.time.{ Millis, Seconds, Span }
import org.scalatest.wordspec.AnyWordSpecLike

import scala.collection.JavaConverters._

class AkkaDiscoveryNameResolverSpec
    extends TestKit(ActorSystem())
    with AnyWordSpecLike
    with Matchers
    with ScalaFutures {
  implicit val ex = system.dispatcher
  implicit override val patienceConfig =
    PatienceConfig(timeout = scaled(Span(2, Seconds)), interval = scaled(Span(5, Millis)))

  "The AkkaDiscovery-backed NameResolver" should {
    "correctly report an error for an unknown hostname" in {
      val host = "example.invalid"
      val resolver = AkkaDiscoveryNameResolver(GrpcClientSettings.connectToServiceAt(host, 80))
      val probe = new NameResolverListenerProbe()

      resolver.start(probe)

      val exception = probe.future.failed.futureValue.asInstanceOf[GrpcServiceException]
      exception shouldBe an[GrpcServiceException]
      exception.status.getCode == Status.UNKNOWN.getCode
      // FIXME: This description is not portable - it arises from native function response, which differs by OS
      // exception.status.getDescription should equal(host + ": Name or service not known")
    }

    "support serving a static host/port" in {
      // Unfortunately it needs to be an actually resolvable address...
      val host = "akka.io"
      val port = 4040
      val resolver = AkkaDiscoveryNameResolver(GrpcClientSettings.connectToServiceAt(host, port))
      val probe = new NameResolverListenerProbe()

      resolver.start(probe)

      val addresses = probe.future.futureValue match {
        case Seq(addressGroup) => addressGroup.getAddresses
        case _                 => fail("Expected a single address group")
      }
      addresses.asScala.toSeq match {
        case Seq(address: InetSocketAddress) =>
          address.getPort should be(port)
          address.getAddress.getHostName should be(host)
        case other =>
          fail(s"Expected a single InetSocketAddress, got $other")
      }
    }
  }
} 
Example 21
Source File: TestServiceImpl.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.interop

import akka.NotUsed
import akka.actor.ActorSystem
import akka.grpc.GrpcServiceException
import akka.stream.{ Materializer, SystemMaterializer }
import akka.stream.scaladsl.{ Flow, Source }

import com.google.protobuf.ByteString

import io.grpc.Status
import io.grpc.testing.integration.empty.Empty

import scala.concurrent.{ ExecutionContext, Future }

// Generated by our plugin
import io.grpc.testing.integration.messages._
import io.grpc.testing.integration.test.TestService

object TestServiceImpl {
  val parametersToResponseFlow: Flow[ResponseParameters, StreamingOutputCallResponse, NotUsed] =
    Flow[ResponseParameters].map { parameters =>
      StreamingOutputCallResponse(Some(Payload(body = ByteString.copyFrom(new Array[Byte](parameters.size)))))
    }
}


class TestServiceImpl(implicit sys: ActorSystem) extends TestService {
  import TestServiceImpl._

  implicit val mat: Materializer = SystemMaterializer(sys).materializer
  implicit val ec: ExecutionContext = sys.dispatcher

  override def emptyCall(req: Empty) =
    Future.successful(Empty())

  override def unaryCall(req: SimpleRequest): Future[SimpleResponse] =
    req.responseStatus match {
      case None =>
        Future.successful(SimpleResponse(Some(Payload(ByteString.copyFrom(new Array[Byte](req.responseSize))))))
      case Some(requestStatus) =>
        val responseStatus = Status.fromCodeValue(requestStatus.code).withDescription(requestStatus.message)
        //  - Either one of the following works
        // Future.failed(new GrpcServiceException(responseStatus))
        throw new GrpcServiceException(responseStatus)
    }

  override def cacheableUnaryCall(in: SimpleRequest): Future[SimpleResponse] = ???

  override def fullDuplexCall(
      in: Source[StreamingOutputCallRequest, NotUsed]): Source[StreamingOutputCallResponse, NotUsed] =
    in.map(req => {
      req.responseStatus.foreach(reqStatus =>
        throw new GrpcServiceException(Status.fromCodeValue(reqStatus.code).withDescription(reqStatus.message)))
      req
    }).mapConcat(_.responseParameters.toList)
      .via(parametersToResponseFlow)

  override def halfDuplexCall(
      in: Source[StreamingOutputCallRequest, NotUsed]): Source[StreamingOutputCallResponse, NotUsed] = ???

  override def streamingInputCall(in: Source[StreamingInputCallRequest, NotUsed]): Future[StreamingInputCallResponse] =
    in.map(_.payload.map(_.body.size).getOrElse(0)).runFold(0)(_ + _).map { sum => StreamingInputCallResponse(sum) }

  override def streamingOutputCall(in: StreamingOutputCallRequest): Source[StreamingOutputCallResponse, NotUsed] =
    Source(in.responseParameters.toList).via(parametersToResponseFlow)

  override def unimplementedCall(in: Empty): Future[Empty] = ???
} 
Example 22
Source File: GrpcMarshallingSpec.scala    From akka-grpc   with Apache License 2.0 5 votes vote down vote up
package akka.grpc.scaladsl

import akka.actor.ActorSystem
import akka.grpc.internal.{ AbstractGrpcProtocol, GrpcProtocolNative, Gzip }
import akka.grpc.scaladsl.headers.`Message-Encoding`
import akka.http.scaladsl.model.{ HttpEntity, HttpRequest }
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Sink
import io.grpc.{ Status, StatusException }
import io.grpc.testing.integration.messages.{ BoolValue, SimpleRequest }
import io.grpc.testing.integration.test.TestService
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

import scala.collection.immutable
import scala.concurrent.{ Await, Future }
import scala.concurrent.duration._

class GrpcMarshallingSpec extends AnyWordSpec with Matchers {
  "The scaladsl GrpcMarshalling" should {
    val message = SimpleRequest(responseCompressed = Some(BoolValue(true)))
    implicit val serializer = TestService.Serializers.SimpleRequestSerializer
    implicit val system = ActorSystem()
    implicit val mat = ActorMaterializer()
    val awaitTimeout = 10.seconds
    val zippedBytes =
      AbstractGrpcProtocol.encodeFrameData(
        AbstractGrpcProtocol.fieldType(Gzip),
        Gzip.compress(serializer.serialize(message)))

    "correctly unmarshal a zipped object" in {
      val request = HttpRequest(
        headers = immutable.Seq(`Message-Encoding`("gzip")),
        entity = HttpEntity.Strict(GrpcProtocolNative.contentType, zippedBytes))

      val marshalled = Await.result(GrpcMarshalling.unmarshal(request), 10.seconds)
      marshalled.responseCompressed should be(Some(BoolValue(true)))
    }

    "correctly unmarshal a zipped stream" in {
      val request = HttpRequest(
        headers = immutable.Seq(`Message-Encoding`("gzip")),
        entity = HttpEntity.Strict(GrpcProtocolNative.contentType, zippedBytes ++ zippedBytes))

      val stream = Await.result(GrpcMarshalling.unmarshalStream(request), 10.seconds)
      val items = Await.result(stream.runWith(Sink.seq), 10.seconds)
      items(0).responseCompressed should be(Some(BoolValue(true)))
      items(1).responseCompressed should be(Some(BoolValue(true)))
    }

    // https://github.com/grpc/grpc/blob/master/doc/compression.md#compression-method-asymmetry-between-peers
    // test case 6
    "fail with INTERNAL when the compressed bit is on but the encoding is identity" in {
      val request = HttpRequest(
        headers = immutable.Seq(`Message-Encoding`("identity")),
        entity = HttpEntity.Strict(GrpcProtocolNative.contentType, zippedBytes))

      assertFailure(GrpcMarshalling.unmarshal(request), Status.Code.INTERNAL, "encoding")
    }

    // https://github.com/grpc/grpc/blob/master/doc/compression.md#compression-method-asymmetry-between-peers
    // test case 6
    "fail with INTERNAL when the compressed bit is on but the encoding is missing" in {
      val request = HttpRequest(entity = HttpEntity.Strict(GrpcProtocolNative.contentType, zippedBytes))

      assertFailure(GrpcMarshalling.unmarshal(request), Status.Code.INTERNAL, "encoding")
    }

    def assertFailure(failure: Future[_], expectedStatusCode: Status.Code, expectedMessageFragment: String): Unit = {
      val e = Await.result(failure.failed, awaitTimeout).asInstanceOf[StatusException]
      e.getStatus.getCode should be(expectedStatusCode)
      e.getStatus.getDescription should include(expectedMessageFragment)
    }
  }
} 
Example 23
Source File: GRPCErrors.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.api.grpc

import com.wavesplatform.api.http.ApiError
import com.wavesplatform.api.http.ApiError._
import io.grpc.Metadata.AsciiMarshaller
import io.grpc.{Metadata, Status, StatusException}

object GRPCErrors {
  private[this] val IntMarshaller: AsciiMarshaller[Int] = new AsciiMarshaller[Int] {
    override def toAsciiString(value: Int): String         = value.toString
    override def parseAsciiString(serialized: String): Int = serialized.toInt
  }

  val ErrorCodeKey = Metadata.Key.of("Error-Code", IntMarshaller)

  def toStatusException(api: ApiError): StatusException = {
    val code = api match {
      case WalletNotExist | WalletAddressDoesNotExist | TransactionDoesNotExist | AliasDoesNotExist(_) | BlockDoesNotExist | MissingSenderPrivateKey |
          DataKeyDoesNotExist =>
        Status.NOT_FOUND
      case WalletAlreadyExists => Status.ALREADY_EXISTS
      case WalletLocked        => Status.PERMISSION_DENIED
      case _                   => Status.INVALID_ARGUMENT
    }

    val metadata = new Metadata()
    metadata.put(ErrorCodeKey, api.id)
    code.withDescription(api.message).asException(metadata)
  }

  def toStatusException(exc: Throwable): StatusException = {
    val status = exc match {
      case _: IllegalArgumentException => Status.INVALID_ARGUMENT
      case _: NoSuchElementException => Status.NOT_FOUND
      case _: IllegalStateException => Status.FAILED_PRECONDITION
      case _ => Status.fromThrowable(exc)
    }
    new StatusException(status.withCause(exc).withDescription(exc.getMessage))
  }
} 
Example 24
Source File: GrpcSerializers.scala    From scio   with Apache License 2.0 5 votes vote down vote up
package com.spotify.scio.coders.instances.kryo

import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.io.{Input, Output}
import com.twitter.chill.KSerializer
import io.grpc.{Metadata, Status, StatusRuntimeException}

private[coders] object GrpcSerializers {

  class StatusSerializer extends KSerializer[Status] {
    override def write(kryo: Kryo, output: Output, status: Status): Unit = {
      output.writeInt(status.getCode().value())
      output.writeString(status.getDescription)
      kryo.writeClassAndObject(output, status.getCause)
    }

    override def read(kryo: Kryo, input: Input, `type`: Class[Status]): Status = {
      val code = input.readInt()
      val description = input.readString()
      val cause = kryo.readClassAndObject(input).asInstanceOf[Throwable]

      Status
        .fromCodeValue(code)
        .withDescription(description)
        .withCause(cause)
    }
  }

  class StatusRuntimeExceptionSerializer extends KSerializer[StatusRuntimeException] {
    lazy val statusSer = new StatusSerializer()

    override def write(kryo: Kryo, output: Output, e: StatusRuntimeException): Unit = {
      kryo.writeObject(output, e.getStatus, statusSer)
      kryo.writeObjectOrNull(output, e.getTrailers, classOf[Metadata])
    }

    override def read(
      kryo: Kryo,
      input: Input,
      `type`: Class[StatusRuntimeException]
    ): StatusRuntimeException = {
      val status = kryo.readObject(input, classOf[Status], statusSer)
      val trailers = kryo.readObjectOrNull(input, classOf[Metadata])

      new StatusRuntimeException(status, trailers)
    }
  }
} 
Example 25
Source File: GrpcSerializersTest.scala    From scio   with Apache License 2.0 5 votes vote down vote up
package com.spotify.scio.coders.instances.kryo

import com.spotify.scio.coders.{Coder, CoderMaterializer}
import io.grpc.{Metadata, Status, StatusRuntimeException}
import org.apache.beam.sdk.util.CoderUtils
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.jdk.CollectionConverters._

class GrpcSerializersTest extends AnyFlatSpec with Matchers {

  "StatusRuntimeException" should "roundtrip with nullable fields present" in {
    val metadata = new Metadata()
    metadata.put(Metadata.Key.of[String]("k", Metadata.ASCII_STRING_MARSHALLER), "v")

    roundtrip(
      new StatusRuntimeException(
        Status.OK.withCause(new RuntimeException("bar")).withDescription("bar"),
        metadata
      )
    )
  }

  it should "roundtrip with nullable fields absent" in {
    roundtrip(new StatusRuntimeException(Status.OK))
  }

  private def roundtrip(t: StatusRuntimeException): Unit = {
    val kryoBCoder = CoderMaterializer.beamWithDefault(Coder[StatusRuntimeException])

    val bytes = CoderUtils.encodeToByteArray(kryoBCoder, t)
    val copy = CoderUtils.decodeFromByteArray(kryoBCoder, bytes)

    checkStatusEq(t.getStatus, copy.getStatus)
    checkTrailersEq(t.getTrailers, copy.getTrailers)
  }

  private def checkTrailersEq(metadata1: Metadata, metadata2: Metadata): Unit =
    (Option(metadata1), Option(metadata2)) match {
      case (Some(m1), Some(m2)) =>
        m1.keys.size shouldEqual m2.keys.size
        m1.keys.asScala.foreach { k =>
          m1.get(Metadata.Key.of[String](k, Metadata.ASCII_STRING_MARSHALLER)) shouldEqual
            m2.get(Metadata.Key.of[String](k, Metadata.ASCII_STRING_MARSHALLER))
        }
      case (None, None) =>
      case _            => fail(s"Metadata were unequal: ($metadata1, $metadata2)")
    }

  private def checkStatusEq(s1: Status, s2: Status): Unit = {
    s1.getCode shouldEqual s2.getCode
    s1.getDescription shouldEqual s2.getDescription
    if (s1.getCause != null) {
      s1.getCause.getClass shouldEqual s2.getCause.getClass
      s1.getCause.getMessage shouldEqual s2.getCause.getMessage
    } else if (s2.getCause != null) {
      fail(s"Status $s1 is missing a cause")
    }
  }
} 
Example 26
Source File: LoggingServerInterceptor.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.grpc.server.interceptor

import io.grpc.ForwardingServerCall.SimpleForwardingServerCall
import io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener
import io.grpc._
import org.slf4j.Logger


class LoggingServerInterceptor(logger: Logger) extends ServerInterceptor {

  override def interceptCall[ReqT, RespT](
      call: ServerCall[ReqT, RespT],
      headers: Metadata,
      next: ServerCallHandler[ReqT, RespT]
  ): ServerCall.Listener[ReqT] = {
    val methodName = call.getMethodDescriptor.getFullMethodName
    val finalCall = new CloseServerCall(methodName, call)
    new OnMessageServerCallListener(methodName, next.startCall(finalCall, headers))
  }

  private class CloseServerCall[A, B](methodName: String, delegate: ServerCall[A, B]) extends SimpleForwardingServerCall[A, B](delegate) {
    override def close(status: Status, trailers: Metadata): Unit = {
      import io.grpc.Status
      if ((status.getCode eq Status.Code.UNKNOWN) || (status.getCode eq Status.Code.INTERNAL)) {
        logger.error(
          String.format(
            "Error response from method %s: %s %s",
            methodName,
            status.getCode,
            status.getDescription
          ),
          status.getCause
        )
      } else if (!status.isOk) {
        logger.warn(
          String.format(
            "Error response from method %s: %s %s",
            methodName,
            status.getCode,
            status.getDescription
          ),
          status.getCause
        )
      } else {
        logger.debug("Successful response from method {}: {}", Array(methodName, status): _*)
      }
      super.close(status, trailers)
    }
  }

  private class OnMessageServerCallListener[A](methodName: String, delegate: ServerCall.Listener[A])
      extends SimpleForwardingServerCallListener[A](delegate) {
    override def onMessage(message: A): Unit = {
      logger.debug("Dispatching method {}", methodName)
      super.onMessage(message)
    }
  }

} 
Example 27
Source File: ContextualizedLogger.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.logging

import akka.NotUsed
import akka.stream.scaladsl.Flow
import com.daml.grpc.GrpcException
import io.grpc.Status
import org.slf4j.{Logger, LoggerFactory}

import scala.collection.concurrent.TrieMap
import scala.util.{Failure, Try}
import scala.util.control.NonFatal

object ContextualizedLogger {

  // Caches loggers to prevent them from needlessly wasting memory
  // Replicates the behavior of the underlying Slf4j logger factory
  private[this] val cache = TrieMap.empty[String, ContextualizedLogger]

  // Allows to explicitly pass a logger, should be used for testing only
  private[logging] def createFor(withoutContext: Logger): ContextualizedLogger =
    new ContextualizedLogger(withoutContext)

  // Slf4j handles the caching of the underlying logger itself
  private[logging] def createFor(name: String): ContextualizedLogger =
    createFor(LoggerFactory.getLogger(name))

  
  def get(clazz: Class[_]): ContextualizedLogger = {
    val name = clazz.getName.stripSuffix("$")
    cache.getOrElseUpdate(name, createFor(name))
  }

}

final class ContextualizedLogger private (val withoutContext: Logger) {

  val trace = new LeveledLogger.Trace(withoutContext)
  val debug = new LeveledLogger.Debug(withoutContext)
  val info = new LeveledLogger.Info(withoutContext)
  val warn = new LeveledLogger.Warn(withoutContext)
  val error = new LeveledLogger.Error(withoutContext)

  private def internalOrUnknown(code: Status.Code): Boolean =
    code == Status.Code.INTERNAL || code == Status.Code.UNKNOWN

  private def logError(t: Throwable)(implicit logCtx: LoggingContext): Unit =
    error("Unhandled internal error", t)

  def logErrorsOnCall[Out](implicit logCtx: LoggingContext): PartialFunction[Try[Out], Unit] = {
    case Failure(e @ GrpcException(s, _)) =>
      if (internalOrUnknown(s.getCode)) {
        logError(e)
      }
    case Failure(NonFatal(e)) =>
      logError(e)
  }

  def logErrorsOnStream[Out](implicit logCtx: LoggingContext): Flow[Out, Out, NotUsed] =
    Flow[Out].mapError {
      case e @ GrpcException(s, _) =>
        if (internalOrUnknown(s.getCode)) {
          logError(e)
        }
        e
      case NonFatal(e) =>
        logError(e)
        e
    }

} 
Example 28
Source File: ApiPackageService.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.apiserver.services

import com.daml.ledger.participant.state.index.v2.IndexPackagesService
import com.daml.lf.data.Ref
import com.daml.daml_lf_dev.DamlLf.{Archive, HashFunction}
import com.daml.dec.{DirectExecutionContext => DEC}
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.package_service.HashFunction.{
  SHA256 => APISHA256,
  Unrecognized => APIUnrecognized
}
import com.daml.ledger.api.v1.package_service.PackageServiceGrpc.PackageService
import com.daml.ledger.api.v1.package_service.{HashFunction => APIHashFunction, _}
import com.daml.logging.{ContextualizedLogger, LoggingContext}
import com.daml.platform.api.grpc.GrpcApiService
import com.daml.platform.server.api.validation.PackageServiceValidation
import io.grpc.{BindableService, ServerServiceDefinition, Status}

import scala.concurrent.Future

final class ApiPackageService private (backend: IndexPackagesService)(
    implicit logCtx: LoggingContext)
    extends PackageService
    with GrpcApiService {

  private val logger = ContextualizedLogger.get(this.getClass)

  override def bindService(): ServerServiceDefinition =
    PackageServiceGrpc.bindService(this, DEC)

  override def close(): Unit = ()

  override def listPackages(request: ListPackagesRequest): Future[ListPackagesResponse] =
    backend
      .listLfPackages()
      .map(p => ListPackagesResponse(p.keys.toSeq))(DEC)
      .andThen(logger.logErrorsOnCall[ListPackagesResponse])(DEC)

  override def getPackage(request: GetPackageRequest): Future[GetPackageResponse] =
    withValidatedPackageId(
      request.packageId,
      pId =>
        backend
          .getLfArchive(pId)
          .flatMap(_.fold(Future.failed[GetPackageResponse](Status.NOT_FOUND.asRuntimeException()))(
            archive => Future.successful(toGetPackageResponse(archive))))(DEC)
          .andThen(logger.logErrorsOnCall[GetPackageResponse])(DEC)
    )

  override def getPackageStatus(
      request: GetPackageStatusRequest): Future[GetPackageStatusResponse] =
    withValidatedPackageId(
      request.packageId,
      pId =>
        backend
          .listLfPackages()
          .map { packages =>
            val result = if (packages.contains(pId)) {
              PackageStatus.REGISTERED
            } else {
              PackageStatus.UNKNOWN
            }
            GetPackageStatusResponse(result)
          }(DEC)
          .andThen(logger.logErrorsOnCall[GetPackageStatusResponse])(DEC)
    )

  private def withValidatedPackageId[T](packageId: String, block: Ref.PackageId => Future[T]) =
    Ref.PackageId
      .fromString(packageId)
      .fold(
        error =>
          Future.failed[T](
            Status.INVALID_ARGUMENT
              .withDescription(error)
              .asRuntimeException()),
        pId => block(pId)
      )

  private def toGetPackageResponse(archive: Archive): GetPackageResponse = {
    val hashF: APIHashFunction = archive.getHashFunction match {
      case HashFunction.SHA256 => APISHA256
      case _ => APIUnrecognized(-1)
    }
    GetPackageResponse(hashF, archive.getPayload, archive.getHash)
  }

}

object ApiPackageService {
  def create(ledgerId: LedgerId, backend: IndexPackagesService)(
      implicit logCtx: LoggingContext): PackageService with GrpcApiService =
    new PackageServiceValidation(new ApiPackageService(backend), ledgerId) with BindableService {
      override def bindService(): ServerServiceDefinition =
        PackageServiceGrpc.bindService(this, DEC)
    }
} 
Example 29
Source File: TrackerImplTest.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.apiserver.services.tracking

import akka.NotUsed
import akka.stream.OverflowStrategy
import akka.stream.scaladsl.{Keep, Source, SourceQueueWithComplete}
import akka.stream.testkit.TestSubscriber
import akka.stream.testkit.scaladsl.TestSink
import com.daml.ledger.api.testing.utils.{
  AkkaBeforeAndAfterAll,
  IsStatusException,
  TestingException
}
import com.daml.ledger.api.v1.command_service.SubmitAndWaitRequest
import com.daml.ledger.api.v1.commands.Commands
import com.daml.ledger.api.v1.completion.Completion
import com.daml.dec.DirectExecutionContext
import com.google.rpc.status.{Status => RpcStatus}
import io.grpc.Status
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.{BeforeAndAfterEach, Matchers, Succeeded, WordSpec}

import scala.concurrent.ExecutionContext.Implicits.global

class TrackerImplTest
    extends WordSpec
    with Matchers
    with BeforeAndAfterEach
    with ScalaFutures
    with AkkaBeforeAndAfterAll {

  private var sut: Tracker = _
  private var consumer: TestSubscriber.Probe[NotUsed] = _
  private var queue: SourceQueueWithComplete[TrackerImpl.QueueInput] = _

  private def input(cid: Int) = SubmitAndWaitRequest(Some(Commands(commandId = cid.toString)))

  override protected def beforeEach(): Unit = {
    val (q, sink) = Source
      .queue[TrackerImpl.QueueInput](1, OverflowStrategy.dropNew)
      .map { in =>
        in.context.success(Completion(in.value.getCommands.commandId, Some(RpcStatus())))
        NotUsed
      }
      .toMat(TestSink.probe[NotUsed])(Keep.both)
      .run()
    queue = q
    sut = new TrackerImpl(q)
    consumer = sink
  }

  override protected def afterEach(): Unit = {
    consumer.cancel()
    queue.complete()
  }

  "Tracker Implementation" when {

    "input is submitted, and the queue is available" should {

      "work successfully" in {

        val resultF1 = sut.track(input(1))
        consumer.requestNext()
        val resultF = resultF1.flatMap(_ => sut.track(input(2)))(DirectExecutionContext)
        consumer.requestNext()
        whenReady(resultF)(_ => Succeeded)
      }
    }

    "input is submitted, and the queue is backpressuring" should {

      "return a RESOURCE_EXHAUSTED error" in {

        sut.track(input(1))
        whenReady(sut.track(input(2)).failed)(IsStatusException(Status.RESOURCE_EXHAUSTED))
      }
    }

    "input is submitted, and the queue has been completed" should {

      "return an ABORTED error" in {

        queue.complete()
        whenReady(sut.track(input(2)).failed)(IsStatusException(Status.ABORTED))
      }
    }

    "input is submitted, and the queue has failed" should {

      "return an ABORTED error" in {

        queue.fail(TestingException("The queue fails with this error."))
        whenReady(sut.track(input(2)).failed)(IsStatusException(Status.ABORTED))
      }
    }
  }
} 
Example 30
Source File: ExpiringStreamServiceCallAuthTests.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.sandbox.auth

import java.time.Duration

import com.daml.grpc.{GrpcException, GrpcStatus}
import com.daml.platform.sandbox.services.SubmitAndWaitDummyCommand
import com.daml.platform.testing.StreamConsumer
import com.daml.timer.Delayed
import io.grpc.Status
import io.grpc.stub.StreamObserver

import scala.concurrent.duration.DurationInt
import scala.concurrent.{Future, Promise}
import scala.util.control.NonFatal

trait ExpiringStreamServiceCallAuthTests[T]
    extends ReadOnlyServiceCallAuthTests
    with SubmitAndWaitDummyCommand {

  protected def stream: Option[String] => StreamObserver[T] => Unit

  private def expectExpiration(token: String): Future[Unit] = {
    val promise = Promise[Unit]()
    stream(Option(token))(new StreamObserver[T] {
      @volatile private[this] var gotSomething = false
      def onNext(value: T): Unit = {
        gotSomething = true
      }
      def onError(t: Throwable): Unit = {
        t match {
          case GrpcException(GrpcStatus(Status.Code.PERMISSION_DENIED, _), _) if gotSomething =>
            val _ = promise.trySuccess(())
          case NonFatal(e) =>
            val _ = promise.tryFailure(e)
        }
      }
      def onCompleted(): Unit = {
        val _ = promise.tryFailure(new RuntimeException("stream completed before token expiration"))
      }
    })
    promise.future
  }

  private def canActAsMainActorExpiresInFiveSeconds =
    toHeader(expiringIn(Duration.ofSeconds(5), readWriteToken(mainActor)))

  private def canReadAsMainActorExpiresInFiveSeconds =
    toHeader(expiringIn(Duration.ofSeconds(5), readOnlyToken(mainActor)))

  it should "break a stream in flight upon read-only token expiration" in {
    val _ = Delayed.Future.by(10.seconds)(submitAndWait())
    expectExpiration(canReadAsMainActorExpiresInFiveSeconds).map(_ => succeed)
  }

  it should "break a stream in flight upon read/write token expiration" in {
    val _ = Delayed.Future.by(10.seconds)(submitAndWait())
    expectExpiration(canActAsMainActorExpiresInFiveSeconds).map(_ => succeed)
  }

  override def serviceCallWithToken(token: Option[String]): Future[Any] =
    submitAndWait().flatMap(_ => new StreamConsumer[T](stream(token)).first())

} 
Example 31
Source File: GrpcHealthService.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.server.api.services.grpc

import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.dec.DirectExecutionContext
import com.daml.ledger.api.health.HealthChecks
import com.daml.platform.api.grpc.GrpcApiService
import com.daml.platform.server.api.DropRepeated
import com.daml.platform.server.api.services.grpc.GrpcHealthService._
import io.grpc.health.v1.health.{
  HealthAkkaGrpc,
  HealthCheckRequest,
  HealthCheckResponse,
  HealthGrpc
}
import io.grpc.{ServerServiceDefinition, Status, StatusException}

import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}

class GrpcHealthService(
    healthChecks: HealthChecks,
    maximumWatchFrequency: FiniteDuration = 1.second,
)(
    implicit protected val esf: ExecutionSequencerFactory,
    protected val mat: Materializer,
    executionContext: ExecutionContext,
) extends HealthAkkaGrpc
    with GrpcApiService {
  override def bindService(): ServerServiceDefinition =
    HealthGrpc.bindService(this, DirectExecutionContext)

  override def check(request: HealthCheckRequest): Future[HealthCheckResponse] =
    Future.fromTry(matchResponse(serviceFrom(request)))

  override def watchSource(request: HealthCheckRequest): Source[HealthCheckResponse, NotUsed] =
    Source
      .fromIterator(() => Iterator.continually(matchResponse(serviceFrom(request)).get))
      .throttle(1, per = maximumWatchFrequency)
      .via(DropRepeated())

  private def matchResponse(componentName: Option[String]): Try[HealthCheckResponse] =
    if (!componentName.forall(healthChecks.hasComponent))
      Failure(new StatusException(Status.NOT_FOUND))
    else if (healthChecks.isHealthy(componentName))
      Success(servingResponse)
    else
      Success(notServingResponse)
}

object GrpcHealthService {
  private[grpc] val servingResponse =
    HealthCheckResponse(HealthCheckResponse.ServingStatus.SERVING)

  private[grpc] val notServingResponse =
    HealthCheckResponse(HealthCheckResponse.ServingStatus.NOT_SERVING)

  private def serviceFrom(request: HealthCheckRequest): Option[String] = {
    Option(request.service).filter(_.nonEmpty)
  }
} 
Example 32
Source File: Assertions.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.api.testtool.infrastructure

import ai.x.diff.DiffShow
import com.daml.grpc.{GrpcException, GrpcStatus}
import java.util.regex.Pattern
import io.grpc.Status

import scala.language.higherKinds
import scala.util.control.NonFatal

object Assertions extends DiffExtensions {
  def fail(message: String): Nothing =
    throw new AssertionError(message)

  def fail(message: String, cause: Throwable): Nothing =
    throw new AssertionError(message, cause)

  def assertLength[A, F[_] <: Seq[_]](context: String, length: Int, as: F[A]): F[A] = {
    assert(as.length == length, s"$context: expected $length item(s), got ${as.length}")
    as
  }

  def assertSingleton[A](context: String, as: Seq[A]): A =
    assertLength(context, 1, as).head

  def assertEquals[T: DiffShow](context: String, actual: T, expected: T): Unit = {
    val diff = DiffShow.diff(actual, expected)
    if (!diff.isIdentical)
      throw AssertionErrorWithPreformattedMessage(
        diff.string,
        s"$context: two objects are supposed to be equal but they are not",
      )
  }

  
  def assertGrpcError(t: Throwable, expectedCode: Status.Code, pattern: String): Unit = {
    assertGrpcError(
      t,
      expectedCode,
      if (pattern.isEmpty) None else Some(Pattern.compile(Pattern.quote(pattern))))
  }
} 
Example 33
Source File: LedgerConfigurationServiceIT.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.api.testtool.tests

import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.Assertions._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import com.daml.ledger.test.model.Test.Dummy
import io.grpc.Status

class LedgerConfigurationServiceIT extends LedgerTestSuite {
  test("ConfigSucceeds", "Return a valid configuration for a valid request", allocate(NoParties))(
    implicit ec => {
      case Participants(Participant(ledger)) =>
        for {
          config <- ledger.configuration()
        } yield {
          assert(
            config.maxDeduplicationTime.isDefined,
            "The maxDeduplicationTime field of the configuration is empty")
        }
    })

  test("ConfigLedgerId", "Return NOT_FOUND to invalid ledger identifier", allocate(NoParties))(
    implicit ec => {
      case Participants(Participant(ledger)) =>
        val invalidLedgerId = "THIS_IS_AN_INVALID_LEDGER_ID"
        for {
          failure <- ledger.configuration(overrideLedgerId = Some(invalidLedgerId)).failed
        } yield {
          assertGrpcError(
            failure,
            Status.Code.NOT_FOUND,
            s"Ledger ID '$invalidLedgerId' not found.")
        }
    })

  test(
    "CSLSuccessIfMaxDedplicationTimeRight",
    "Submission returns OK if deduplication time is within the accepted interval",
    allocate(SingleParty),
  )(implicit ec => {
    case Participants(Participant(ledger, party)) =>
      // Submission using the maximum allowed deduplication time
      val request = ledger.submitRequest(party, Dummy(party).create.command)
      for {
        config <- ledger.configuration()
        maxDedupTime = config.maxDeduplicationTime.get
        _ <- ledger.submit(request.update(_.commands.deduplicationTime := maxDedupTime))
      } yield {
        // No assertions to make, since the command went through as expected
      }
  })

  test(
    "CSLSuccessIfMaxDeduplicationTimeExceeded",
    "Submission returns OK if deduplication time is too high",
    allocate(SingleParty),
  )(implicit ec => {
    case Participants(Participant(ledger, party)) =>
      val request = ledger.submitRequest(party, Dummy(party).create.command)
      for {
        config <- ledger.configuration()
        maxDedupTime = config.maxDeduplicationTime.get
        failure <- ledger
          .submit(
            request.update(_.commands.deduplicationTime := maxDedupTime.update(
              _.seconds := maxDedupTime.seconds + 1)))
          .failed
      } yield {
        assertGrpcError(failure, Status.Code.INVALID_ARGUMENT, "")
      }
  })
} 
Example 34
Source File: PackageManagementServiceIT.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.api.testtool.tests

import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.Assertions._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import com.daml.ledger.packagemanagementtest.PackageManagementTest.PackageManagementTestTemplate
import com.daml.ledger.packagemanagementtest.PackageManagementTest.PackageManagementTestTemplate._
import com.google.protobuf.ByteString
import io.grpc.Status

import scala.concurrent.{ExecutionContext, Future}

final class PackageManagementServiceIT extends LedgerTestSuite {
  private[this] val testPackageResourcePath =
    "/ledger/ledger-api-test-tool/PackageManagementTest.dar"

  private def loadTestPackage()(implicit ec: ExecutionContext): Future[ByteString] = {
    val testPackage = Future {
      val in = getClass.getResourceAsStream(testPackageResourcePath)
      assert(in != null, s"Unable to load test package resource at '$testPackageResourcePath'")
      in
    }
    val bytes = testPackage.map(ByteString.readFrom)
    bytes.onComplete(_ => testPackage.map(_.close()))
    bytes
  }

  test(
    "PackageManagementEmptyUpload",
    "An attempt at uploading an empty payload should fail",
    allocate(NoParties),
  )(implicit ec => {
    case Participants(Participant(ledger)) =>
      for {
        failure <- ledger.uploadDarFile(ByteString.EMPTY).failed
      } yield {
        assertGrpcError(
          failure,
          Status.Code.INVALID_ARGUMENT,
          "Invalid argument: Invalid DAR: package-upload",
        )
      }
  })

  test(
    "PackageManagementLoad",
    "Concurrent uploads of the same package should be idempotent and result in the package being available for use",
    allocate(SingleParty),
  )(implicit ec => {
    case Participants(Participant(ledger, party)) =>
      for {
        testPackage <- loadTestPackage()
        _ <- Future.sequence(Vector.fill(8)(ledger.uploadDarFile(testPackage)))
        knownPackages <- ledger.listKnownPackages()
        contract <- ledger.create(party, new PackageManagementTestTemplate(party))
        acsBefore <- ledger.activeContracts(party)
        _ <- ledger.exercise(party, contract.exerciseTestChoice)
        acsAfter <- ledger.activeContracts(party)
      } yield {
        val duplicatePackageIds =
          knownPackages.groupBy(_.packageId).mapValues(_.size).filter(_._2 > 1)
        assert(
          duplicatePackageIds.isEmpty,
          s"There are duplicate package identifiers: ${duplicatePackageIds map {
            case (name, count) => s"$name ($count)"
          } mkString (", ")}",
        )
        assert(
          acsBefore.size == 1,
          "After the contract has been created there should be one active contract but there's none",
        )
        assert(
          acsAfter.isEmpty,
          s"There should be no active package after the contract has been consumed: ${acsAfter.map(_.contractId).mkString(", ")}",
        )
      }
  })
} 
Example 35
Source File: PackageServiceIT.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.api.testtool.tests

import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.Assertions._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import io.grpc.Status

final class PackageServiceIT extends LedgerTestSuite {

  
  private[this] val unknownPackageId = " "

  test("PackagesList", "Listing packages should return a result", allocate(NoParties))(implicit ec => {
    case Participants(Participant(ledger)) =>
      for {
        knownPackages <- ledger.listPackages()
      } yield
        assert(
          knownPackages.size >= 3,
          s"List of packages was expected to contain at least 3 packages, got ${knownPackages.size} instead.",
        )
  })

  test("PackagesGet", "Getting package content should return a valid result", allocate(NoParties))(
    implicit ec => {
      case Participants(Participant(ledger)) =>
        for {
          somePackageId <- ledger
            .listPackages()
            .map(_.headOption.getOrElse(fail("No package found")))
          somePackage <- ledger.getPackage(somePackageId)
        } yield {
          assert(somePackage.hash.length > 0, s"Package $somePackageId has an empty hash.")
          assert(
            somePackage.hash == somePackageId,
            s"Package $somePackageId has hash ${somePackage.hash}, expected hash to be equal to the package ID.",
          )
          assert(somePackage.archivePayload.size() >= 0, s"Package $somePackageId has zero size.")
        }
    })

  test(
    "PackagesGetUnknown",
    "Getting package content for an unknown package should fail",
    allocate(NoParties),
  )(implicit ec => {
    case Participants(Participant(ledger)) =>
      for {
        failure <- ledger.getPackage(unknownPackageId).failed
      } yield {
        assertGrpcError(failure, Status.Code.NOT_FOUND, "")
      }
  })

  test(
    "PackagesStatus",
    "Getting package status should return a valid result",
    allocate(NoParties))(implicit ec => {
    case Participants(Participant(ledger)) =>
      for {
        somePackageId <- ledger.listPackages().map(_.headOption.getOrElse(fail("No package found")))
        status <- ledger.getPackageStatus(somePackageId)
      } yield {
        assert(status.isRegistered, s"Package $somePackageId is not registered.")
      }
  })

  test(
    "PackagesStatusUnknown",
    "Getting package status for an unknown package should fail",
    allocate(NoParties),
  )(implicit ec => {
    case Participants(Participant(ledger)) =>
      for {
        status <- ledger.getPackageStatus(unknownPackageId)
      } yield {
        assert(status.isUnknown, s"Package $unknownPackageId is not unknown.")
      }
  })
} 
Example 36
Source File: ClosedWorldIT.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.api.testtool.tests

import com.daml.ledger.api.testtool.infrastructure.Allocation.{
  Participant,
  Participants,
  SingleParty,
  allocate
}
import com.daml.ledger.api.testtool.infrastructure.Assertions.assertGrpcError
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import com.daml.ledger.client.binding
import com.daml.ledger.test.semantic.SemanticTests.{Amount, Iou}
import io.grpc.Status

class ClosedWorldIT extends LedgerTestSuite {

  private[this] val onePound = Amount(BigDecimal(1), "GBP")

  

  test(
    "ClosedWorldObserver",
    "Cannot execute a transaction that references unallocated observer parties",
    allocate(SingleParty),
  )(implicit ec => {
    case Participants(Participant(alpha, payer)) =>
      for {
        failure <- alpha
          .create(payer, Iou(payer, binding.Primitive.Party("unallocated"), onePound))
          .failed
      } yield {
        assertGrpcError(failure, Status.Code.INVALID_ARGUMENT, "Party not known on ledger")
      }
  })
} 
Example 37
Source File: IsStatusException.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.api.testing.utils

import com.daml.grpc.{GrpcException, GrpcStatus}
import io.grpc.Status
import org.scalatest.{Assertion, Matchers}

import scala.util.control.NonFatal

object IsStatusException extends Matchers {

  def apply(expectedStatusCode: Status.Code)(throwable: Throwable): Assertion = {
    throwable match {
      case GrpcException(GrpcStatus(code, _), _) => code shouldEqual expectedStatusCode
      case NonFatal(other) => fail(s"$other is not a gRPC Status exception.")
    }
  }

  def apply(expectedStatus: Status): Throwable => Assertion = {
    apply(expectedStatus.getCode)
  }
} 
Example 38
Source File: ReferenceImplementation.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.grpc.sampleservice.implementations

import com.daml.grpc.sampleservice.Responding
import com.daml.platform.hello.HelloServiceGrpc.HelloService
import com.daml.platform.hello.{HelloRequest, HelloResponse, HelloServiceGrpc}
import io.grpc.stub.StreamObserver
import io.grpc.{BindableService, ServerServiceDefinition, Status}

import scala.concurrent.ExecutionContext

class ReferenceImplementation
    extends HelloService
    with Responding
    with BindableService
    with AutoCloseable {

  override def close(): Unit = ()

  override def bindService(): ServerServiceDefinition =
    HelloServiceGrpc.bindService(this, ExecutionContext.global)

  override def serverStreaming(
      request: HelloRequest,
      responseObserver: StreamObserver[HelloResponse],
  ): Unit = {
    validateRequest(request)
    for (i <- 1.to(request.reqInt)) responseObserver.onNext(HelloResponse(i))
    responseObserver.onCompleted()
  }

  private def validateRequest(request: HelloRequest): Unit =
    if (request.reqInt < 0)
      throw Status.INVALID_ARGUMENT
        .withDescription("request cannot be negative")
        .asRuntimeException()

} 
Example 39
Source File: ResultAssertions.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.grpc.adapter.client

import com.daml.platform.hello.HelloResponse
import com.google.protobuf.ByteString
import io.grpc.{Status, StatusRuntimeException}
import org.scalatest.{Assertion, Matchers}

import scala.util.Random

trait ResultAssertions { self: Matchers =>

  protected def elemCount: Int = 1024
  protected lazy val elemRange: Range = 1.to(elemCount)
  protected lazy val halfCount: Int = elemCount / 2
  protected lazy val halfRange: Range = elemRange.take(halfCount)

  protected def isCancelledException(err: Throwable): Assertion = {
    err shouldBe a[StatusRuntimeException]
    err.asInstanceOf[StatusRuntimeException].getStatus.getCode shouldEqual Status.CANCELLED.getCode
  }

  protected def assertElementsAreInOrder(expectedCount: Long)(
      results: Seq[HelloResponse]
  ): Assertion = {
    results should have length expectedCount
    results.map(_.respInt) shouldEqual (1 to expectedCount.toInt)
  }

  protected def elementsAreSummed(results: Seq[HelloResponse]): Assertion = {
    results should have length 1
    results.foldLeft(0)(_ + _.respInt) shouldEqual elemRange.sum
  }

  protected def everyElementIsDoubled(results: Seq[HelloResponse]): Assertion = {
    results should have length elemCount.toLong
    //the order does matter
    results.map(_.respInt) shouldEqual elemRange.map(_ * 2)
  }

  protected def genPayload(): ByteString = {
    val bytes = new Array[Byte](1024)
    Random.nextBytes(bytes)
    ByteString.copyFrom(bytes)
  }
} 
Example 40
Source File: PlaySpecs2Spec.scala    From play-grpc   with Apache License 2.0 5 votes vote down vote up
package play.grpc.specs2

import org.junit.runner.RunWith
import org.specs2.runner.JUnitRunner

import play.api.inject.bind
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.libs.ws.WSClient
import play.api.libs.ws.WSRequest
import play.api.routing.Router
import play.api.test._

import akka.grpc.internal.GrpcProtocolNative

import example.myapp.helloworld.grpc.helloworld._

import io.grpc.Status


@RunWith(classOf[JUnitRunner])
class PlaySpecs2Spec extends ForServer with ServerGrpcClient with PlaySpecification with ApplicationFactories {

  protected def applicationFactory: ApplicationFactory =
    withGuiceApp(GuiceApplicationBuilder().overrides(bind[Router].to[GreeterServiceImpl]))

  // RICH: Still need to work out how to make WSClient work properly with endpoints
  def wsUrl(path: String)(implicit running: RunningServer): WSRequest = {
    val ws  = running.app.injector.instanceOf[WSClient]
    val url = running.endpoints.httpEndpoint.get.pathUrl(path)
    ws.url(url)
  }

  "A Play server bound to a gRPC router" should {
    "give a 404 when routing a non-gRPC request" >> { implicit rs: RunningServer =>
      val result = await(wsUrl("/").get)
      result.status must ===(404) // Maybe should be a 426, see #396
    }
    "give a 415 error when not using a gRPC content-type" >> { implicit rs: RunningServer =>
      val result = await(wsUrl(s"/${GreeterService.name}/FooBar").get)
      result.status must ===(415) // Maybe should be a 426, see #396
    }
    "give a grpc UNIMPLEMENTED when routing a non-existent gRPC method" >> { implicit rs: RunningServer =>
      val result = await(
        wsUrl(s"/${GreeterService.name}/FooBar")
          .addHttpHeaders("Content-Type" -> GrpcProtocolNative.contentType.toString)
          .get,
      )
      result.status must ===(200) // Maybe should be a 426, see #396
      result.header("grpc-status") must beSome(Status.Code.UNIMPLEMENTED.value().toString)
    }
    "give a grpc INVALID_ARGUMENT error when routing an empty request to a gRPC method" >> {
      implicit rs: RunningServer =>
        val result = await(
          wsUrl(s"/${GreeterService.name}/SayHello")
            .addHttpHeaders("Content-Type" -> GrpcProtocolNative.contentType.toString)
            .get,
        )
        result.status must ===(200) // Maybe should be a 426, see #396

        // grpc-status 3 means INVALID_ARGUMENT error. See https://developers.google.com/maps-booking/reference/grpc-api/status_codes
        result.header("grpc-status") must beSome(Status.Code.INVALID_ARGUMENT.value().toString)
    }
    "work with a gRPC client" >> { implicit rs: RunningServer =>
      withGrpcClient[GreeterServiceClient] { client: GreeterServiceClient =>
        val reply = await(client.sayHello(HelloRequest("Alice")))
        reply.message must ===("Hello, Alice!")
      }
    }
  }
} 
Example 41
Source File: GrpcStatus.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.grpc

import com.google.rpc.status.{Status => ProtobufStatus}
import io.grpc.Status
import io.grpc.Status.Code

object GrpcStatus {
  type Description = Option[String]

  def unapply(status: Status): Some[(Code, Description)] =
    Some((status.getCode, Option(status.getDescription)))

  def toProto(code: Code, description: Description): ProtobufStatus =
    ProtobufStatus(code.value, description.getOrElse(""))

  def toProto(status: Status): ProtobufStatus =
    toProto(status.getCode, Option(status.getDescription))

  private[grpc] final class SpecificGrpcStatus(code: Code) {
    def unapply(status: Status): Boolean =
      status.getCode == code
  }

  val OK = new SpecificGrpcStatus(Code.OK)
  val CANCELLED = new SpecificGrpcStatus(Code.CANCELLED)
  val UNKNOWN = new SpecificGrpcStatus(Code.UNKNOWN)
  val INVALID_ARGUMENT = new SpecificGrpcStatus(Code.INVALID_ARGUMENT)
  val DEADLINE_EXCEEDED = new SpecificGrpcStatus(Code.DEADLINE_EXCEEDED)
  val NOT_FOUND = new SpecificGrpcStatus(Code.NOT_FOUND)
  val ALREADY_EXISTS = new SpecificGrpcStatus(Code.ALREADY_EXISTS)
  val PERMISSION_DENIED = new SpecificGrpcStatus(Code.PERMISSION_DENIED)
  val RESOURCE_EXHAUSTED = new SpecificGrpcStatus(Code.RESOURCE_EXHAUSTED)
  val FAILED_PRECONDITION = new SpecificGrpcStatus(Code.FAILED_PRECONDITION)
  val ABORTED = new SpecificGrpcStatus(Code.ABORTED)
  val OUT_OF_RANGE = new SpecificGrpcStatus(Code.OUT_OF_RANGE)
  val UNIMPLEMENTED = new SpecificGrpcStatus(Code.UNIMPLEMENTED)
  val INTERNAL = new SpecificGrpcStatus(Code.INTERNAL)
  val UNAVAILABLE = new SpecificGrpcStatus(Code.UNAVAILABLE)
  val DATA_LOSS = new SpecificGrpcStatus(Code.DATA_LOSS)
  val UNAUTHENTICATED = new SpecificGrpcStatus(Code.UNAUTHENTICATED)
} 
Example 42
Source File: GrpcException.scala    From daml   with Apache License 2.0 5 votes vote down vote up
// Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.grpc

import com.daml.grpc.GrpcStatus.SpecificGrpcStatus
import io.grpc.{Metadata, Status, StatusException, StatusRuntimeException}

object GrpcException {
  def unapply(exception: Exception): Option[(Status, Metadata)] =
    exception match {
      case e: StatusRuntimeException => Some((e.getStatus, e.getTrailers))
      case e: StatusException => Some((e.getStatus, e.getTrailers))
      case _ => None
    }

  private[grpc] final class SpecificGrpcException(status: SpecificGrpcStatus) {
    def unapply(exception: Exception): Boolean =
      exception match {
        case e: StatusRuntimeException => status.unapply(e.getStatus)
        case e: StatusException => status.unapply(e.getStatus)
        case _ => false
      }
  }

  val OK = new SpecificGrpcException(GrpcStatus.OK)
  val CANCELLED = new SpecificGrpcException(GrpcStatus.CANCELLED)
  val UNKNOWN = new SpecificGrpcException(GrpcStatus.UNKNOWN)
  val INVALID_ARGUMENT = new SpecificGrpcException(GrpcStatus.INVALID_ARGUMENT)
  val DEADLINE_EXCEEDED = new SpecificGrpcException(GrpcStatus.DEADLINE_EXCEEDED)
  val NOT_FOUND = new SpecificGrpcException(GrpcStatus.NOT_FOUND)
  val ALREADY_EXISTS = new SpecificGrpcException(GrpcStatus.ALREADY_EXISTS)
  val PERMISSION_DENIED = new SpecificGrpcException(GrpcStatus.PERMISSION_DENIED)
  val RESOURCE_EXHAUSTED = new SpecificGrpcException(GrpcStatus.RESOURCE_EXHAUSTED)
  val FAILED_PRECONDITION = new SpecificGrpcException(GrpcStatus.FAILED_PRECONDITION)
  val ABORTED = new SpecificGrpcException(GrpcStatus.ABORTED)
  val OUT_OF_RANGE = new SpecificGrpcException(GrpcStatus.OUT_OF_RANGE)
  val UNIMPLEMENTED = new SpecificGrpcException(GrpcStatus.UNIMPLEMENTED)
  val INTERNAL = new SpecificGrpcException(GrpcStatus.INTERNAL)
  val UNAVAILABLE = new SpecificGrpcException(GrpcStatus.UNAVAILABLE)
  val DATA_LOSS = new SpecificGrpcException(GrpcStatus.DATA_LOSS)
  val UNAUTHENTICATED = new SpecificGrpcException(GrpcStatus.UNAUTHENTICATED)
} 
Example 43
Source File: GRPCErrors.scala    From matcher   with MIT License 5 votes vote down vote up
package com.wavesplatform.dex.grpc.integration.protobuf

import com.wavesplatform.api.http.ApiError
import com.wavesplatform.api.http.ApiError._
import io.grpc.Metadata.AsciiMarshaller
import io.grpc.{Metadata, Status, StatusException}

object GRPCErrors {
  private[this] val IntMarshaller: AsciiMarshaller[Int] = new AsciiMarshaller[Int] {
    override def toAsciiString(value: Int): String         = value.toString
    override def parseAsciiString(serialized: String): Int = serialized.toInt
  }

  val ErrorCodeKey = Metadata.Key.of("Error-Code", IntMarshaller)

  def toStatusException(api: ApiError): StatusException = {
    val code = api match {
      case WalletNotExist | WalletAddressDoesNotExist | TransactionDoesNotExist | AliasDoesNotExist(_) | BlockDoesNotExist | MissingSenderPrivateKey |
          DataKeyDoesNotExist =>
        Status.NOT_FOUND
      case WalletAlreadyExists => Status.ALREADY_EXISTS
      case WalletLocked        => Status.PERMISSION_DENIED
      case _                   => Status.INVALID_ARGUMENT
    }

    val metadata = new Metadata()
    metadata.put(ErrorCodeKey, api.id)
    code.withDescription(api.message).asException(metadata)
  }
} 
Example 44
Source File: GrpcAkkaStreamsClientCalls.scala    From grpcakkastream   with MIT License 5 votes vote down vote up
package grpc.akkastreams

import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference}

import akka.NotUsed
import akka.stream.scaladsl.{Flow, Source}
import com.trueaccord.scalapb.grpc.Grpc
import io.grpc.{ClientCall, Metadata, Status}
import io.grpc.stub._

object GrpcAkkaStreamsClientCalls {

  def unaryFlow[I, O](call: ClientCall[I, O]): Flow[I, O, NotUsed] =
    Flow[I].flatMapConcat(request =>
      Source.fromFuture(
        Grpc.guavaFuture2ScalaFuture(
          ClientCalls.futureUnaryCall(call, request)
        )
      )
    )

  def serverStreamingFlow[I, O](call: ClientCall[I, O]): Flow[I, O, NotUsed] =
    Flow.fromGraph(
      new GrpcGraphStage[I, O](outputObserver => {
        val out = outputObserver.asInstanceOf[ClientResponseObserver[I, O]]
        val in = new ClientCallStreamObserver[I] {
          val halfClosed = new AtomicBoolean(false)
          val onReadyHandler = new AtomicReference[Option[Runnable]](None)
          val listener = new ClientCall.Listener[O] {
            override def onClose(status: Status, trailers: Metadata): Unit =
              status.getCode match {
                case Status.Code.OK => out.onCompleted()
                case _ => out.onError(status.asException(trailers))
              }
            override def onMessage(message: O): Unit =
              out.onNext(message)
            override def onReady(): Unit =
              onReadyHandler.get().foreach(_.run())
          }
          call.start(listener, new Metadata())

          override def cancel(message: String, cause: Throwable): Unit =
            call.cancel(message, cause)
          override def setOnReadyHandler(onReadyHandler: Runnable): Unit =
            this.onReadyHandler.set(Some(onReadyHandler))
          override def request(count: Int): Unit = call.request(count)
          override def disableAutoInboundFlowControl(): Unit = ()
          override def isReady: Boolean = !halfClosed.get() || call.isReady
          override def setMessageCompression(enable: Boolean): Unit =
            call.setMessageCompression(enable)
          override def onError(t: Throwable): Unit =
            call.cancel("Cancelled by client with StreamObserver.onError()", t)
          override def onCompleted(): Unit = ()
          override def onNext(request: I): Unit = {
            call.sendMessage(request)
            halfClosed.set(true)
            call.halfClose()
          }
        }
        out.beforeStart(in)
        in
      })
    )

  def clientStreamingFlow[I, O](call: ClientCall[I, O]): Flow[I, O, NotUsed] =
    Flow.fromGraph(new GrpcGraphStage[I, O](ClientCalls.asyncClientStreamingCall(call, _)))

  def bidiStreamingFlow[I, O](call: ClientCall[I, O]): Flow[I, O, NotUsed] =
    Flow.fromGraph(new GrpcGraphStage[I, O](ClientCalls.asyncBidiStreamingCall(call, _)))
} 
Example 45
Source File: Fs2StreamClientCallListener.scala    From fs2-grpc   with MIT License 5 votes vote down vote up
package org.lyranthe.fs2_grpc
package java_runtime
package client

import cats.effect.{Effect, ConcurrentEffect}
import cats.implicits._
import fs2.{Pull, Stream}
import fs2.concurrent.Queue
import io.grpc.{ClientCall, Metadata, Status}

class Fs2StreamClientCallListener[F[_]: Effect, Response](
    request: Int => Unit,
    queue: Queue[F, Either[GrpcStatus, Response]]
) extends ClientCall.Listener[Response] {

  override def onMessage(message: Response): Unit = {
    request(1)
    queue.enqueue1(message.asRight).unsafeRun()
  }

  override def onClose(status: Status, trailers: Metadata): Unit =
    queue.enqueue1(GrpcStatus(status, trailers).asLeft).unsafeRun()

  def stream: Stream[F, Response] = {

    def go(q: Stream[F, Either[GrpcStatus, Response]]): Pull[F, Response, Unit] = {
      q.pull.uncons1.flatMap {
        case Some((Right(v), tl)) => Pull.output1(v) >> go(tl)
        case Some((Left(GrpcStatus(status, trailers)), _)) =>
          if (!status.isOk)
            Pull.raiseError[F](status.asRuntimeException(trailers))
          else
            Pull.done
        case None => Pull.done
      }
    }

    go(queue.dequeue).stream
  }
}

object Fs2StreamClientCallListener {

  def apply[F[_]: ConcurrentEffect, Response](request: Int => Unit): F[Fs2StreamClientCallListener[F, Response]] =
    Queue.unbounded[F, Either[GrpcStatus, Response]].map(new Fs2StreamClientCallListener[F, Response](request, _))

} 
Example 46
Source File: Fs2ServerCallListener.scala    From fs2-grpc   with MIT License 5 votes vote down vote up
package org.lyranthe.fs2_grpc
package java_runtime
package server

import cats.effect._
import cats.effect.concurrent.Deferred
import cats.implicits._
import fs2.Stream
import io.grpc.{Metadata, Status, StatusException, StatusRuntimeException}

private[server] trait Fs2ServerCallListener[F[_], G[_], Request, Response] {
  def source: G[Request]
  def isCancelled: Deferred[F, Unit]
  def call: Fs2ServerCall[F, Request, Response]

  private def reportError(t: Throwable)(implicit F: Sync[F]): F[Unit] = {
    t match {
      case ex: StatusException =>
        call.closeStream(ex.getStatus, Option(ex.getTrailers).getOrElse(new Metadata()))
      case ex: StatusRuntimeException =>
        call.closeStream(ex.getStatus, Option(ex.getTrailers).getOrElse(new Metadata()))
      case ex =>
        // TODO: Customize failure trailers?
        call.closeStream(Status.INTERNAL.withDescription(ex.getMessage).withCause(ex), new Metadata())
    }
  }

  private def handleUnaryResponse(headers: Metadata, response: F[Response])(implicit F: Sync[F]): F[Unit] =
    call.sendHeaders(headers) *> call.request(1) *> response >>= call.sendMessage

  private def handleStreamResponse(headers: Metadata, response: Stream[F, Response])(implicit F: Sync[F]): F[Unit] =
    call.sendHeaders(headers) *> call.request(1) *> response.evalMap(call.sendMessage).compile.drain

  private def unsafeRun(f: F[Unit])(implicit F: ConcurrentEffect[F]): Unit = {
    val bracketed = F.guaranteeCase(f) {
      case ExitCase.Completed => call.closeStream(Status.OK, new Metadata())
      case ExitCase.Canceled => call.closeStream(Status.CANCELLED, new Metadata())
      case ExitCase.Error(t) => reportError(t)
    }

    // Exceptions are reported by closing the call
    F.runAsync(F.race(bracketed, isCancelled.get))(_ => IO.unit).unsafeRunSync()
  }

  def unsafeUnaryResponse(headers: Metadata, implementation: G[Request] => F[Response])(implicit
      F: ConcurrentEffect[F]
  ): Unit =
    unsafeRun(handleUnaryResponse(headers, implementation(source)))

  def unsafeStreamResponse(headers: Metadata, implementation: G[Request] => Stream[F, Response])(implicit
      F: ConcurrentEffect[F]
  ): Unit =
    unsafeRun(handleStreamResponse(headers, implementation(source)))
} 
Example 47
Source File: DummyServerCall.scala    From fs2-grpc   with MIT License 5 votes vote down vote up
package org.lyranthe.fs2_grpc.java_runtime.server

import io.grpc.{Metadata, MethodDescriptor, ServerCall, Status}

import scala.collection.mutable.ArrayBuffer

class DummyServerCall extends ServerCall[String, Int] {
  val messages: ArrayBuffer[Int] = ArrayBuffer[Int]()
  var currentStatus: Option[Status] = None

  override def request(numMessages: Int): Unit = ()
  override def sendMessage(message: Int): Unit = {
    messages += message
    ()
  }
  override def sendHeaders(headers: Metadata): Unit = {
    ()
  }
  override def getMethodDescriptor: MethodDescriptor[String, Int] = ???
  override def close(status: Status, trailers: Metadata): Unit = {
    currentStatus = Some(status)
  }
  override def isCancelled: Boolean = false
} 
Example 48
Source File: RetryPolicyDefaults.scala    From akka-cloudpubsub   with Apache License 2.0 5 votes vote down vote up
package com.qubit.pubsub.client.retry

import java.util.concurrent.Executors

import com.gilt.gfc.concurrent.ThreadFactoryBuilder
import com.typesafe.scalalogging.LazyLogging
import io.grpc.{Status, StatusRuntimeException}

import scala.concurrent.ExecutionContext
import scala.util.Failure

object RetryPolicyDefaults extends LazyLogging {
  import atmos.dsl._
  import Slf4jSupport._

  import scala.concurrent.duration._

  private val unrecoverableErrorCodes = Set(Status.Code.PERMISSION_DENIED,
                                            Status.Code.UNAUTHENTICATED,
                                            Status.Code.INVALID_ARGUMENT)
  private val rateLimitingErrorCodes =
    Set(Status.Code.RESOURCE_EXHAUSTED, Status.Code.UNAVAILABLE)

  val retryPolicy = retryFor {
    10.attempts
  } using selectedBackoff {
    case Failure(sre: StatusRuntimeException)
        if rateLimitingErrorCodes.contains(sre.getStatus.getCode) =>
      linearBackoff { 50.seconds }
    case _ =>
      exponentialBackoff { 30.seconds } randomized 10.second -> 100.seconds
  } monitorWith {
    logger.underlying
  } onError {
    case sre: StatusRuntimeException
        if unrecoverableErrorCodes.contains(sre.getStatus.getCode) =>
      stopRetrying
  }

  val retryExecCtx = ExecutionContext.fromExecutor(
    Executors.newFixedThreadPool(
      10,
      ThreadFactoryBuilder("retry-pool", "retry-worker").build()
    ))
} 
Example 49
Source File: StatusExtract.scala    From gatling-grpc   with Apache License 2.0 5 votes vote down vote up
package com.github.phisgr.gatling.grpc.check

import io.gatling.commons.validation.SuccessWrapper
import io.gatling.core.Predef.value2Expression
import io.gatling.core.check._
import io.grpc.Status

private[gatling] object StatusExtract {

  val StatusDescription: ValidatorCheckBuilder[StatusExtract, Status, String] = ValidatorCheckBuilder(
    extractor = new FindExtractor[Status, String](
      name = "grpcStatusDescription",
      extractor = status => Option(status.getDescription).success
    ),
    displayActualValue = true
  )

  val StatusCode: ValidatorCheckBuilder[StatusExtract, Status, Status.Code] = ValidatorCheckBuilder(
    extractor = new FindExtractor[Status, Status.Code](
      name = "grpcStatusCode",
      extractor = status => Some(status.getCode).success
    ),
    displayActualValue = true
  )

  object Materializer extends CheckMaterializer[StatusExtract, GrpcCheck[Any], GrpcResponse[Any], Status](
    specializer = GrpcCheck(_, GrpcCheck.Status)
  ) {
    override protected def preparer: Preparer[GrpcResponse[Any], Status] = _.status.success
  }

  val DefaultCheck: GrpcCheck[Any] = StatusCode.is(value2Expression(Status.Code.OK)).build(Materializer)

}

// phantom type for implicit materializer resolution
trait StatusExtract 
Example 50
Source File: GrpcCheckSupport.scala    From gatling-grpc   with Apache License 2.0 5 votes vote down vote up
package com.github.phisgr.gatling.grpc.check

import io.gatling.core.check.{CheckBuilder, CheckMaterializer, DefaultMultipleFindCheckBuilder, FindCheckBuilder, ValidatorCheckBuilder}
import io.grpc.{Metadata, Status}

trait GrpcCheckSupport {

  val statusCode = StatusExtract.StatusCode

  val statusDescription = StatusExtract.StatusDescription

  def extract[T, X](f: T => Option[X]): ValidatorCheckBuilder[ResponseExtract, T, X] =
    ResponseExtract.extract(f)

  def extractMultiple[T, X](f: T => Option[Seq[X]]): DefaultMultipleFindCheckBuilder[ResponseExtract, T, X] =
    ResponseExtract.extractMultiple(f)

  def trailer[T](key: Metadata.Key[T]): DefaultMultipleFindCheckBuilder[TrailersExtract, Metadata, T] =
    TrailersExtract.trailer(key)

  implicit def resMat[Res]: CheckMaterializer[ResponseExtract, GrpcCheck[Res], GrpcResponse[Res], Res] =
    ResponseExtract.materializer[Res]

  implicit val statusMat: CheckMaterializer[StatusExtract, GrpcCheck[Any], GrpcResponse[Any], Status] =
    StatusExtract.Materializer

  implicit val trailersMat: CheckMaterializer[TrailersExtract, GrpcCheck[Any], GrpcResponse[Any], Metadata] =
    TrailersExtract.Materializer

  // The contravarianceHelper is needed because without it, the implicit conversion does not turn
  // CheckBuilder[StatusExtract, GrpcResponse[Any], X] into a GrpcCheck[Res]
  // Despite GrpcCheck[Any] is a subtype of GrpcCheck[Res].
  implicit def checkBuilder2GrpcCheck[A, P, X, ResOrAny, Res](checkBuilder: CheckBuilder[A, P, X])(
    implicit materializer: CheckMaterializer[A, GrpcCheck[ResOrAny], GrpcResponse[ResOrAny], P],
    contravarianceHelper: GrpcCheck[ResOrAny] => GrpcCheck[Res]
  ): GrpcCheck[Res] =
    contravarianceHelper(checkBuilder.build(materializer))

  implicit def validatorCheckBuilder2GrpcCheck[A, P, X, ResOrAny, Res](vCheckBuilder: ValidatorCheckBuilder[A, P, X])(
    implicit materializer: CheckMaterializer[A, GrpcCheck[ResOrAny], GrpcResponse[ResOrAny], P],
    contravarianceHelper: GrpcCheck[ResOrAny] => GrpcCheck[Res]
  ): GrpcCheck[Res] =
    vCheckBuilder.exists

  implicit def findCheckBuilder2GrpcCheck[A, P, X, ResOrAny, Res](findCheckBuilder: FindCheckBuilder[A, P, X])(
    implicit materializer: CheckMaterializer[A, GrpcCheck[ResOrAny], GrpcResponse[ResOrAny], P],
    contravarianceHelper: GrpcCheck[ResOrAny] => GrpcCheck[Res]
  ): GrpcCheck[Res] =
    findCheckBuilder.find.exists

  implicit def someWrapper[T](value: T): SomeWrapper[T] = new SomeWrapper(value)
} 
Example 51
Source File: util.scala    From gatling-grpc   with Apache License 2.0 5 votes vote down vote up
package com.github.phisgr.gatling.grpc.check

import io.gatling.commons.validation.{Failure, Success, Validation}
import io.grpc.{Metadata, Status}

import scala.annotation.unchecked.uncheckedVariance

case class GrpcResponse[+T](
  private val res: T, // can be null if status is not OK
  status: Status,
  trailers: Metadata
) {
  private var _validation: Validation[T@uncheckedVariance] = _

  // Hand-rolling lazy val because lazy is thread-safe
  def validation: Validation[T] = {
    if (_validation eq null) {
      _validation = if (status.isOk) {
        Success(res)
      } else {
        val description = status.getDescription
        Failure(if (description eq null) status.getCode.toString else s"${status.getCode}: $description")
      }
    }
    _validation
  }
}

class SomeWrapper[T](val value: T) extends AnyVal {
  def some: Some[T] = Some(value)
}