All Downloads are FREE. Search and download functionalities are using the official Maven repository.

akka.grpc.internal.MetadataImpl.scala Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2009-2019 Lightbend Inc. 
 */

package akka.grpc.internal

import java.util.Optional

import akka.annotation.InternalApi
import akka.grpc.{ javadsl, scaladsl }
import akka.util.ByteString
import io.grpc.Metadata
import scala.compat.java8.OptionConverters._

// the io.grpc.Metadata class is mutable and has a horrible API, let's hide it
@InternalApi private[akka] sealed trait MetadataEntry
@InternalApi private[akka] case class StringEntry(value: String) extends MetadataEntry
@InternalApi private[akka] case class BytesEntry(value: ByteString) extends MetadataEntry

@InternalApi private[akka] object MetadataImpl {
  val empty = new MetadataImpl(List.empty)

  // somewhat expensive, but we try to avoid, multi-value entries are in reversed order
  private def metadataMapFromGoogleGrpcMetadata(mutableMetadata: io.grpc.Metadata): Map[String, List[MetadataEntry]] = {
    val iterator = mutableMetadata.keys().iterator()
    var entries = Map.empty[String, List[MetadataEntry]]
    while (iterator.hasNext) {
      val key = iterator.next()
      val entry =
        if (key.endsWith("-bin")) {
          val bytes = mutableMetadata.get(Metadata.Key.of(key, Metadata.BINARY_BYTE_MARSHALLER))
          BytesEntry(ByteString(bytes))
        } else {
          val text = mutableMetadata.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER))
          StringEntry(text)
        }
      if (entries.contains(key)) {
        entries += (key -> (entry :: entries(key)))
      } else
        entries += (key -> (entry :: Nil))
    }
    entries
  }

  def scalaMetadataFromGoogleGrpcMetadata(mutableMetadata: io.grpc.Metadata): akka.grpc.scaladsl.Metadata = {
    val metadata = metadataMapFromGoogleGrpcMetadata(mutableMetadata)
    new akka.grpc.scaladsl.Metadata {
      def getText(key: String): Option[String] = metadata.get(key) match {
        case Some(StringEntry(text) :: Nil) => Some(text)
        case Some(multiple) => multiple.reverseIterator.collectFirst { case StringEntry(text) => text }
        case _ => None
      }
      def getBinary(key: String): Option[ByteString] = metadata.get(key) match {
        case Some(BytesEntry(bytes) :: Nil) => Some(bytes)
        case Some(multiple) => multiple.reverseIterator.collectFirst { case BytesEntry(bytes) => bytes }
        case _ => None
      }
      override def toString: String = s"Metadata(${niceStringRep(metadata)})"
    }
  }

  def javaMetadataFromGoogleGrpcMetadata(mutableMetadata: io.grpc.Metadata): akka.grpc.javadsl.Metadata = {
    val metadata = metadataMapFromGoogleGrpcMetadata(mutableMetadata)
    new javadsl.Metadata {
      def getText(key: String): Optional[String] = metadata.get(key) match {
        case Some(StringEntry(text) :: Nil) => Optional.of(text)
        case Some(multiple) => multiple.reverseIterator.collectFirst { case StringEntry(text) => text }.asJava
        case _ => Optional.empty()
      }
      def getBinary(key: String): Optional[ByteString] = metadata.get(key) match {
        case Some(BytesEntry(bytes) :: Nil) => Optional.of(bytes)
        case Some(multiple) => multiple.reverseIterator.collectFirst { case BytesEntry(bytes) => bytes }.asJava
        case _ => Optional.empty()
      }
      override def toString: String = s"Metadata(${niceStringRep(metadata)})"
    }
  }

  private def niceStringRep(metadata: Map[String, List[MetadataEntry]]) =
    metadata.map { case (key, values) => key + " -> " + values.mkString("[", ", ", "]") }.mkString(", ")
}

@InternalApi private[akka] final class MetadataImpl(entries: List[(String, MetadataEntry)]) {

  def addEntry(key: String, value: String): MetadataImpl = {
    if (key.endsWith("-bin")) throw new IllegalArgumentException("String header names must not end with '-bin'")
    new MetadataImpl((key -> StringEntry(value)) :: entries)
  }

  def addEntry(key: String, value: ByteString): MetadataImpl = {
    if (!key.endsWith("-bin")) throw new IllegalArgumentException("Binary headers names must end with '-bin'")
    new MetadataImpl((key -> BytesEntry(value)) :: entries)
  }

  def toGoogleGrpcMetadata(): io.grpc.Metadata = {
    val mutableMetadata = new io.grpc.Metadata()
    entries.reverseIterator.foreach {
      case (key, entry) =>
        entry match {
          case StringEntry(value) =>
            mutableMetadata.put(io.grpc.Metadata.Key.of(key, io.grpc.Metadata.ASCII_STRING_MARSHALLER), value)

          case BytesEntry(value) =>
            mutableMetadata.put(io.grpc.Metadata.Key.of(key, io.grpc.Metadata.BINARY_BYTE_MARSHALLER), value.toArray)
        }
    }
    mutableMetadata
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy