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

sttp.client3.asynchttpclient.BodyToAHC.scala Maven / Gradle / Ivy

The newest version!
package sttp.client3.asynchttpclient

import java.nio.charset.Charset

import io.netty.buffer.ByteBuf
import org.asynchttpclient.{Param, RequestBuilder}
import org.asynchttpclient.request.body.multipart.{ByteArrayPart, FilePart, StringPart}
import org.reactivestreams.Publisher
import sttp.capabilities.Streams
import sttp.client3.{
  ByteArrayBody,
  ByteBufferBody,
  FileBody,
  InputStreamBody,
  MultipartBody,
  NoBody,
  Request,
  RequestBody,
  StreamBody,
  StringBody
}
import sttp.client3.internal.{throwNestedMultipartNotAllowed, toByteArray}
import sttp.model.{HeaderNames, MediaType, Part}

import scala.collection.JavaConverters._

private[asynchttpclient] trait BodyToAHC[F[_], S] {
  val streams: Streams[S]
  protected def streamToPublisher(s: streams.BinaryStream): Publisher[ByteBuf]

  def apply[R](r: Request[_, R], body: RequestBody[R], rb: RequestBuilder): Unit = {
    body match {
      case NoBody => // skip
      case StringBody(b, encoding, _) =>
        rb.setBody(b.getBytes(encoding))

      case ByteArrayBody(b, _) =>
        rb.setBody(b)

      case ByteBufferBody(b, _) =>
        rb.setBody(b)

      case InputStreamBody(b, _) =>
        rb.setBody(b)

      case FileBody(b, _) =>
        rb.setBody(b.toFile)

      case StreamBody(s) =>
        val cl = r.headers
          .find(_.is(HeaderNames.ContentLength))
          .map(_.value.toLong)
          .getOrElse(-1L)
        rb.setBody(streamToPublisher(s.asInstanceOf[streams.BinaryStream]), cl)

      case MultipartBody(ps) =>
        ps.foreach(addMultipartBody(rb, _))
    }
  }

  private def addMultipartBody(rb: RequestBuilder, mp: Part[RequestBody[_]]): Unit = {
    // async http client only supports setting file names on file parts. To
    // set a file name on an arbitrary part we have to use a small "work
    // around", combining the file name with the name (surrounding quotes
    // are added by ahc).
    def nameWithFilename = mp.fileName.fold(mp.name) { fn => s"""${mp.name}"; ${Part.FileNameDispositionParam}="$fn""" }

    val ctOrNull = mp.contentType.orNull

    val bodyPart = mp.body match {
      case NoBody => new StringPart(nameWithFilename, "")
      case StringBody(b, encoding, _) =>
        new StringPart(
          nameWithFilename,
          b,
          mp.contentType.getOrElse(MediaType.TextPlain.toString),
          Charset.forName(encoding)
        )
      case ByteArrayBody(b, _) =>
        new ByteArrayPart(nameWithFilename, b, ctOrNull)
      case ByteBufferBody(b, _) =>
        new ByteArrayPart(nameWithFilename, b.array(), ctOrNull)
      case InputStreamBody(b, _) =>
        // sadly async http client only supports parts that are strings,
        // byte arrays or files
        new ByteArrayPart(nameWithFilename, toByteArray(b), ctOrNull)
      case FileBody(b, _) =>
        new FilePart(mp.name, b.toFile, ctOrNull, null, mp.fileName.orNull)
      case StreamBody(_) =>
        throw new IllegalArgumentException("Streaming multipart bodies are not supported")
      case MultipartBody(_) => throwNestedMultipartNotAllowed
    }

    bodyPart.setCustomHeaders(
      mp.headers.filterNot(_.is(HeaderNames.ContentType)).map(h => new Param(h.name, h.value)).toList.asJava
    )

    rb.addBodyPart(bodyPart)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy