diff --git a/async-http-client-backend/zio/src/main/scala/sttp/client3/asynchttpclient/zio/AsyncHttpClientZioBackend.scala b/async-http-client-backend/zio/src/main/scala/sttp/client3/asynchttpclient/zio/AsyncHttpClientZioBackend.scala index 8eadb001dd..22cdd96ad8 100644 --- a/async-http-client-backend/zio/src/main/scala/sttp/client3/asynchttpclient/zio/AsyncHttpClientZioBackend.scala +++ b/async-http-client-backend/zio/src/main/scala/sttp/client3/asynchttpclient/zio/AsyncHttpClientZioBackend.scala @@ -110,6 +110,10 @@ object AsyncHttpClientZioBackend { new AsyncHttpClientZioBackend(runtime, asyncHttpClient, closeClient, customizeRequest, webSocketBufferCapacity) ) + // work-around for "You must not use an intersection type, yet have provided SttpBackend[Task, ZioStreams & WebSockets]", #2244 + implicit val sttpClientTag: Tag[SttpBackend[Task, ZioStreams with WebSockets]] = + Tag.materialize[SttpBackend[Task, ZioStreams]].asInstanceOf[Tag[SttpBackend[Task, ZioStreams with WebSockets]]] + def apply( options: SttpBackendOptions = SttpBackendOptions.Default, customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, diff --git a/build.sbt b/build.sbt index 6d376a7db1..d0023ef3f4 100644 --- a/build.sbt +++ b/build.sbt @@ -8,11 +8,11 @@ import complete.DefaultParsers._ // run JS tests inside Chrome, due to jsdom not supporting fetch import com.softwaremill.SbtSoftwareMillBrowserTestJS._ -val scala2_12 = "2.12.18" -val scala2_13 = "2.13.12" +val scala2_12 = "2.12.20" +val scala2_13 = "2.13.14" val scala2 = List(scala2_12, scala2_13) val scala2alive = List(scala2_12, scala2_13) -val scala3 = List("3.3.1") +val scala3 = List("3.3.3") lazy val testServerPort = settingKey[Int]("Port to run the http test server on") lazy val startTestServer = taskKey[Unit]("Start a http server used by tests") @@ -76,7 +76,6 @@ val commonJsBackendSettings = JSDependenciesPlugin.projectSettings ++ List( ) val commonNativeSettings = commonSettings ++ Seq( - nativeLinkStubs := true, Test / test := { // TODO: re-enable after scala-native release > 0.4.0-M2 if (sys.env.isDefinedAt("RELEASE_VERSION")) { @@ -119,26 +118,26 @@ val testServerSettings = Seq( ) val circeVersion: Option[(Long, Long)] => String = { _ => - "0.14.6" + "0.14.10" } -val jsoniterVersion = "2.22.1" +val jsoniterVersion = "2.30.11" val play2JsonVersion: Option[(Long, Long)] => String = { case Some((2, 11)) => "2.7.4" case _ => "2.9.2" } val playJsonVersion = "3.0.2" -val catsEffect_3_version = "3.5.1" -val fs2_3_version = "3.7.0" +val catsEffect_3_version = "3.5.4" +val fs2_3_version = "3.11.0" val catsEffect_2_version: Option[(Long, Long)] => String = { case Some((2, 11)) => "2.0.0" - case _ => "2.5.4" + case _ => "2.5.5" } val fs2_2_version: Option[(Long, Long)] => String = { case Some((2, 11)) => "2.1.0" - case _ => "2.5.9" + case _ => "2.5.12" } val akkaHttp = "com.typesafe.akka" %% "akka-http" % "10.2.10" @@ -150,16 +149,16 @@ val pekkoStreamVersion = "1.0.1" val pekkoStreams = "org.apache.pekko" %% "pekko-stream" % pekkoStreamVersion val scalaTest = libraryDependencies ++= Seq("freespec", "funsuite", "flatspec", "wordspec", "shouldmatchers").map(m => - "org.scalatest" %%% s"scalatest-$m" % "3.2.15" % Test + "org.scalatest" %%% s"scalatest-$m" % "3.2.19" % Test ) val zio1Version = "1.0.18" -val zio2Version = "2.0.10" +val zio2Version = "2.1.9" val zio1InteropRsVersion = "1.3.12" -val zio2InteropRsVersion = "2.0.1" +val zio2InteropRsVersion = "2.0.2" -val sttpModelVersion = "1.7.9" -val sttpSharedVersion = "1.3.16" +val sttpModelVersion = "1.7.11" +val sttpSharedVersion = "1.3.22" val logback = "ch.qos.logback" % "logback-classic" % "1.4.5" @@ -392,10 +391,6 @@ lazy val cats = (projectMatrix in file("effects/cats")) scalaVersions = scala2alive ++ scala3, settings = commonJsSettings ++ commonJsBackendSettings ++ browserChromeTestSettings ++ testServerSettings ) - .nativePlatform( - scalaVersions = scala2alive ++ scala3, - settings = commonNativeSettings - ) lazy val fs2Ce2 = (projectMatrix in file("effects/fs2-ce2")) .settings( @@ -440,7 +435,6 @@ lazy val fs2 = (projectMatrix in file("effects/fs2")) ) ) .jsPlatform(scalaVersions = scala2alive ++ scala3, settings = commonJsSettings) - .nativePlatform(scalaVersions = scala2alive ++ scala3, settings = commonNativeSettings) lazy val monix = (projectMatrix in file("effects/monix")) .settings( @@ -883,7 +877,7 @@ lazy val upickle = (projectMatrix in file("json/upickle")) .settings( name := "upickle", libraryDependencies ++= Seq( - "com.lihaoyi" %%% "upickle" % "2.0.0" + "com.lihaoyi" %%% "upickle" % "3.3.1" ), scalaTest, // using macroRW causes a "match may not be exhaustive" error @@ -999,7 +993,7 @@ lazy val scribeBackend = (projectMatrix in file("logging/scribe")) .settings( name := "scribe-backend", libraryDependencies ++= Seq( - "com.outr" %%% "scribe" % "3.11.1" + "com.outr" %%% "scribe" % "3.15.0" ), scalaTest ) diff --git a/core/src/main/scalanative/sttp/client3/AbstractCurlBackend.scala b/core/src/main/scalanative/sttp/client3/AbstractCurlBackend.scala index dcfb1721b5..692cdd986d 100644 --- a/core/src/main/scalanative/sttp/client3/AbstractCurlBackend.scala +++ b/core/src/main/scalanative/sttp/client3/AbstractCurlBackend.scala @@ -13,6 +13,7 @@ import sttp.monad.MonadError import sttp.monad.syntax._ import scala.collection.immutable.Seq +import scala.collection.mutable.ArrayBuffer import scala.io.Source import scala.scalanative.libc.stdio.{FILE, fclose, fopen} import scala.scalanative.libc.stdlib._ @@ -31,9 +32,41 @@ abstract class AbstractCurlBackend[F[_]](monad: MonadError[F], verbose: Boolean) type PE = Any with Effect[F] + /** A request-specific context, with allocated zones and headers. */ + private class Context() { + implicit val zone: Zone = Zone.open() + private val headers = ArrayBuffer[CurlList]() + + /** Create a new Headers list that gets cleaned up when the context is destroyed. */ + def transformHeaders(reqHeaders: Iterable[Header]): CurlList = { + val h = reqHeaders + .map(header => s"${header.name}: ${header.value}") + .foldLeft(new CurlList(null)) { case (acc, h) => + new CurlList(acc.ptr.append(h)) + } + headers += h + h + } + + def close() = { + zone.close() + headers.foreach(l => if (l.ptr != null) l.ptr.free()) + } + } + + private object Context { + + /** Create a new context and evaluates the body with it. Closes the context at the end. */ + def evaluateUsing[T](body: Context => F[T]): F[T] = { + implicit val ctx = new Context() + body(ctx).ensure(monad.unit(ctx.close())) + } + } + override def send[T, R >: PE](request: Request[T, R]): F[Response[T]] = adjustExceptions(request) { - unsafe.Zone { implicit z => + def perform(implicit ctx: Context): F[Response[T]] = { + implicit val z = ctx.zone val curl = CurlApi.init if (verbose) { curl.option(Verbose, parameter = true) @@ -61,6 +94,7 @@ abstract class AbstractCurlBackend[F[_]](monad: MonadError[F], verbose: Boolean) case None => handleBase(request, curl, spaces) } } + Context.evaluateUsing(ctx => perform(ctx)) } private def adjustExceptions[T](request: Request[_, _])(t: => F[T]): F[T] = diff --git a/core/src/main/scalanative/sttp/client3/curl/CurlApi.scala b/core/src/main/scalanative/sttp/client3/curl/CurlApi.scala index 2faaa60195..42a4456de8 100644 --- a/core/src/main/scalanative/sttp/client3/curl/CurlApi.scala +++ b/core/src/main/scalanative/sttp/client3/curl/CurlApi.scala @@ -93,7 +93,7 @@ private[client3] object CurlApi { def withEncoding(encoding: String)(implicit zone: Zone): CurlCode = CCurl.mimeEncoder(handle, toCString(encoding)) def withData(data: String, datasize: Long = CurlZeroTerminated)(implicit zone: Zone): CurlCode = - CCurl.mimeData(handle, toCString(data), datasize.toULong) + CCurl.mimeData(handle, toCString(data), datasize.toCSize) def withFileData(filename: String)(implicit zone: Zone): CurlCode = CCurl.mimeFiledata(handle, toCString(filename)) diff --git a/effects/zio/src/main/scalajvm/sttp/client3/httpclient/zio/package.scala b/effects/zio/src/main/scalajvm/sttp/client3/httpclient/zio/package.scala index 9782b0f570..8a8455ae6e 100644 --- a/effects/zio/src/main/scalajvm/sttp/client3/httpclient/zio/package.scala +++ b/effects/zio/src/main/scalajvm/sttp/client3/httpclient/zio/package.scala @@ -9,7 +9,11 @@ import sttp.client3.impl.zio.ExtendEnv package object zio { /** Type alias to be used as the sttp ZIO service (mainly in ZIO environment). */ - type SttpClient = SttpBackend[Task, ZioStreams with WebSockets] + type SttpClient = SttpBackend[Task, ZioStreams & WebSockets] + + // work-around for "You must not use an intersection type, yet have provided SttpBackend[Task, ZioStreams & WebSockets]", #2244 + implicit val sttpClientTag: Tag[SttpClient] = + Tag.materialize[SttpBackend[Task, ZioStreams]].asInstanceOf[Tag[SttpClient]] /** Sends the request. Only requests for which the method & URI are specified can be sent. * diff --git a/project/plugins.sbt b/project/plugins.sbt index 52cfadd976..dee4a8b189 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,15 +1,15 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.2") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17") -addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1") -addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.9.0") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") +addSbtPlugin("io.spray" % "sbt-revolver" % "0.10.0") +addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.0") -val sbtSoftwareMillVersion = "2.0.19" +val sbtSoftwareMillVersion = "2.0.20" addSbtPlugin("com.softwaremill.sbt-softwaremill" % "sbt-softwaremill-common" % sbtSoftwareMillVersion) addSbtPlugin("com.softwaremill.sbt-softwaremill" % "sbt-softwaremill-publish" % sbtSoftwareMillVersion) addSbtPlugin("com.softwaremill.sbt-softwaremill" % "sbt-softwaremill-browser-test-js" % sbtSoftwareMillVersion) -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.7") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.6.0") -addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.1") -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.1") +addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.2") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4")