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

sttp.client3.pekkohttp.BodyToPekko.scala Maven / Gradle / Ivy

package sttp.client3.pekkohttp

import org.apache.pekko
import pekko.http.scaladsl.model.{
  ContentType,
  HttpCharsets,
  HttpEntity,
  HttpRequest,
  MediaType,
  Multipart => PekkoMultipart,
  RequestEntity
}
import pekko.stream.scaladsl.{Source, StreamConverters}
import pekko.util.ByteString
import sttp.capabilities.pekko.PekkoStreams
import sttp.client3.internal.throwNestedMultipartNotAllowed
import sttp.client3._
import sttp.model.{HeaderNames, Part}

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

private[pekkohttp] object BodyToPekko {
  def apply[R](
      r: Request[_, R],
      body: RequestBody[R],
      ar: HttpRequest
  ): Try[HttpRequest] = {
    def ctWithCharset(ct: ContentType, charset: String) =
      HttpCharsets
        .getForKey(charset)
        .map(hc => ContentType.apply(ct.mediaType, () => hc))
        .getOrElse(ct)

    def contentLength = r.headers.find(_.is(HeaderNames.ContentLength)).flatMap(h => Try(h.value.toLong).toOption)

    def toBodyPart(mp: Part[RequestBody[_]]): Try[PekkoMultipart.FormData.BodyPart] = {
      def streamPartEntity(contentType: ContentType, s: PekkoStreams.BinaryStream) =
        mp.contentLength match {
          case None    => HttpEntity.IndefiniteLength(contentType, s)
          case Some(l) => HttpEntity(contentType, l, s)
        }

      def entity(ct: ContentType) =
        mp.body match {
          case StringBody(b, encoding, _) => HttpEntity(ctWithCharset(ct, encoding), b.getBytes(encoding))
          case ByteArrayBody(b, _)        => HttpEntity(ct, b)
          case ByteBufferBody(b, _)       => HttpEntity(ct, ByteString(b))
          case isb: InputStreamBody       => streamPartEntity(ct, StreamConverters.fromInputStream(() => isb.b))
          case FileBody(b, _)             => HttpEntity.fromPath(ct, b.toPath)
          case StreamBody(b)              => streamPartEntity(ct, b.asInstanceOf[PekkoStreams.BinaryStream])
          case MultipartBody(_)           => throwNestedMultipartNotAllowed
          case NoBody                     => HttpEntity.Empty
        }

      for {
        ct <- Util.parseContentTypeOrOctetStream(mp.contentType)
        headers <- ToPekko.headers(mp.headers.toList)
      } yield PekkoMultipart.FormData.BodyPart(mp.name, entity(ct), mp.dispositionParams, headers)
    }

    def streamEntity(contentType: ContentType, s: PekkoStreams.BinaryStream) =
      contentLength match {
        case None    => HttpEntity(contentType, s)
        case Some(l) => HttpEntity(contentType, l, s)
      }

    Util.parseContentTypeOrOctetStream(r).flatMap { ct =>
      body match {
        case NoBody                     => Success(ar)
        case StringBody(b, encoding, _) => Success(ar.withEntity(ctWithCharset(ct, encoding), b.getBytes(encoding)))
        case ByteArrayBody(b, _)        => Success(ar.withEntity(HttpEntity(ct, b)))
        case ByteBufferBody(b, _)       => Success(ar.withEntity(HttpEntity(ct, ByteString(b))))
        case InputStreamBody(b, _) =>
          Success(ar.withEntity(streamEntity(ct, StreamConverters.fromInputStream(() => b))))
        case FileBody(b, _) => Success(ar.withEntity(ct, b.toPath))
        case StreamBody(s)  => Success(ar.withEntity(streamEntity(ct, s.asInstanceOf[PekkoStreams.BinaryStream])))
        case m: MultipartBody[_] =>
          Util
            .traverseTry(m.parts.map(toBodyPart))
            .flatMap(bodyParts => multipartEntity(r, bodyParts).map(ar.withEntity))
      }
    }
  }

  private def multipartEntity(
      r: Request[_, _],
      bodyParts: Seq[PekkoMultipart.FormData.BodyPart]
  ): Try[RequestEntity] =
    r.headers.find(Util.isContentType) match {
      case None => Success(PekkoMultipart.FormData(bodyParts: _*).toEntity)
      case Some(ct) =>
        Util.parseContentType(ct.value).map(_.mediaType).flatMap {
          case m: MediaType.Multipart =>
            Success(
              PekkoMultipart
                .General(m, Source(bodyParts.map(bp => PekkoMultipart.General.BodyPart(bp.entity, bp.headers))))
                .toEntity
            )
          case _ => Failure(new RuntimeException(s"Non-multipart content type: $ct"))
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy