diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25cdf702..6f377d9b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,9 +29,11 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - scala: [2.13, 3] + scala: [2.12, 2.13, 3] java: [temurin@11, temurin@17] exclude: + - scala: 2.12 + java: temurin@17 - scala: 3 java: temurin@17 runs-on: ${{ matrix.os }} @@ -98,15 +100,15 @@ jobs: run: sbt '++ ${{ matrix.scala }}' unusedCompileDependenciesTest - name: Make target directories - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/series/0.9') run: mkdir -p core/target project/target - name: Compress target directories - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/series/0.9') run: tar cf targets.tar core/target project/target - name: Upload target directories - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/series/0.9') uses: actions/upload-artifact@v4 with: name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }} @@ -115,7 +117,7 @@ jobs: publish: name: Publish Artifacts needs: [build] - if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/series/0.9') strategy: matrix: os: [ubuntu-latest] @@ -156,6 +158,16 @@ jobs: if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' run: sbt +update + - name: Download target directories (2.12) + uses: actions/download-artifact@v4 + with: + name: target-${{ matrix.os }}-${{ matrix.java }}-2.12 + + - name: Inflate target directories (2.12) + run: | + tar xf targets.tar + rm targets.tar + - name: Download target directories (2.13) uses: actions/download-artifact@v4 with: @@ -246,7 +258,7 @@ jobs: - name: Submit Dependencies uses: scalacenter/sbt-dependency-submission@v2 with: - modules-ignore: root_2.13 root_3 docs_2.13 docs_3 sbt-http4s-org-scalafix-internal_2.13 sbt-http4s-org-scalafix-internal_3 + modules-ignore: root_2.12 root_2.13 root_3 docs_2.12 docs_2.13 docs_3 sbt-http4s-org-scalafix-internal_2.12 sbt-http4s-org-scalafix-internal_2.13 sbt-http4s-org-scalafix-internal_3 configs-ignore: test scala-tool scala-doc-tool test-internal site: @@ -295,7 +307,7 @@ jobs: run: sbt docs/tlSite - name: Publish site - if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/series/0.9' uses: peaceiris/actions-gh-pages@v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.mergify.yml b/.mergify.yml index 3a3cb2e3..d97e0ec2 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -10,6 +10,7 @@ pull_request_rules: conditions: - author=http4s-steward[bot] - body~=labels:.*early-semver-patch + - status-success=Build and Test (ubuntu-latest, 2.12, temurin@11) - status-success=Build and Test (ubuntu-latest, 2.13, temurin@11) - status-success=Build and Test (ubuntu-latest, 2.13, temurin@17) - status-success=Build and Test (ubuntu-latest, 3, temurin@11) diff --git a/.scalafmt.conf b/.scalafmt.conf index 4943dbe8..ac610336 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,6 +1,6 @@ version=3.8.3 -runner.dialect = scala213 +runner.dialect = scala212 style = default maxColumn = 100 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0396092a..a3564655 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,15 @@ This file summarizes **notable** changes for each release, but does not describe ---- -## v1.0.0-M9 (2023-02-08) +## v0.9.1 (2023-05-13) + +### Dependency updates + +* cats-effect-3.5.0 +* fs2-3.7.0 +* http4s-0.23.19 + +## v0.9.0 (2023-02-08) ### "Breaking" changes @@ -14,15 +22,8 @@ This file summarizes **notable** changes for each release, but does not describe * scala-3.2.2 * fs2-3.6.0 -* http4s-1.0.0-M39 - -## v1.0.0-M8 (2023-01-05) - -### Dependency updates -* **http4s-1.0.0-M38** - -## v1.0.0-M7 (2022-11-20) +## v0.8.0 (2022-11-20) ### Breaking changes @@ -33,120 +34,37 @@ This file summarizes **notable** changes for each release, but does not describe * cats-effect-3.4.1 * fs2-3.4.0 -## v1.0.0-M6 (2022-09-20) - -### Dependency updates - -* **http4s-1.0.0-M37** - -## v1.0.0-M5 (2022-08-27) - -### Dependency updates - -* **http4s-1.0.0-M36** - -## v1.0.0-M4 (2022-07-29) - -### Dependency updates - -* **http4s-1.0.0-M35** - -## v1.0.0-M3 (2022-07-07) - -### Dependency updates - -* cats-2.8.0 -* **http4s-1.0.0-M34** - -## v1.0.0-M2 (2022-05-31) - -### Improvements - -* Optimizations via new entity model. [#644](https://github.com/http4s/http4s-jdk-http-client/pull/644) -* Use Cats Effect EC as `HttpClient` executor. [#641](https://github.com/http4s/http4s-jdk-http-client/pull/641) +## v0.7.0 (2022-03-23) -### Dependency updates - -* cats-effect-3.3.12 -* fs2-3.2.7 -* **http4s-1.0.0-M33** - -## v1.0.0-M1 (2022-03-23) - -This is the spiritual successor to v0.6.0-M7. +This is the spiritual successor to v0.5.0. ### Breaking changes -* We now use the upstream WebSocket model made public in http4s-1.0.0-M32. +* We now use the upstream WebSocket model made public in http4s-0.23.11. ### Dependency updates -* scala-2.13.8, scala-3.1.1 +* scala-2.12.15, scala-2.13.8, scala-3.1.1 +* cats-2.7.0 * cats-effect-3.3.8 * fs2-3.2.5 -* http4s-1.0.0-M32 - -## v0.6.0-M7 (2021-12-09) - -### Dependency updates - -* **http4s-1.0.0-M30** -* **scala-3.1.0** -* scala-2.13.7 -* cats-2.7.0 -* cats-effect-3.3.0 -* fs2-3.2.3 -* scodec-bits-1.1.30 -* case-insensitive-1.2.0 - -## v0.6.0-M6 (2021-10-13) - -### Dependency updates +* http4s-0.23.11 -* **http4s-1.0.0-M29** -* fs2-3.1.5 +## v0.5.0 (2021-07-31) -## v0.6.0-M5 (2021-10-07) - -### Dependency updates - -* **http4s-1.0.0-M28** -* fs2-3.1.4 -* scodec-1.1.29 - -## v0.6.0-M4 (2021-09-21) - -### Dependency updates - -* **http4s-1.0.0-M27** -* scala-2.12.15 -* cats-effect-3.2.9 -* vault-3.1.0 - -## v0.6.0-M3 (2021-09-15) - -### Dependency updates - -* **http4s-1.0.0-M25** -* scala-3.0.2 -* cats-effect-3.2.8 -* fs2-3.1.2 -* scodec-bits-1.1.28 - -## v0.6.0-M2 (2021-08-08) +This release is considered stable. ### Dependency updates -* **http4s-1.0.0-M24** +* **http4s-0.23.0** * scala-2.12.14 * scala-3.0.1 -* cats-effect-3.2.2 -* fs2-3.1.0 -* vault-3.0.4 +* cats-effect-3.2.1 +* fs2-3.0.6 -## v0.6.0-M1 (2021-05-28) +## v0.5.0-RC1 (2021-05-28) -This is the moral successor to v0.5.0-M4, tracking the 1.x releases of http4s. +The v0.5.x releases will now track http4s-0.23.x. ### Breaking changes @@ -155,7 +73,7 @@ This is the moral successor to v0.5.0-M4, tracking the 1.x releases of http4s. ### Dependency updates * **scala-3.0.0** -* **http4s-1.0.0-M23** +* **http4s-0.23.0-RC1** * scala-2.13.6 * cats-2.6.1 * cats-effect-3.1.1 diff --git a/build.sbt b/build.sbt index 5f878b6c..01e59985 100644 --- a/build.sbt +++ b/build.sbt @@ -38,8 +38,7 @@ val catsV = "2.12.0" val catsEffectV = "3.5.5" val fs2V = "3.11.0" val scodecV = "1.2.1" -val http4sV = "1.0.0-M43" -val log4catsV = "2.7.0" +val http4sV = "0.23.29" val reactiveStreamsV = "1.0.4" val vaultV = "3.6.0" val caseInsensitiveV = "1.4.2" @@ -49,8 +48,7 @@ val munitCatsEffectV = "2.0.0" val emberServer = Seq( "org.http4s" %% "http4s-ember-server" % http4sV, - "org.http4s" %% "http4s-dsl" % http4sV, - "org.typelevel" %% "log4cats-noop" % log4catsV + "org.http4s" %% "http4s-dsl" % http4sV ) val coreDeps = Seq( @@ -71,9 +69,9 @@ val coreDeps = Seq( )).map(_ % Test) val scala213 = "2.13.15" -ThisBuild / crossScalaVersions := Seq(scala213, "3.3.4") +ThisBuild / crossScalaVersions := Seq("2.12.20", scala213, "3.3.4") ThisBuild / scalaVersion := scala213 -ThisBuild / tlBaseVersion := "1.0" +ThisBuild / tlBaseVersion := "0.9" ThisBuild / startYear := Some(2019) ThisBuild / developers := List( tlGitHubDev("ChristopherDavenport", "Christopher Davenport"), @@ -83,8 +81,8 @@ ThisBuild / developers := List( ThisBuild / tlJdkRelease := Some(11) ThisBuild / githubWorkflowJavaVersions := Seq("11", "17").map(JavaSpec.temurin(_)) -ThisBuild / tlCiReleaseBranches := Seq("main") -ThisBuild / tlSitePublishBranch := Some("main") +ThisBuild / tlCiReleaseBranches := Seq("series/0.9") +ThisBuild / tlSitePublishBranch := Some("series/0.9") lazy val docsSettings = Seq( @@ -95,9 +93,8 @@ lazy val docsSettings = import laika.config._ tlSiteHelium.value.site.versions( Versions - .forCurrentVersion(Version("1.x", "1.x")) + .forCurrentVersion(Version("0.9.x", "0.9")) .withOlderVersions( - Version("0.9.x", "0.9"), Version("0.8.x", "0.8"), Version("0.7.x", "0.7"), Version("0.6.x", "0.6.0-M7"), diff --git a/core/src/main/scala/org/http4s/jdkhttpclient/JdkHttpClient.scala b/core/src/main/scala/org/http4s/jdkhttpclient/JdkHttpClient.scala index 1a23c924..1c0472d7 100644 --- a/core/src/main/scala/org/http4s/jdkhttpclient/JdkHttpClient.scala +++ b/core/src/main/scala/org/http4s/jdkhttpclient/JdkHttpClient.scala @@ -24,7 +24,6 @@ import fs2.Chunk import fs2.Stream import fs2.concurrent.SignallingRef import fs2.interop.flow -import org.http4s.Entity import org.http4s.Header import org.http4s.Headers import org.http4s.HttpVersion @@ -32,12 +31,12 @@ import org.http4s.Request import org.http4s.Response import org.http4s.Status import org.http4s.client.Client +import org.http4s.internal.CollectionCompat.CollectionConverters._ import org.typelevel.ci.CIString import java.net.URI import java.net.http.HttpClient import java.net.http.HttpRequest -import java.net.http.HttpRequest.BodyPublisher import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse import java.net.http.HttpResponse.BodyHandlers @@ -45,7 +44,6 @@ import java.nio.ByteBuffer import java.time.Duration import java.util import java.util.concurrent.Flow -import scala.jdk.CollectionConverters._ object JdkHttpClient { @@ -62,16 +60,12 @@ object JdkHttpClient { jdkHttpClient: HttpClient, ignoredHeaders: Set[CIString] = restrictedHeaders )(implicit F: Async[F]): Client[F] = { - def convertRequest(req: Request[F]): Resource[F, HttpRequest] = for { - version <- Resource.eval(convertHttpVersionFromHttp4s[F](req.httpVersion)) - bodyPublisher <- req.entity match { - case Entity.Empty => Resource.pure[F, BodyPublisher](BodyPublishers.noBody()) - case Entity.Strict(bytes) => - Resource.pure[F, BodyPublisher](BodyPublishers.ofInputStream(() => bytes.toInputStream)) - case Entity.Streamed(body, _) => - flow - .toPublisher(body.chunks.map(_.toByteBuffer)) - .map { publisher => + def convertRequest(req: Request[F]): Resource[F, HttpRequest] = + flow.toPublisher(req.body.chunks.map(_.toByteBuffer)).evalMap { publisher => + convertHttpVersionFromHttp4s[F](req.httpVersion).map { version => + val rb = HttpRequest.newBuilder + .method( + req.method.name, if (req.isChunked) BodyPublishers.fromPublisher(publisher) else @@ -80,17 +74,16 @@ object JdkHttpClient { BodyPublishers.fromPublisher(publisher, length) case _ => BodyPublishers.noBody } - } + ) + .uri(URI.create(req.uri.renderString)) + .version(version) + val headers = req.headers.headers.iterator + .filterNot(h => ignoredHeaders.contains(h.name)) + .flatMap(h => Iterator(h.name.toString, h.value)) + .toArray + (if (headers.isEmpty) rb else rb.headers(headers: _*)).build + } } - rb = HttpRequest.newBuilder - .method(req.method.name, bodyPublisher) - .uri(URI.create(req.uri.renderString)) - .version(version) - headers = req.headers.headers.iterator - .filterNot(h => ignoredHeaders.contains(h.name)) - .flatMap(h => Iterator(h.name.toString, h.value)) - .toArray - } yield (if (headers.isEmpty) rb else rb.headers(headers: _*)).build // Convert the JDK HttpResponse into a http4s Response value. // @@ -224,14 +217,12 @@ object JdkHttpClient { case HttpClient.Version.HTTP_1_1 => HttpVersion.`HTTP/1.1` case HttpClient.Version.HTTP_2 => HttpVersion.`HTTP/2` }, - entity = Entity.stream( - body - .interruptWhen(signal) - .flatMap(bs => - Stream.fromIterator(bs.iterator.asScala.map(Chunk.byteBuffer), 1) - ) - .flatMap(Stream.chunk) - ) + body = body + .interruptWhen(signal) + .flatMap(bs => + Stream.fromIterator(bs.iterator.asScala.map(Chunk.byteBuffer), 1) + ) + .flatMap(Stream.chunk) ) -> signal.set(true) } ) diff --git a/core/src/test/scala/org/http4s/jdkhttpclient/BodyLeakExample.scala b/core/src/test/scala/org/http4s/jdkhttpclient/BodyLeakExample.scala index 584f5734..7da879a8 100644 --- a/core/src/test/scala/org/http4s/jdkhttpclient/BodyLeakExample.scala +++ b/core/src/test/scala/org/http4s/jdkhttpclient/BodyLeakExample.scala @@ -24,15 +24,11 @@ import org.http4s._ import org.http4s.client._ import org.http4s.ember.server.EmberServerBuilder import org.http4s.syntax.all._ -import org.typelevel.log4cats.LoggerFactory -import org.typelevel.log4cats.noop.NoOpFactory // This is a *manual* test for the body leak fixed in #335 // Run e.g. with `bloop run core-test --args -J-Xmx200M` object BodyLeakExample extends IOApp { - implicit val loggerFactory: LoggerFactory[IO] = NoOpFactory[IO] - val app: HttpApp[IO] = Kleisli((_: Request[IO]) => IO.pure(Response[IO]().withEntity("Hello, HTTP"))) diff --git a/core/src/test/scala/org/http4s/jdkhttpclient/CompletableFutureTerminationTest.scala b/core/src/test/scala/org/http4s/jdkhttpclient/CompletableFutureTerminationTest.scala index 18ae0afa..a1b79923 100644 --- a/core/src/test/scala/org/http4s/jdkhttpclient/CompletableFutureTerminationTest.scala +++ b/core/src/test/scala/org/http4s/jdkhttpclient/CompletableFutureTerminationTest.scala @@ -26,8 +26,6 @@ import munit.CatsEffectSuite import org.http4s._ import org.http4s.ember.server._ import org.http4s.server._ -import org.typelevel.log4cats.LoggerFactory -import org.typelevel.log4cats.noop.NoOpFactory import java.net.URI import java.net.http.HttpClient @@ -40,8 +38,6 @@ import scala.concurrent.duration._ final class CompletableFutureTerminationTest extends CatsEffectSuite { import CompletableFutureTerminationTest._ - implicit val loggerFactory: LoggerFactory[IO] = NoOpFactory[IO] - private val duration: FiniteDuration = FiniteDuration(50L, TimeUnit.MILLISECONDS) @@ -176,7 +172,7 @@ object CompletableFutureTerminationTest { * ensure the server has received the request. This permit is acquired ''before'' one is * acquired from `semaphore`. */ - private def stallingServerR[F[_]: Network: LoggerFactory]( + private def stallingServerR[F[_]: Network]( semaphore: Semaphore[F], gotRequest: Semaphore[F] )(implicit F: Async[F]): Resource[F, Server] = diff --git a/core/src/test/scala/org/http4s/jdkhttpclient/JdkWSClientSpec.scala b/core/src/test/scala/org/http4s/jdkhttpclient/JdkWSClientSpec.scala index 0e49af62..d81e63de 100644 --- a/core/src/test/scala/org/http4s/jdkhttpclient/JdkWSClientSpec.scala +++ b/core/src/test/scala/org/http4s/jdkhttpclient/JdkWSClientSpec.scala @@ -29,16 +29,12 @@ import org.http4s.ember.server.EmberServerBuilder import org.http4s.implicits._ import org.http4s.websocket.WebSocketFrame import org.typelevel.ci._ -import org.typelevel.log4cats.LoggerFactory -import org.typelevel.log4cats.noop.NoOpFactory import scodec.bits.ByteVector import scala.concurrent.duration._ class JdkWSClientSpec extends CatsEffectSuite { - implicit val loggerFactory: LoggerFactory[IO] = NoOpFactory[IO] - val webSocket: IOFixture[WSClient[IO]] = ResourceSuiteLocalFixture("webSocket", Resource.eval(JdkWSClient.simple[IO])) val echoServerUri: IOFixture[Uri] = diff --git a/docs/README.md b/docs/README.md index 721bcf2e..987fccb7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -189,11 +189,6 @@ import org.http4s.dsl.io._ import org.http4s.implicits._ import org.http4s.ember.server.EmberServerBuilder import com.comcast.ip4s._ -import org.typelevel.log4cats.LoggerFactory -import org.typelevel.log4cats.noop.NoOpFactory - -implicit val loggerFactory: LoggerFactory[IO] = NoOpFactory[IO] - val echoServer = EmberServerBuilder.default[IO] .withPort(port"0") .withHttpWebSocketApp(wsb => HttpRoutes.of[IO] {