diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82279f83f3f..6c8d484d170 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,110 +29,42 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - scala: [3.0.2, 2.12.15, 2.13.8] - java: [temurin@8, temurin@11, temurin@17] - exclude: - - scala: 3.0.2 - java: temurin@11 - - scala: 3.0.2 - java: temurin@17 - - scala: 2.12.15 - java: temurin@11 - - scala: 2.12.15 - java: temurin@17 + scala: ["3.3.4", "2.12.18", "2.13.11"] + java: + - { java-version: "17", java-distribution: "temurin" } runs-on: ${{ matrix.os }} steps: - name: Checkout current branch (full) - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Download Java (temurin@8) - id: download-java-temurin-8 - if: matrix.java == 'temurin@8' - uses: typelevel/download-java@v1 + - name: Setup Java (${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}) + uses: actions/setup-java@v4.4.0 with: - distribution: temurin - java-version: 8 + distribution: ${{ matrix.java.java-distribution }} + java-version: ${{ matrix.java.java-version }} + cache: 'sbt' + - uses: sbt/setup-sbt@v1 - - name: Setup Java (temurin@8) - if: matrix.java == 'temurin@8' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 8 - jdkFile: ${{ steps.download-java-temurin-8.outputs.jdkFile }} - - - name: Download Java (temurin@11) - id: download-java-temurin-11 - if: matrix.java == 'temurin@11' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 11 - - - name: Setup Java (temurin@11) - if: matrix.java == 'temurin@11' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 11 - jdkFile: ${{ steps.download-java-temurin-11.outputs.jdkFile }} - - - name: Download Java (temurin@17) - id: download-java-temurin-17 - if: matrix.java == 'temurin@17' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 17 - - - name: Setup Java (temurin@17) - if: matrix.java == 'temurin@17' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 17 - jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} - - - name: Cache sbt - uses: actions/cache@v2 - with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - - - name: Check that workflows are up to date - run: sbt '++${{ matrix.scala }}' 'project /' githubWorkflowCheck - - - name: Check headers and formatting - if: matrix.java == 'temurin@8' - run: sbt '++${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck +# - name: Check that workflows are up to date +# run: sbt '++${{ matrix.scala }}' 'project /' githubWorkflowCheck - name: Test run: sbt '++${{ matrix.scala }}' test - name: Check binary compatibility - if: matrix.java == 'temurin@8' + if: matrix.java == 'temurin@17' run: sbt '++${{ matrix.scala }}' mimaReportBinaryIssues - name: Generate API documentation - if: matrix.java == 'temurin@8' + if: matrix.java == 'temurin@17' run: sbt '++${{ matrix.scala }}' doc - name: Check Scalafix rules - if: matrix.scala != '3.0.2' + if: matrix.scala != '3.3.4' run: sbt '++${{ matrix.scala }}' 'scalafixAll --check' - - name: Check unused compile dependencies - if: matrix.java == 'temurin@8' - 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/series/0.22') run: mkdir -p examples/ember/target blaze-client/target server/target examples/target scalafix-internal/input/target blaze-server/target scalafix-internal/tests/target scalatags/target target examples/docker/target twirl/target unidocs/target site/target laws/target tests/target tomcat-server/target prometheus-metrics/target scalafix-internal/output/target servlet/target examples/tomcat/target examples/jetty/target testing/target ember-server/target okhttp-client/target examples/blaze/target scalafix-internal/rules/target client/target blaze-core/target circe/target examples/war/target jetty-client/target ember-client/target boopickle/target play-json/target async-http-client/target core/target scala-xml/target dsl/target dropwizard-metrics/target jetty-server/target ember-core/target jawn/target bench/target project/target @@ -143,9 +75,9 @@ jobs: - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/series/0.22') - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }} + name: target-${{ matrix.os }}-${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}-${{ matrix.scala }} path: targets.tar publish: @@ -155,121 +87,70 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.13.8] - java: [temurin@8] + scala: [2.13.11] + java: + - { java-version: "17", java-distribution: "temurin" } runs-on: ${{ matrix.os }} steps: - name: Checkout current branch (full) - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Download Java (temurin@8) - id: download-java-temurin-8 - if: matrix.java == 'temurin@8' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 8 - - - name: Setup Java (temurin@8) - if: matrix.java == 'temurin@8' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 8 - jdkFile: ${{ steps.download-java-temurin-8.outputs.jdkFile }} - - - name: Download Java (temurin@11) - id: download-java-temurin-11 - if: matrix.java == 'temurin@11' - uses: typelevel/download-java@v1 + - name: Setup Java (${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}) + uses: actions/setup-java@v4.4.0 with: - distribution: temurin - java-version: 11 + distribution: ${{ matrix.java.java-distribution }} + java-version: ${{ matrix.java.java-version }} + cache: 'sbt' + - uses: sbt/setup-sbt@v1 - - name: Setup Java (temurin@11) - if: matrix.java == 'temurin@11' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 11 - jdkFile: ${{ steps.download-java-temurin-11.outputs.jdkFile }} - - - name: Download Java (temurin@17) - id: download-java-temurin-17 - if: matrix.java == 'temurin@17' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 17 - - - name: Setup Java (temurin@17) - if: matrix.java == 'temurin@17' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 17 - jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} - - - name: Cache sbt - uses: actions/cache@v2 - with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - - - name: Download target directories (3.0.2) + - name: Download target directories (3.3.4) uses: actions/download-artifact@v2 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-3.0.2 + name: target-${{ matrix.os }}-${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}-3.3.4 - - name: Inflate target directories (3.0.2) + - name: Inflate target directories (3.3.4) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.12.15) + - name: Download target directories (2.12.18) uses: actions/download-artifact@v2 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.12.15 + name: target-${{ matrix.os }}-${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}-2.12.18 - - name: Inflate target directories (2.12.15) + - name: Inflate target directories (2.12.18) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.13.8) + - name: Download target directories (2.13.11) uses: actions/download-artifact@v2 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.13.8 + name: target-${{ matrix.os }}-${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}-2.13.11 - - name: Inflate target directories (2.13.8) + - name: Inflate target directories (2.13.11) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.13.8) + - name: Download target directories (2.13.11) uses: actions/download-artifact@v2 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.13.8 + name: target-${{ matrix.os }}-${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}-2.13.11 - - name: Inflate target directories (2.13.8) + - name: Inflate target directories (2.13.11) run: | tar xf targets.tar rm targets.tar - - name: Download target directories (2.13.8) + - name: Download target directories (2.13.11) uses: actions/download-artifact@v2 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-2.13.8 + name: target-${{ matrix.os }}-${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}-2.13.11 - - name: Inflate target directories (2.13.8) + - name: Inflate target directories (2.13.11) run: | tar xf targets.tar rm targets.tar @@ -293,77 +174,26 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [3.0.2, 2.12.15, 2.13.8] - java: [temurin@8] + scala: ["3.3.4", "2.12.18", "2.13.11"] + java: + - { java-version: "17", java-distribution: "temurin" } runs-on: ${{ matrix.os }} steps: - name: Checkout current branch (full) - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Download Java (temurin@8) - id: download-java-temurin-8 - if: matrix.java == 'temurin@8' - uses: typelevel/download-java@v1 + - name: Setup Java (${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}) + uses: actions/setup-java@v4.4.0 with: - distribution: temurin - java-version: 8 - - - name: Setup Java (temurin@8) - if: matrix.java == 'temurin@8' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 8 - jdkFile: ${{ steps.download-java-temurin-8.outputs.jdkFile }} - - - name: Download Java (temurin@11) - id: download-java-temurin-11 - if: matrix.java == 'temurin@11' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 11 - - - name: Setup Java (temurin@11) - if: matrix.java == 'temurin@11' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 11 - jdkFile: ${{ steps.download-java-temurin-11.outputs.jdkFile }} - - - name: Download Java (temurin@17) - id: download-java-temurin-17 - if: matrix.java == 'temurin@17' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 17 - - - name: Setup Java (temurin@17) - if: matrix.java == 'temurin@17' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 17 - jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} - - - name: Cache sbt - uses: actions/cache@v2 - with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + distribution: ${{ matrix.java.java-distribution }} + java-version: ${{ matrix.java.java-version }} + cache: 'sbt' + - uses: sbt/setup-sbt@v1 - name: Scalafix tests - if: matrix.scala == '2.13.8' + if: matrix.scala == '2.13.11' run: | cd scalafix sbt ci @@ -373,74 +203,23 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.13.8] - java: [temurin@8] + scala: [2.13.11] + java: + - { java-version: "17", java-distribution: "temurin" } runs-on: ${{ matrix.os }} steps: - name: Checkout current branch (full) - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Download Java (temurin@8) - id: download-java-temurin-8 - if: matrix.java == 'temurin@8' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 8 - - - name: Setup Java (temurin@8) - if: matrix.java == 'temurin@8' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 8 - jdkFile: ${{ steps.download-java-temurin-8.outputs.jdkFile }} - - - name: Download Java (temurin@11) - id: download-java-temurin-11 - if: matrix.java == 'temurin@11' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 11 - - - name: Setup Java (temurin@11) - if: matrix.java == 'temurin@11' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 11 - jdkFile: ${{ steps.download-java-temurin-11.outputs.jdkFile }} - - - name: Download Java (temurin@17) - id: download-java-temurin-17 - if: matrix.java == 'temurin@17' - uses: typelevel/download-java@v1 - with: - distribution: temurin - java-version: 17 - - - name: Setup Java (temurin@17) - if: matrix.java == 'temurin@17' - uses: actions/setup-java@v2 - with: - distribution: jdkfile - java-version: 17 - jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }} - - - name: Cache sbt - uses: actions/cache@v2 + - name: Setup Java (${{ matrix.java.java-distribution }}@${{ matrix.java.java-version }}) + uses: actions/setup-java@v4.4.0 with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + distribution: ${{ matrix.java.java-distribution }} + java-version: ${{ matrix.java.java-version }} + cache: 'sbt' + - uses: sbt/setup-sbt@v1 - name: Generate site run: sbt '++${{ matrix.scala }}' site/tlSite diff --git a/blaze-core/src/main/scala/org/http4s/blazecore/util/CachingChunkWriter.scala b/blaze-core/src/main/scala/org/http4s/blazecore/util/CachingChunkWriter.scala index 94a3fdaa3b2..78ec0f9f386 100644 --- a/blaze-core/src/main/scala/org/http4s/blazecore/util/CachingChunkWriter.scala +++ b/blaze-core/src/main/scala/org/http4s/blazecore/util/CachingChunkWriter.scala @@ -25,6 +25,7 @@ import org.http4s.util.StringWriter import java.nio.ByteBuffer import java.nio.charset.StandardCharsets.ISO_8859_1 +import scala.annotation.nowarn import scala.collection.mutable.Buffer import scala.concurrent._ @@ -38,6 +39,7 @@ private[http4s] class CachingChunkWriter[F[_]]( import ChunkWriter._ private[this] var pendingHeaders: StringWriter = _ + @nowarn("msg=local var .* is never updated") private[this] var bodyBuffer: Buffer[Chunk[Byte]] = Buffer() private[this] var size: Int = 0 diff --git a/build.sbt b/build.sbt index 457fcb54d0c..737354c9045 100644 --- a/build.sbt +++ b/build.sbt @@ -231,6 +231,9 @@ lazy val server = libraryProject("server") .settings( description := "Base library for building http4s servers", startYear := Some(2014), + libraryDependencies ++= Seq( + scalacCompatAnnotation + ), mimaBinaryIssueFilters ++= Seq( ProblemFilters.exclude[IncompatibleMethTypeProblem]( "org.http4s.server.middleware.CSRF.this" @@ -581,6 +584,7 @@ lazy val jettyClient = libraryProject("jetty-client") Http4sPlugin.jettyClient, jettyHttp, jettyUtil, + scalaJava8Compat, ), ) .dependsOn(core, testing % "test->test", client % "compile;test->test") @@ -888,7 +892,7 @@ lazy val scalafixInternalRules = project .settings( startYear := Some(2021), libraryDependencies ++= Seq( - "ch.epfl.scala" %% "scalafix-core" % _root_.scalafix.sbt.BuildInfo.scalafixVersion + "ch.epfl.scala" %% "scalafix-core" % V.scalafix ).filter(_ => !tlIsScala3.value), ) @@ -931,6 +935,12 @@ lazy val scalafixInternalTests = project .settings(headerSources / excludeFilter := AllPassFilter) .disablePlugins(ScalafixPlugin) .dependsOn(scalafixInternalRules) + .settings( + dependencyOverrides ++= Seq( + "ch.epfl.scala" %% "scalafix-core" % V.scalafix, + "ch.epfl.scala" %% "scalafix-testkit" % V.scalafix % Test cross CrossVersion.full, + ) + ) def http4sProject(name: String) = Project(name, file(name)) diff --git a/jetty-client/src/main/scala/org/http4s/jetty/client/JettyClient.scala b/jetty-client/src/main/scala/org/http4s/jetty/client/JettyClient.scala index fc8dfa11b1f..a90569cbe31 100644 --- a/jetty-client/src/main/scala/org/http4s/jetty/client/JettyClient.scala +++ b/jetty-client/src/main/scala/org/http4s/jetty/client/JettyClient.scala @@ -22,9 +22,8 @@ import cats.effect._ import cats.syntax.all._ import fs2._ import org.eclipse.jetty.client.HttpClient -import org.eclipse.jetty.client.api.{Request => JettyRequest} +import org.eclipse.jetty.client.{Request => JettyRequest} import org.eclipse.jetty.http.{HttpVersion => JHttpVersion} -import org.eclipse.jetty.util.ssl.SslContextFactory import org.http4s.client.Client import org.log4s.Logger import org.log4s.getLogger @@ -34,7 +33,7 @@ object JettyClient { def allocate[F[_]]( client: HttpClient = defaultHttpClient() - )(implicit F: ConcurrentEffect[F]): F[(Client[F], F[Unit])] = { + )(implicit F: ConcurrentEffect[F], CS: ContextShift[F]): F[(Client[F], F[Unit])] = { val acquire = F .pure(client) .flatTap(client => F.delay(client.start())) @@ -46,7 +45,7 @@ object JettyClient { jReq <- F.catchNonFatal(toJettyRequest(client, req, dcp)) rl <- ResponseListener(cb) _ <- F.delay(jReq.send(rl)) - _ <- dcp.write(req) + _ <- dcp.write(req.body) } yield ()).recover { case e => cb(Left(e)) } @@ -62,19 +61,18 @@ object JettyClient { acquire.map((_, dispose)) } - def resource[F[_]](client: HttpClient = defaultHttpClient())(implicit - F: ConcurrentEffect[F] + def resource[F[_]: ConcurrentEffect: ContextShift]( + client: HttpClient = defaultHttpClient() ): Resource[F, Client[F]] = Resource(allocate[F](client)) - def stream[F[_]](client: HttpClient = defaultHttpClient())(implicit - F: ConcurrentEffect[F] + def stream[F[_]: ConcurrentEffect: ContextShift]( + client: HttpClient = defaultHttpClient() ): Stream[F, Client[F]] = Stream.resource(resource(client)) def defaultHttpClient(): HttpClient = { - val sslCtxFactory = new SslContextFactory.Client(); - val c = new HttpClient(sslCtxFactory) + val c = new HttpClient() c.setFollowRedirects(false) c.setDefaultRequestContentType(null) c @@ -96,9 +94,10 @@ object JettyClient { case _ => JHttpVersion.HTTP_1_1 } ) - - for (h <- request.headers.headers if h.isNameValid) - jReq.header(h.name.toString, h.value) - jReq.content(dcp) + jReq.headers { jettyHeaders => + for (h <- request.headers.headers if h.isNameValid) + jettyHeaders.add(h.name.toString, h.value) + } + jReq.body(dcp) } } diff --git a/jetty-client/src/main/scala/org/http4s/jetty/client/ResponseListener.scala b/jetty-client/src/main/scala/org/http4s/jetty/client/ResponseListener.scala index 82d8774aadc..86f52237c12 100644 --- a/jetty-client/src/main/scala/org/http4s/jetty/client/ResponseListener.scala +++ b/jetty-client/src/main/scala/org/http4s/jetty/client/ResponseListener.scala @@ -24,11 +24,10 @@ import cats.syntax.all._ import fs2.Stream._ import fs2._ import fs2.concurrent.Queue -import org.eclipse.jetty.client.api.Result -import org.eclipse.jetty.client.api.{Response => JettyResponse} +import org.eclipse.jetty.client.Result +import org.eclipse.jetty.client.{Response => JettyResponse} import org.eclipse.jetty.http.HttpFields import org.eclipse.jetty.http.{HttpVersion => JHttpVersion} -import org.eclipse.jetty.util.{Callback => JettyCallback} import org.http4s.internal.CollectionCompat.CollectionConverters._ import org.http4s.internal.invokeCallback import org.http4s.internal.loggingAsyncCallback @@ -40,8 +39,8 @@ import java.nio.ByteBuffer private[jetty] final case class ResponseListener[F[_]]( queue: Queue[F, Item], cb: Callback[Resource[F, Response[F]]], -)(implicit F: ConcurrentEffect[F]) - extends JettyResponse.Listener.Adapter { +)(implicit F: ConcurrentEffect[F], CS: ContextShift[F]) + extends JettyResponse.Listener { import ResponseListener.logger /* Needed to properly propagate client errors */ @@ -87,14 +86,13 @@ private[jetty] final case class ResponseListener[F[_]]( override def onContent( response: JettyResponse, content: ByteBuffer, - callback: JettyCallback, ): Unit = { val copy = ByteBuffer.allocate(content.remaining()) copy.put(content).flip() enqueue(Item.Buf(copy)) { - case Right(_) => IO(callback.succeeded()) + case Right(_) => IO.unit case Left(e) => - IO(logger.error(e)("Error in asynchronous callback")) >> IO(callback.failed(e)) + IO(logger.error(e)("Error in asynchronous callback")) } } @@ -110,11 +108,21 @@ private[jetty] final case class ResponseListener[F[_]]( // (the request might complete after the response has been entirely received) override def onComplete(result: Result): Unit = () - private def abort(t: Throwable, response: JettyResponse): Unit = - if (!response.abort(t)) // this also aborts the request - logger.error(t)("Failed to abort the response") - else - closeStream() + private def abort(t: Throwable, response: JettyResponse): Unit = { + import scala.compat.java8.FutureConverters._ + + Async + .fromFuture(F.delay(response.abort(t).toScala)) + .runAsync { attempt => + loggingAsyncCallback(logger)(attempt.map { aborted => + if (!aborted) + logger.error(t)("Failed to abort the response") + else + closeStream() + }) + } + .unsafeRunSync() + } private def closeStream(): Unit = enqueue(Item.Done)(loggingAsyncCallback(logger)) @@ -135,9 +143,9 @@ private[jetty] object ResponseListener { private val logger = getLogger - def apply[F[_]]( + def apply[F[_]: ConcurrentEffect: ContextShift]( cb: Callback[Resource[F, Response[F]]] - )(implicit F: ConcurrentEffect[F]): F[ResponseListener[F]] = + ): F[ResponseListener[F]] = Queue .synchronous[F, Item] .map(q => ResponseListener(q, cb)) diff --git a/jetty-client/src/main/scala/org/http4s/jetty/client/StreamRequestContentProvider.scala b/jetty-client/src/main/scala/org/http4s/jetty/client/StreamRequestContentProvider.scala index 053313bb460..1d8ae579ffd 100644 --- a/jetty-client/src/main/scala/org/http4s/jetty/client/StreamRequestContentProvider.scala +++ b/jetty-client/src/main/scala/org/http4s/jetty/client/StreamRequestContentProvider.scala @@ -23,18 +23,18 @@ import cats.effect.concurrent.Semaphore import cats.effect.implicits._ import cats.syntax.all._ import fs2._ -import org.eclipse.jetty.client.util.DeferredContentProvider +import org.eclipse.jetty.client.AsyncRequestContent import org.eclipse.jetty.util.{Callback => JettyCallback} import org.http4s.internal.loggingAsyncCallback import org.log4s.getLogger private[jetty] final case class StreamRequestContentProvider[F[_]](s: Semaphore[F])(implicit F: Effect[F] -) extends DeferredContentProvider { +) extends AsyncRequestContent { import StreamRequestContentProvider.logger - def write(req: Request[F]): F[Unit] = - req.body.chunks + def write(body: Stream[F, Byte]): F[Unit] = + body.chunks .through(pipe) .compile .drain @@ -43,13 +43,11 @@ private[jetty] final case class StreamRequestContentProvider[F[_]](s: Semaphore[ private val pipe: Pipe[F, Chunk[Byte], Unit] = _.evalMap { c => write(c) - .ensure(new Exception("something terrible has happened"))(res => res) - .map(_ => ()) } - private def write(chunk: Chunk[Byte]): F[Boolean] = + private def write(chunk: Chunk[Byte]): F[Unit] = s.acquire - .map(_ => super.offer(chunk.toByteBuffer, callback)) + .map(_ => super.write(chunk.toByteBuffer, callback)) private val callback: JettyCallback = new JettyCallback { override def succeeded(): Unit = @@ -61,5 +59,6 @@ private[jetty] object StreamRequestContentProvider { private val logger = getLogger def apply[F[_]]()(implicit F: ConcurrentEffect[F]): F[StreamRequestContentProvider[F]] = - Semaphore[F](1).map(StreamRequestContentProvider(_)) + Semaphore[F](1).map(new StreamRequestContentProvider(_)) + } diff --git a/jetty-server/src/main/scala/org/http4s/jetty/server/JettyBuilder.scala b/jetty-server/src/main/scala/org/http4s/jetty/server/JettyBuilder.scala index 40cc8f768de..804e29dce61 100644 --- a/jetty-server/src/main/scala/org/http4s/jetty/server/JettyBuilder.scala +++ b/jetty-server/src/main/scala/org/http4s/jetty/server/JettyBuilder.scala @@ -20,15 +20,15 @@ package server import cats.effect._ import cats.syntax.all._ +import org.eclipse.jetty.ee8.servlet.FilterHolder +import org.eclipse.jetty.ee8.servlet.ServletContextHandler +import org.eclipse.jetty.ee8.servlet.ServletHolder import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory import org.eclipse.jetty.server.HttpConfiguration import org.eclipse.jetty.server.HttpConnectionFactory import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.handler.StatisticsHandler import org.eclipse.jetty.server.{Server => JServer} -import org.eclipse.jetty.servlet.FilterHolder -import org.eclipse.jetty.servlet.ServletContextHandler -import org.eclipse.jetty.servlet.ServletHolder import org.eclipse.jetty.util.ssl.SslContextFactory import org.eclipse.jetty.util.thread.ThreadPool import org.http4s.jetty.server.JettyBuilder._ diff --git a/jetty-server/src/main/scala/org/http4s/jetty/server/JettyLifeCycle.scala b/jetty-server/src/main/scala/org/http4s/jetty/server/JettyLifeCycle.scala index 59a76836b4c..280243959b5 100644 --- a/jetty-server/src/main/scala/org/http4s/jetty/server/JettyLifeCycle.scala +++ b/jetty-server/src/main/scala/org/http4s/jetty/server/JettyLifeCycle.scala @@ -55,36 +55,40 @@ private[jetty] object JettyLifeCycle { * internally, e.g. due to some internal error occurring. */ private[this] def stopLifeCycle[F[_]](lifeCycle: LifeCycle)(implicit F: Async[F]): F[Unit] = - F.async[Unit] { cb => - lifeCycle.addLifeCycleListener( - new LifeCycle.Listener { - override def lifeCycleStopped(a: LifeCycle): Unit = - cb(Right(())) - override def lifeCycleFailure(a: LifeCycle, error: Throwable): Unit = - cb(Left(error)) - } - ) + F.asyncF[Unit] { cb => + F.defer { + val listener = + new LifeCycle.Listener { + override def lifeCycleStopped(a: LifeCycle): Unit = + cb(Right(())) + + override def lifeCycleFailure(a: LifeCycle, error: Throwable): Unit = + cb(Left(error)) + } + lifeCycle.addEventListener(listener) - // In the general case, it is not sufficient to merely call stop(). For - // example, the concrete implementation of stop() for the canonical - // Jetty Server instance will shortcut to a `return` call taking no - // action if the server is "stopping". This method _can't_ return until - // we are _actually stopped_, so we have to check three different states - // here. + // In the general case, it is not sufficient to merely call stop(). For + // example, the concrete implementation of stop() for the canonical + // Jetty Server instance will shortcut to a `return` call taking no + // action if the server is "stopping". This method _can't_ return until + // we are _actually stopped_, so we have to check three different states + // here. - if (lifeCycle.isStopped) { - // If the first case, we are already stopped, so our listener won't be - // called and we just return. - cb(Right(())) - } else if (lifeCycle.isStopping()) { - // If it is stopping, we need to wait for our listener to get invoked. - () - } else { - // If it is neither stopped nor stopping, we need to request a stop - // and then wait for the event. It is imperative that we add the - // listener beforehand here. Otherwise we have some very annoying race - // conditions. - lifeCycle.stop() + if (lifeCycle.isStopped) { + // If the first case, we are already stopped, so our listener won't be + // called and we just return. + cb(Right(())) + } else if (lifeCycle.isStopping()) { + // If it is stopping, we need to wait for our listener to get invoked. + () + } else { + // If it is neither stopped nor stopping, we need to request a stop + // and then wait for the event. It is imperative that we add the + // listener beforehand here. Otherwise we have some very annoying race + // conditions. + lifeCycle.stop() + } + F.delay(lifeCycle.removeEventListener(listener)).void } } @@ -95,51 +99,55 @@ private[jetty] object JettyLifeCycle { * (or starting) this will fail. */ private[this] def startLifeCycle[F[_]](lifeCycle: LifeCycle)(implicit F: Async[F]): F[Unit] = - F.async[Unit] { cb => - lifeCycle.addLifeCycleListener( - new LifeCycle.Listener { - override def lifeCycleStarted(a: LifeCycle): Unit = - cb(Right(())) - override def lifeCycleFailure(a: LifeCycle, error: Throwable): Unit = - cb(Left(error)) - } - ) + F.asyncF[Unit] { cb => + F.defer { + val listener = + new LifeCycle.Listener { + override def lifeCycleStarted(a: LifeCycle): Unit = + cb(Right(())) + + override def lifeCycleFailure(a: LifeCycle, error: Throwable): Unit = + cb(Left(error)) + } + lifeCycle.addEventListener(listener) - // Sanity check to ensure the LifeCycle component is not already - // started. A couple of notes here. - // - // - There is _always_ going to be a small chance of a race condition - // here in the final branch where we invoke `lifeCycle.start()` _if_ - // something else has a reference to the `LifeCycle` - // value. Thankfully, unlike the stopLifeCycle function, this is - // completely in the control of the caller. As long as the caller - // doesn't leak the reference (or call .start() themselves) nothing - // internally to Jetty should ever invoke .start(). - // - Jetty components allow for reuse in many cases, unless the - // .destroy() method is invoked (and the particular type implements - // `Destroyable`, it's not part of `LifeCycle`). Jetty uses this for - // "soft" resets of the `LifeCycle` component. Thus it is possible - // that this `LifeCycle` component has been started before, though I - // don't recommend this and we don't (at this time) do that in the - // http4s codebase. - if (lifeCycle.isStarted) { - cb( - Left( - new IllegalStateException( - "Attempting to start Jetty LifeCycle component, but it is already started." + // Sanity check to ensure the LifeCycle component is not already + // started. A couple of notes here. + // + // - There is _always_ going to be a small chance of a race condition + // here in the final branch where we invoke `lifeCycle.start()` _if_ + // something else has a reference to the `LifeCycle` + // value. Thankfully, unlike the stopLifeCycle function, this is + // completely in the control of the caller. As long as the caller + // doesn't leak the reference (or call .start() themselves) nothing + // internally to Jetty should ever invoke .start(). + // - Jetty components allow for reuse in many cases, unless the + // .destroy() method is invoked (and the particular type implements + // `Destroyable`, it's not part of `LifeCycle`). Jetty uses this for + // "soft" resets of the `LifeCycle` component. Thus it is possible + // that this `LifeCycle` component has been started before, though I + // don't recommend this and we don't (at this time) do that in the + // http4s codebase. + if (lifeCycle.isStarted) { + cb( + Left( + new IllegalStateException( + "Attempting to start Jetty LifeCycle component, but it is already started." + ) ) ) - ) - } else if (lifeCycle.isStarting) { - cb( - Left( - new IllegalStateException( - "Attempting to start Jetty LifeCycle component, but it is already starting." + } else if (lifeCycle.isStarting) { + cb( + Left( + new IllegalStateException( + "Attempting to start Jetty LifeCycle component, but it is already starting." + ) ) ) - ) - } else { - lifeCycle.start() + } else { + lifeCycle.start() + } + F.delay(lifeCycle.removeEventListener(listener)).void } } } diff --git a/jetty-server/src/test/scala/org/http4s/jetty/server/Issue454.scala b/jetty-server/src/test/scala/org/http4s/jetty/server/Issue454.scala index ebd2fc6f742..106997d690f 100644 --- a/jetty-server/src/test/scala/org/http4s/jetty/server/Issue454.scala +++ b/jetty-server/src/test/scala/org/http4s/jetty/server/Issue454.scala @@ -20,12 +20,12 @@ package server import cats.effect.ContextShift import cats.effect.IO +import org.eclipse.jetty.ee8.servlet.ServletContextHandler +import org.eclipse.jetty.ee8.servlet.ServletHolder import org.eclipse.jetty.server.HttpConfiguration import org.eclipse.jetty.server.HttpConnectionFactory import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.ServerConnector -import org.eclipse.jetty.servlet.ServletContextHandler -import org.eclipse.jetty.servlet.ServletHolder import org.http4s.dsl.io._ import org.http4s.server.DefaultServiceErrorHandler import org.http4s.servlet.AsyncHttp4sServlet diff --git a/project/Http4sPlugin.scala b/project/Http4sPlugin.scala index ac423aa73d1..aa49a533c21 100644 --- a/project/Http4sPlugin.scala +++ b/project/Http4sPlugin.scala @@ -24,9 +24,9 @@ object Http4sPlugin extends AutoPlugin { override def requires = Http4sOrgPlugin - val scala_213 = "2.13.8" - val scala_212 = "2.12.15" - val scala_3 = "3.0.2" + val scala_213 = "2.13.11" + val scala_212 = "2.12.18" + val scala_3 = "3.3.4" override lazy val globalSettings = Seq( isCi := githubIsWorkflowBuild.value @@ -154,7 +154,8 @@ object Http4sPlugin extends AutoPlugin { val javaWebSocket = "1.5.3" val jawn = "1.3.2" val jawnFs2 = "1.2.1" - val jetty = "9.4.46.v20220331" +// val jetty = "9.4.46.v20220331" + val jetty = "12.0.15" val keypool = "0.3.6" val literally = "1.0.2" val logback = "1.2.6" @@ -172,15 +173,19 @@ object Http4sPlugin extends AutoPlugin { val quasiquotes = "2.1.0" val scalacheck = "1.15.4" val scalacheckEffect = "1.0.3" + val scalacCompatAnnotation = "0.1.4" + val scalaJava8Compat = "1.0.2" val scalatags = "0.10.0" val scalaXml = "2.1.0" val scodecBits = "1.1.29" - val servlet = "3.1.0" + val servlet = "4.0.1" val slf4j = "1.7.36" val tomcat = "9.0.62" val treehugger = "0.4.4" val twirl = "1.4.2" val vault = "2.2.1" + + val scalafix = "0.11.0" } lazy val asyncHttpClient = "org.asynchttpclient" % "async-http-client" % V.asyncHttpClient @@ -218,10 +223,10 @@ object Http4sPlugin extends AutoPlugin { lazy val jawnPlay = "org.typelevel" %% "jawn-play" % V.jawn lazy val jettyClient = "org.eclipse.jetty" % "jetty-client" % V.jetty lazy val jettyHttp = "org.eclipse.jetty" % "jetty-http" % V.jetty - lazy val jettyHttp2Server = "org.eclipse.jetty.http2" % "http2-server" % V.jetty - lazy val jettyRunner = "org.eclipse.jetty" % "jetty-runner" % V.jetty + lazy val jettyHttp2Server = "org.eclipse.jetty.http2" % "jetty-http2-server" % V.jetty lazy val jettyServer = "org.eclipse.jetty" % "jetty-server" % V.jetty - lazy val jettyServlet = "org.eclipse.jetty" % "jetty-servlet" % V.jetty + lazy val jettyRunner = "org.eclipse.jetty.ee8" % "jetty-ee8-runner" % V.jetty + lazy val jettyServlet = "org.eclipse.jetty.ee8" % "jetty-ee8-servlet" % V.jetty lazy val jettyUtil = "org.eclipse.jetty" % "jetty-util" % V.jetty lazy val keypool = "org.typelevel" %% "keypool" % V.keypool lazy val literally = "org.typelevel" %% "literally" % V.literally @@ -246,7 +251,10 @@ object Http4sPlugin extends AutoPlugin { lazy val scalacheck = "org.scalacheck" %% "scalacheck" % V.scalacheck lazy val scalacheckEffect = "org.typelevel" %% "scalacheck-effect" % V.scalacheckEffect lazy val scalacheckEffectMunit = "org.typelevel" %% "scalacheck-effect-munit" % V.scalacheckEffect + lazy val scalacCompatAnnotation = + "org.typelevel" %% "scalac-compat-annotation" % V.scalacCompatAnnotation def scalaReflect(sv: String) = "org.scala-lang" % "scala-reflect" % sv + lazy val scalaJava8Compat = "org.scala-lang.modules" %% "scala-java8-compat" % V.scalaJava8Compat lazy val scalatagsApi = "com.lihaoyi" %% "scalatags" % V.scalatags lazy val scalaXml = "org.scala-lang.modules" %% "scala-xml" % V.scalaXml lazy val scodecBits = "org.scodec" %% "scodec-bits" % V.scodecBits diff --git a/project/plugins.sbt b/project/plugins.sbt index 4367489bbff..557df6af6b9 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,9 +1,11 @@ libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.11" +addDependencyTreePlugin + // https://github.com/coursier/coursier/issues/450 classpathTypes += "maven-plugin" -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.0") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.0") addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.4") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") diff --git a/scalafix-internal/rules/src/main/scala-2/fix/GeneralLinters.scala b/scalafix-internal/rules/src/main/scala-2/fix/GeneralLinters.scala index ac0dd2d2e4c..a1d386c6224 100644 --- a/scalafix-internal/rules/src/main/scala-2/fix/GeneralLinters.scala +++ b/scalafix-internal/rules/src/main/scala-2/fix/GeneralLinters.scala @@ -40,7 +40,7 @@ class GeneralLinters extends SemanticRule("Http4sGeneralLinters") { def noNonFinalCaseClass(implicit doc: SemanticDocument) = doc.tree.collect { - case c @ Defn.Class(mods, _, _, _, _) + case c @ Defn.Class.After_4_6_0(mods, _, _, _, _) if mods.exists(_.is[Mod.Case]) && !mods.exists(mod => (mod.is[Mod.Final] | mod.is[Mod.Sealed] | mod.is[Mod.Private]) ) && !c.isDescendentOf[Defn.Def] && !c.isDescendentOf[Defn.Val] => @@ -49,17 +49,17 @@ class GeneralLinters extends SemanticRule("Http4sGeneralLinters") { def leakingSealedHierarchy(implicit doc: SemanticDocument) = { def doCheck(t: Tree, mods: List[Mod], inits: List[Init]): Patch = inits.collect { - case Init(typ, _, _) if typ.symbol.info.exists(_.isSealed) => + case Init.After_4_6_0(typ, _, _) if typ.symbol.info.exists(_.isSealed) => if (!mods.exists(mod => (mod.is[Mod.Final] | mod.is[Mod.Sealed] | mod.is[Mod.Private]))) { Patch.lint(LeakingSealedHierarchy(t)) } else Patch.empty }.asPatch doc.tree.collect { - case t @ Defn.Class(mods, _, _, _, Template(_, inits, _, _)) + case t @ Defn.Class.After_4_6_0(mods, _, _, _, Template.Initial(_, inits, _, _)) if !t.isDescendentOf[Defn.Def] && !t.isDescendentOf[Defn.Val] => doCheck(t, mods, inits) - case t @ Defn.Trait(mods, _, _, _, Template(_, inits, _, _)) + case t @ Defn.Trait.After_4_6_0(mods, _, _, _, Template.Initial(_, inits, _, _)) if !t.isDescendentOf[Defn.Def] && !t.isDescendentOf[Defn.Val] => doCheck(t, mods, inits) }.asPatch @@ -67,7 +67,7 @@ class GeneralLinters extends SemanticRule("Http4sGeneralLinters") { def nonValidatingCopyConstructor(implicit doc: SemanticDocument) = doc.tree.collect { - case c @ Defn.Class(mods, _, _, Ctor.Primary(ctorMods, _, _), _) + case c @ Defn.Class.After_4_6_0(mods, _, _, Ctor.Primary.After_4_6_0(ctorMods, _, _), _) if mods.exists(_.is[Mod.Case]) && ctorMods .exists(_.is[Mod.Private]) && !mods.exists(_.is[Mod.Abstract]) => Patch.lint(NonValidatingCopyConstructor(c)) diff --git a/scalafix-internal/rules/src/main/scala-2/fix/UseLiteralsSyntax.scala b/scalafix-internal/rules/src/main/scala-2/fix/UseLiteralsSyntax.scala index f6c7fc900c7..4ecf8a750aa 100644 --- a/scalafix-internal/rules/src/main/scala-2/fix/UseLiteralsSyntax.scala +++ b/scalafix-internal/rules/src/main/scala-2/fix/UseLiteralsSyntax.scala @@ -23,12 +23,12 @@ import scala.meta._ class UseLiteralsSyntax extends SemanticRule("Http4sUseLiteralsSyntax") { override def fix(implicit doc: SemanticDocument): Patch = doc.tree.collect { - case t @ Term.Apply( + case t @ Term.Apply.Initial( Uri_unsafeFromString_M(_), List(lit @ Lit.String(_)) ) => Patch.replaceTree(t, s"uri$lit") + importLiteralsIfNeeded - case t @ Term.Apply( + case t @ Term.Apply.Initial( Path_unsafeFromString_M(_), List(lit @ Lit.String(_)) ) => diff --git a/scalafix-internal/tests/src/test/scala-2/fix/RuleSuite.scala b/scalafix-internal/tests/src/test/scala-2/fix/RuleSuite.scala index 5754bae5bb8..63e5fad6c7d 100644 --- a/scalafix-internal/tests/src/test/scala-2/fix/RuleSuite.scala +++ b/scalafix-internal/tests/src/test/scala-2/fix/RuleSuite.scala @@ -1,8 +1,8 @@ package fix -import org.scalatest.FunSpecLike +import org.scalatest.funspec.AnyFunSpecLike import scalafix.testkit.AbstractSemanticRuleSuite -class RuleSuite extends AbstractSemanticRuleSuite with FunSpecLike { +class RuleSuite extends AbstractSemanticRuleSuite with AnyFunSpecLike { runAllTests() } diff --git a/scalafix/build.sbt b/scalafix/build.sbt index 9923875f285..6c65db2779d 100644 --- a/scalafix/build.sbt +++ b/scalafix/build.sbt @@ -1,4 +1,5 @@ lazy val V = _root_.scalafix.sbt.BuildInfo +lazy val scalafixVersion = V.scalafixVersion lazy val outputVersion = "0.22.7" inThisBuild( List( @@ -40,7 +41,7 @@ skip in publish := true lazy val rules = project.settings( moduleName := "http4s-scalafix", - libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % V.scalafixVersion + libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % scalafixVersion, ) .settings(scalafixSettings) @@ -75,7 +76,7 @@ lazy val output = project.settings( lazy val tests = project .settings( skip in publish := true, - libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % V.scalafixVersion % Test cross CrossVersion.full, + libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % scalafixVersion % Test cross CrossVersion.full, Compile / compile := (Compile / compile).dependsOn(input / Compile / compile).value, scalafixTestkitOutputSourceDirectories := diff --git a/scalafix/project/build.properties b/scalafix/project/build.properties index 0b2e09c5ac9..c8fcab543a9 100644 --- a/scalafix/project/build.properties +++ b/scalafix/project/build.properties @@ -1 +1 @@ -sbt.version=1.4.7 +sbt.version=1.6.2 diff --git a/scalafix/project/plugins.sbt b/scalafix/project/plugins.sbt index 164753b945c..853f6cf2682 100644 --- a/scalafix/project/plugins.sbt +++ b/scalafix/project/plugins.sbt @@ -1,5 +1,5 @@ resolvers += Resolver.sonatypeRepo("releases") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.0") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.0") addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.0.0") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1") diff --git a/scalafix/tests/src/test/scala/fix/RuleSuite.scala b/scalafix/tests/src/test/scala/fix/RuleSuite.scala index 04566e69440..f396eec882b 100644 --- a/scalafix/tests/src/test/scala/fix/RuleSuite.scala +++ b/scalafix/tests/src/test/scala/fix/RuleSuite.scala @@ -16,9 +16,9 @@ package fix -import org.scalatest.FunSpecLike +import org.scalatest.funspec.AnyFunSpecLike import scalafix.testkit.AbstractSemanticRuleSuite -class RuleSuite extends AbstractSemanticRuleSuite with FunSpecLike { +class RuleSuite extends AbstractSemanticRuleSuite with AnyFunSpecLike { runAllTests() } diff --git a/server/src/main/scala/org/http4s/server/middleware/CORS.scala b/server/src/main/scala/org/http4s/server/middleware/CORS.scala index f629b4728b9..b21d85cd64d 100644 --- a/server/src/main/scala/org/http4s/server/middleware/CORS.scala +++ b/server/src/main/scala/org/http4s/server/middleware/CORS.scala @@ -29,8 +29,8 @@ import org.http4s.headers._ import org.http4s.syntax.header._ import org.log4s.getLogger import org.typelevel.ci._ +import org.typelevel.scalaccompat.annotation._ -import scala.annotation.nowarn import scala.concurrent.duration._ import scala.util.hashing.MurmurHash3 @@ -195,7 +195,7 @@ object CORS { "Depends on a deficient `CORSConfig`. See https://github.com/http4s/http4s/security/advisories/GHSA-52cf-226f-rhr6. If config.anyOrigin is true and config.allowCredentials is true, then the `Access-Control-Allow-Credentials` header will be suppressed starting with 0.22.3.", "0.21.27", ) - @nowarn("cat=deprecation") + @nowarn212("cat=deprecation") def apply[F[_], G[_]](http: Http[F, G], config: CORSConfig = CORSConfig.default)(implicit F: Applicative[F] ): Http[F, G] = { diff --git a/servlet/src/test/scala/org/http4s/servlet/TestEclipseServer.scala b/servlet/src/test/scala/org/http4s/servlet/TestEclipseServer.scala index 0f86d2b9c88..82756565f3d 100644 --- a/servlet/src/test/scala/org/http4s/servlet/TestEclipseServer.scala +++ b/servlet/src/test/scala/org/http4s/servlet/TestEclipseServer.scala @@ -18,12 +18,12 @@ package org.http4s.servlet import cats.effect.IO import cats.effect.Resource +import org.eclipse.jetty.ee8.servlet.ServletContextHandler +import org.eclipse.jetty.ee8.servlet.ServletHolder import org.eclipse.jetty.server.HttpConfiguration import org.eclipse.jetty.server.HttpConnectionFactory import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.{Server => EclipseServer} -import org.eclipse.jetty.servlet.ServletContextHandler -import org.eclipse.jetty.servlet.ServletHolder import javax.servlet.Servlet