From e7ed7720a3ae4389c4b755b476e94bafb26ba694 Mon Sep 17 00:00:00 2001 From: adamw Date: Tue, 14 Mar 2023 10:11:34 +0100 Subject: [PATCH] Reformat using scalafmt --- .scalafmt.conf | 5 +- .../client4/akkahttp/AkkaHttpBackend.scala | 21 +++--- .../client4/akkahttp/AkkaHttpClient.scala | 3 +- .../sttp/client4/akkahttp/BodyFromAkka.scala | 21 +++--- .../sttp/client4/akkahttp/BodyToAkka.scala | 23 +++---- .../scala/sttp/client4/akkahttp/Util.scala | 9 +-- .../armeria/cats/ArmeriaCatsBackend.scala | 8 +-- .../armeria/cats/ArmeriaCatsBackend.scala | 8 +-- .../armeria/fs2/ArmeriaFs2Backend.scala | 18 ++---- .../armeria/fs2/ArmeriaFs2Backend.scala | 2 +- .../armeria/monix/ArmeriaMonixBackend.scala | 4 +- .../armeria/scalaz/ArmeriaScalazBackend.scala | 2 +- .../armeria/AbstractArmeriaBackend.scala | 39 ++++++----- .../client4/armeria/ArmeriaWebClient.scala | 10 ++- .../armeria/BodyFromStreamMessage.scala | 30 ++++----- .../armeria/StreamMessageAggregator.scala | 15 ++--- .../armeria/future/ArmeriaFutureBackend.scala | 2 +- .../armeria/zio/ArmeriaZioBackend.scala | 7 +- .../armeria/zio/ArmeriaZioBackend.scala | 7 +- .../cats/AsyncHttpClientCatsBackend.scala | 33 ++++++---- .../cats/AsyncHttpClientCatsBackend.scala | 33 ++++++---- .../fs2/AsyncHttpClientFs2Backend.scala | 42 ++++++------ .../fs2/AsyncHttpClientFs2Backend.scala | 42 ++++++------ .../future/AsyncHttpClientFutureBackend.scala | 20 ++++-- .../monix/AsyncHttpClientMonixBackend.scala | 36 +++++------ .../scalaz/AsyncHttpClientScalazBackend.scala | 20 ++++-- .../AsyncHttpClientBackend.scala | 59 +++++++---------- .../client4/asynchttpclient/BodyFromAHC.scala | 23 +++---- .../client4/asynchttpclient/BodyToAHC.scala | 5 +- .../asynchttpclient/WebSocketImpl.scala | 21 ++---- .../client4/asynchttpclient/reactive.scala | 15 ++--- .../zio/AsyncHttpClientZioBackend.scala | 10 +-- .../zio/AsyncHttpClientZioBackend.scala | 52 +++++++-------- .../scala/sttp/client4/BackendOptions.scala | 9 +-- .../main/scala/sttp/client4/ResponseAs.scala | 3 +- .../main/scala/sttp/client4/RetryWhen.scala | 3 +- .../sttp/client4/SpecifyAuthScheme.scala | 3 +- .../src/main/scala/sttp/client4/SttpApi.scala | 6 +- .../sttp/client4/SttpClientException.scala | 3 +- .../src/main/scala/sttp/client4/backend.scala | 4 +- .../client4/internal/BodyFromResponseAs.scala | 10 ++- .../internal/DigestAuthenticator.scala | 61 ++++++++---------- .../client4/internal/ToCurlConverter.scala | 18 ++---- .../client4/internal/ToRfc2616Converter.scala | 6 +- .../internal/WwwAuthHeaderParser.scala | 20 ++---- .../internal/ws/FutureSimpleQueue.scala | 5 +- .../sttp/client4/internal/ws/SyncQueue.scala | 3 +- .../client4/listener/ListenerBackend.scala | 9 ++- .../main/scala/sttp/client4/logging/Log.scala | 39 ++++++----- .../client4/logging/LoggingListener.scala | 9 +-- .../LoggingWithResponseBodyBackend.scala | 6 +- .../scala/sttp/client4/monad/MapEffect.scala | 3 +- .../src/main/scala/sttp/client4/request.scala | 28 ++++---- .../scala/sttp/client4/requestBuilder.scala | 7 +- .../client4/testing/AbstractBackendStub.scala | 25 ++++---- .../testing/AtomicCyclicIterator.scala | 3 +- .../client4/testing/RecordingBackend.scala | 6 +- .../client4/testing/StreamBackendStub.scala | 6 +- .../client4/testing/SyncBackendStub.scala | 4 +- .../testing/WebSocketBackendStub.scala | 6 +- .../testing/WebSocketStreamBackendStub.scala | 10 +-- .../client4/wrappers/DelegateBackend.scala | 4 +- .../DigestAuthenticationBackend.scala | 22 +++++-- .../wrappers/FollowRedirectsBackend.scala | 20 +++--- .../wrappers/MappedEffectBackend.scala | 10 ++- .../wrappers/ResolveRelativeUrisBackend.scala | 15 ++++- .../scalajs/sttp/client4/WebSocketImpl.scala | 2 +- .../client4/fetch/AbstractFetchBackend.scala | 27 ++++---- .../sttp/client4/fetch/FetchBackend.scala | 3 +- .../sttp/client4/DefaultFutureBackend.scala | 3 +- .../httpclient/HttpClientAsyncBackend.scala | 13 ++-- .../httpclient/HttpClientBackend.scala | 6 +- .../httpclient/HttpClientFutureBackend.scala | 4 +- .../httpclient/HttpClientSyncBackend.scala | 12 ++-- .../HttpURLConnectionBackend.scala | 64 ++++++++++++------- .../sttp/client4/internal/FileHelpers.scala | 17 ++--- .../httpclient/BodyFromHttpClient.scala | 6 +- .../httpclient/BodyToHttpClient.scala | 11 ++-- .../DelegatingWebSocketListener.scala | 15 ++--- .../internal/httpclient/FutureSequencer.scala | 2 +- .../InputStreamBodyFromHttpClient.scala | 3 +- .../internal/httpclient/WebSocketImpl.scala | 12 ++-- .../sttp/client4/FileHelpers.scala | 7 +- .../client4/curl/AbstractCurlBackend.scala | 18 +++--- .../sttp/client4/curl/internal/CurlApi.scala | 39 ++++------- .../httpclient/cats/CatsSimpleQueue.scala | 3 +- .../cats/HttpClientCatsBackend.scala | 21 +++--- .../client4/impl/fs2/Fs2SimpleQueue.scala | 3 +- .../sttp/client4/impl/fs2/Fs2WebSockets.scala | 3 +- .../fs2/Fs2BodyFromHttpClient.scala | 9 +-- .../httpclient/fs2/HttpClientFs2Backend.scala | 19 +++--- .../sttp/client4/impl/fs2/Fs2WebSockets.scala | 3 +- .../Fs2SimpleQueue.scala | 3 +- .../fs2/Fs2BodyFromHttpClient.scala | 9 +-- .../httpclient/fs2/HttpClientFs2Backend.scala | 20 +++--- .../client4/impl/fs2/Fs2SimpleQueue.scala | 3 +- .../impl/monix/MonixServerSentEvents.scala | 3 +- .../client4/impl/monix/MonixSimpleQueue.scala | 5 +- .../client4/impl/monix/MonixWebSockets.scala | 3 +- .../impl/monix/FetchMonixBackend.scala | 6 +- .../monix/HttpClientMonixBackend.scala | 17 +++-- .../monix/MonixBodyFromHttpClient.scala | 6 +- .../client4/impl/zio/ZioSimpleQueue.scala | 6 +- .../client4/impl/zio/FetchZioBackend.scala | 6 +- .../httpclient/zio/HttpClientZioBackend.scala | 11 ++-- .../zio/ZioBodyFromHttpClient.scala | 7 +- .../client4/impl/zio/ZioSimpleQueue.scala | 6 +- .../client4/impl/zio/FetchZioBackend.scala | 6 +- .../httpclient/zio/HttpClientZioBackend.scala | 29 ++++----- .../zio/ZioBodyFromHttpClient.scala | 7 +- ...ostSerializeJsonMonixHttpClientCirce.scala | 2 +- .../client4/examples/WebSocketTesting.scala | 3 +- .../examples/GetAndParseJsonZioCirce.scala | 2 +- .../sttp/client4/examples/RetryZio.scala | 5 +- .../sttp/client4/examples/StreamFs2.scala | 9 ++- .../sttp/client4/examples/StreamZio.scala | 6 +- .../sttp/client4/examples/WebSocketZio.scala | 3 +- .../sttp/client4/finagle/FinagleBackend.scala | 38 ++++++----- .../sttp/client4/http4s/Http4sBackend.scala | 24 ++++--- .../sttp/client4/http4s/Http4sBackend.scala | 24 +++---- .../sttp/client4/circe/SttpCirceApi.scala | 3 +- .../sttp/client4/json4s/SttpJson4sApi.scala | 3 +- .../jsoniter/SttpJsoniterJsonApi.scala | 12 ++-- .../client4/playJson/SttpPlayJsonApi.scala | 3 +- .../client4/sprayJson/SttpSprayJsonApi.scala | 3 +- .../client4/upicklejson/SttpUpickleApi.scala | 9 ++- .../sttp/client4/ziojson/SttpZioJsonApi.scala | 6 +- .../ziojson/SttpZioJsonApiExtensions.scala | 2 +- .../sttp/client4/ziojson/SttpZioJsonApi.scala | 6 +- .../ziojson/SttpZioJsonApiExtensions.scala | 2 +- .../OpenTelemetryMetricsBackend.scala | 27 ++++---- .../OpenTelemetryMetricsConfig.scala | 56 ++++++++-------- .../zio/OpenTelemetryTracingZioBackend.scala | 6 +- .../zio/OpenTelemetryTracingZioBackend.scala | 6 +- .../prometheus/PrometheusBackend.scala | 59 +++++++++-------- .../client4/prometheus/PrometheusConfig.scala | 23 +++---- .../okhttp/monix/OkHttpMonixBackend.scala | 21 +++--- .../sttp/client4/okhttp/BodyFromOkHttp.scala | 11 ++-- .../sttp/client4/okhttp/BodyToOkHttp.scala | 2 +- .../client4/okhttp/OkHttpAsyncBackend.scala | 2 +- .../sttp/client4/okhttp/OkHttpBackend.scala | 28 ++++---- .../client4/okhttp/OkHttpFutureBackend.scala | 12 ++-- .../client4/okhttp/OkHttpSyncBackend.scala | 23 +++++-- .../sttp/client4/okhttp/WebSocketImpl.scala | 11 ++-- .../http/scaladsl/coding/DeflateNoWrap.scala | 3 +- .../client4/testing/server/HttpServer.scala | 22 +++---- 146 files changed, 922 insertions(+), 1064 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index d466dc4bde..8b8c735de5 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,3 +1,4 @@ +version = 3.7.2 +maxColumn = 120 +rewrite.rules = [RedundantBraces, RedundantParens, SortImports] runner.dialect = scala3 -version = 3.7.2 -maxColumn = 120 \ No newline at end of file diff --git a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/AkkaHttpBackend.scala b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/AkkaHttpBackend.scala index 90e611b345..20af82596c 100644 --- a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/AkkaHttpBackend.scala +++ b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/AkkaHttpBackend.scala @@ -50,7 +50,7 @@ class AkkaHttpBackend private ( if (r.isWebSocket) sendWebSocket(r) else sendRegular(r) } - private def sendRegular[T](r: GenericRequest[T, R]): Future[Response[T]] = { + private def sendRegular[T](r: GenericRequest[T, R]): Future[Response[T]] = Future .fromTry(ToAkka.request(r).flatMap(BodyToAkka(r, r.body, _))) .map(customizeRequest) @@ -62,7 +62,6 @@ class AkkaHttpBackend private ( .flatMap(response => responseFromAkka(r, response, None).recoverWith(consumeResponseOnFailure(response))) ) ) - } private def sendWebSocket[T](r: GenericRequest[T, R]): Future[Response[T]] = { val akkaWebsocketRequest = ToAkka @@ -121,9 +120,9 @@ class AkkaHttpBackend private ( private lazy val bodyFromAkka = new BodyFromAkka()(ec, implicitly[Materializer], monad) private def responseFromAkka[T]( - r: GenericRequest[T, R], - hr: HttpResponse, - wsFlow: Option[Promise[Flow[Message, Message, NotUsed]]] + r: GenericRequest[T, R], + hr: HttpResponse, + wsFlow: Option[Promise[Flow[Message, Message, NotUsed]]] ): Future[Response[T]] = { val code = StatusCode(hr.status.intValue()) val statusText = hr.status.reason() @@ -141,10 +140,9 @@ class AkkaHttpBackend private ( } // http://doc.akka.io/docs/akka-http/10.0.7/scala/http/common/de-coding.html - private def decodeAkkaResponse(response: HttpResponse, disableAutoDecompression: Boolean): HttpResponse = { + private def decodeAkkaResponse(response: HttpResponse, disableAutoDecompression: Boolean): HttpResponse = if (!response.status.allowsEntity() || disableAutoDecompression) response else customEncodingHandler.orElse(EncodingHandler(standardEncoding)).apply(response -> response.encoding) - } private def standardEncoding: (HttpResponse, HttpEncoding) => HttpResponse = { case (body, HttpEncodings.gzip) => Coders.Gzip.decodeMessage(body) @@ -156,7 +154,7 @@ class AkkaHttpBackend private ( private def adjustExceptions[T](request: GenericRequest[_, _])(t: => Future[T]): Future[T] = SttpClientException.adjustExceptions(monad)(t)(FromAkka.exception(request, _)) - override def close(): Future[Unit] = { + override def close(): Future[Unit] = if (terminateActorSystemOnClose) { CoordinatedShutdown(as).addTask( CoordinatedShutdown.PhaseServiceRequestsDone, @@ -164,7 +162,6 @@ class AkkaHttpBackend private ( )(() => Http(as).shutdownAllConnectionPools.map(_ => Done)) actorSystem.terminate().map(_ => ()) } else Future.successful(()) - } } object AkkaHttpBackend { @@ -252,7 +249,7 @@ object AkkaHttpBackend { customEncodingHandler: EncodingHandler = PartialFunction.empty )(implicit ec: Option[ExecutionContext] = None - ): WebSocketStreamBackend[Future, AkkaStreams] = { + ): WebSocketStreamBackend[Future, AkkaStreams] = usingClient( actorSystem, options, @@ -263,7 +260,6 @@ object AkkaHttpBackend { customizeResponse, customEncodingHandler ) - } /** @param actorSystem * The actor system which will be used for the http-client actors. @@ -282,7 +278,7 @@ object AkkaHttpBackend { customEncodingHandler: EncodingHandler = PartialFunction.empty )(implicit ec: Option[ExecutionContext] = None - ): WebSocketStreamBackend[Future, AkkaStreams] = { + ): WebSocketStreamBackend[Future, AkkaStreams] = make( actorSystem, ec.getOrElse(actorSystem.dispatcher), @@ -295,7 +291,6 @@ object AkkaHttpBackend { customizeResponse, customEncodingHandler ) - } /** Create a stub backend for testing, which uses the [[Future]] response wrapper, and doesn't support streaming. * diff --git a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/AkkaHttpClient.scala b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/AkkaHttpClient.scala index 04c0801240..528bfef7cf 100644 --- a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/AkkaHttpClient.scala +++ b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/AkkaHttpClient.scala @@ -37,14 +37,13 @@ object AkkaHttpClient { override def singleRequest( request: HttpRequest, settings: ConnectionPoolSettings - ): Future[HttpResponse] = { + ): Future[HttpResponse] = http.singleRequest( request, connectionContext.getOrElse(http.defaultClientHttpsContext), settings, customLog.getOrElse(system.log) ) - } override def singleWebsocketRequest[WS_RESULT]( request: WebSocketRequest, diff --git a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/BodyFromAkka.scala b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/BodyFromAkka.scala index a5635b121f..654ce5d35d 100644 --- a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/BodyFromAkka.scala +++ b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/BodyFromAkka.scala @@ -20,9 +20,9 @@ import scala.util.Failure private[akkahttp] class BodyFromAkka()(implicit ec: ExecutionContext, mat: Materializer, m: MonadError[Future]) { def apply[T, R]( - responseAs: ResponseAsDelegate[T, R], - meta: ResponseMetadata, - response: Either[HttpResponse, Promise[Flow[Message, Message, NotUsed]]] + responseAs: ResponseAsDelegate[T, R], + meta: ResponseMetadata, + response: Either[HttpResponse, Promise[Flow[Message, Message, NotUsed]]] ): Future[T] = bodyFromResponseAs(responseAs, meta, response) @@ -40,16 +40,14 @@ private[akkahttp] class BodyFromAkka()(implicit ec: ExecutionContext, mat: Mater Future.successful(response.copy(entity = replayEntity)) } - override protected def regularIgnore(response: HttpResponse): Future[Unit] = { + override protected def regularIgnore(response: HttpResponse): Future[Unit] = // todo: Replace with HttpResponse#discardEntityBytes() once https://github.com/akka/akka-http/issues/1459 is resolved response.entity.dataBytes.runWith(Sink.ignore).map(_ => ()) - } - override protected def regularAsByteArray(response: HttpResponse): Future[Array[Byte]] = { + override protected def regularAsByteArray(response: HttpResponse): Future[Array[Byte]] = response.entity.dataBytes .runFold(ByteString(""))(_ ++ _) .map(_.toArray[Byte]) - } override protected def regularAsFile(response: HttpResponse, file: SttpFile): Future[SttpFile] = { val f = file.toFile @@ -63,7 +61,7 @@ private[akkahttp] class BodyFromAkka()(implicit ec: ExecutionContext, mat: Mater override protected def regularAsStream( response: HttpResponse - ): Future[(Source[ByteString, Any], () => Future[Unit])] = { + ): Future[(Source[ByteString, Any], () => Future[Unit])] = Future.successful( ( response.entity.dataBytes, @@ -71,7 +69,6 @@ private[akkahttp] class BodyFromAkka()(implicit ec: ExecutionContext, mat: Mater () => response.discardEntityBytes().future().map(_ => ()).recover { case _ => () } ) ) - } override protected def handleWS[T]( responseAs: GenericWebSocketResponseAs[T, _], @@ -92,7 +89,7 @@ private[akkahttp] class BodyFromAkka()(implicit ec: ExecutionContext, mat: Mater rr: GenericWebSocketResponseAs[T, R], wsFlow: Promise[Flow[Message, Message, NotUsed]], meta: ResponseMetadata - )(implicit ec: ExecutionContext, mat: Materializer): Future[T] = { + )(implicit ec: ExecutionContext, mat: Materializer): Future[T] = rr match { case ResponseAsWebSocket(f) => val (flow, wsFuture) = webSocketAndFlow(meta) @@ -126,7 +123,6 @@ private[akkahttp] class BodyFromAkka()(implicit ec: ExecutionContext, mat: Mater donePromise.future.map(_ => ()) } - } private def webSocketAndFlow(meta: ResponseMetadata)(implicit ec: ExecutionContext, @@ -210,7 +206,7 @@ private[akkahttp] class BodyFromAkka()(implicit ec: ExecutionContext, mat: Mater msg.dataStream.runFold(ByteString.empty)(_ ++ _).map(b => WebSocketFrame.binary(b.toArray)) } - private def frameToMessage(w: WebSocketFrame): Option[Message] = { + private def frameToMessage(w: WebSocketFrame): Option[Message] = w match { case WebSocketFrame.Text(p, _, _) => Some(TextMessage(p)) case WebSocketFrame.Binary(p, _, _) => Some(BinaryMessage(ByteString(p))) @@ -218,5 +214,4 @@ private[akkahttp] class BodyFromAkka()(implicit ec: ExecutionContext, mat: Mater case WebSocketFrame.Pong(_) => None case WebSocketFrame.Close(_, _) => throw WebSocketClosed(None) } - } } diff --git a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/BodyToAkka.scala b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/BodyToAkka.scala index 78118888f7..e3e1ae499c 100644 --- a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/BodyToAkka.scala +++ b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/BodyToAkka.scala @@ -6,8 +6,8 @@ import akka.http.scaladsl.model.{ HttpEntity, HttpRequest, MediaType, - RequestEntity, - Multipart => AkkaMultipart + Multipart => AkkaMultipart, + RequestEntity } import akka.stream.scaladsl.{Source, StreamConverters} import akka.util.ByteString @@ -21,9 +21,9 @@ import scala.util.{Failure, Success, Try} private[akkahttp] object BodyToAkka { def apply[R]( - r: GenericRequest[_, R], - body: GenericRequestBody[R], - ar: HttpRequest + r: GenericRequest[_, R], + body: GenericRequestBody[R], + ar: HttpRequest ): Try[HttpRequest] = { def ctWithCharset(ct: ContentType, charset: String) = HttpCharsets @@ -53,9 +53,7 @@ private[akkahttp] object BodyToAkka { for { ct <- Util.parseContentTypeOrOctetStream(mp.contentType) headers <- ToAkka.headers(mp.headers.toList) - } yield { - AkkaMultipart.FormData.BodyPart(mp.name, entity(ct), mp.dispositionParams, headers) - } + } yield AkkaMultipart.FormData.BodyPart(mp.name, entity(ct), mp.dispositionParams, headers) } def streamEntity(contentType: ContentType, s: AkkaStreams.BinaryStream) = @@ -83,9 +81,9 @@ private[akkahttp] object BodyToAkka { } private def multipartEntity( - r: GenericRequest[_, _], - bodyParts: Seq[AkkaMultipart.FormData.BodyPart] - ): Try[RequestEntity] = { + r: GenericRequest[_, _], + bodyParts: Seq[AkkaMultipart.FormData.BodyPart] + ): Try[RequestEntity] = r.headers.find(Util.isContentType) match { case None => Success(AkkaMultipart.FormData(bodyParts: _*).toEntity()) case Some(ct) => @@ -93,11 +91,10 @@ private[akkahttp] object BodyToAkka { case m: MediaType.Multipart => Success( AkkaMultipart - .General(m, Source(bodyParts.map { bp => AkkaMultipart.General.BodyPart(bp.entity, bp.headers) })) + .General(m, Source(bodyParts.map(bp => AkkaMultipart.General.BodyPart(bp.entity, bp.headers)))) .toEntity() ) case _ => Failure(new RuntimeException(s"Non-multipart content type: $ct")) } } - } } diff --git a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/Util.scala b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/Util.scala index bb4136d61e..673d44d056 100644 --- a/akka-http-backend/src/main/scala/sttp/client4/akkahttp/Util.scala +++ b/akka-http-backend/src/main/scala/sttp/client4/akkahttp/Util.scala @@ -19,25 +19,22 @@ private[akkahttp] object Util { else Failure[Seq[T]](fs.head.exception) } - def parseContentTypeOrOctetStream(r: GenericRequest[_, _]): Try[ContentType] = { + def parseContentTypeOrOctetStream(r: GenericRequest[_, _]): Try[ContentType] = parseContentTypeOrOctetStream( r.headers .find(isContentType) .map(_.value) ) - } - def parseContentTypeOrOctetStream(ctHeader: Option[String]): Try[ContentType] = { + def parseContentTypeOrOctetStream(ctHeader: Option[String]): Try[ContentType] = ctHeader .map(parseContentType) .getOrElse(Success(`application/octet-stream`)) - } - def parseContentType(ctHeader: String): Try[ContentType] = { + def parseContentType(ctHeader: String): Try[ContentType] = ContentType .parse(ctHeader) .fold(errors => Failure(new RuntimeException(s"Cannot parse content type: $errors")), Success(_)) - } def isContentType(header: Header): Boolean = header.name.toLowerCase.contains(`Content-Type`.lowercaseName) diff --git a/armeria-backend/cats-ce2/src/main/scala/sttp/client4/armeria/cats/ArmeriaCatsBackend.scala b/armeria-backend/cats-ce2/src/main/scala/sttp/client4/armeria/cats/ArmeriaCatsBackend.scala index e7a71c6f37..49b45f581e 100644 --- a/armeria-backend/cats-ce2/src/main/scala/sttp/client4/armeria/cats/ArmeriaCatsBackend.scala +++ b/armeria-backend/cats-ce2/src/main/scala/sttp/client4/armeria/cats/ArmeriaCatsBackend.scala @@ -10,7 +10,7 @@ import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.impl.cats.CatsMonadAsyncError import sttp.client4.internal.NoStreams import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{Backend, BackendOptions, wrappers} +import sttp.client4.{wrappers, Backend, BackendOptions} import sttp.monad.MonadAsyncError private final class ArmeriaCatsBackend[F[_]: Concurrent](client: WebClient, closeFactory: Boolean) @@ -44,13 +44,11 @@ object ArmeriaCatsBackend { def resource[F[_]: Concurrent]( options: BackendOptions = BackendOptions.Default - ): Resource[F, Backend[F]] = { + ): Resource[F, Backend[F]] = Resource.make(Sync[F].delay(apply(newClient(options), closeFactory = true)))(_.close()) - } - def resourceUsingClient[F[_]: Concurrent](client: WebClient): Resource[F, Backend[F]] = { + def resourceUsingClient[F[_]: Concurrent](client: WebClient): Resource[F, Backend[F]] = Resource.make(Sync[F].delay(apply(client, closeFactory = true)))(_.close()) - } def usingDefaultClient[F[_]: Concurrent](): Backend[F] = apply(newClient(), closeFactory = false) diff --git a/armeria-backend/cats/src/main/scala/sttp/client4/armeria/cats/ArmeriaCatsBackend.scala b/armeria-backend/cats/src/main/scala/sttp/client4/armeria/cats/ArmeriaCatsBackend.scala index 63e5a58486..d926a678b1 100644 --- a/armeria-backend/cats/src/main/scala/sttp/client4/armeria/cats/ArmeriaCatsBackend.scala +++ b/armeria-backend/cats/src/main/scala/sttp/client4/armeria/cats/ArmeriaCatsBackend.scala @@ -10,7 +10,7 @@ import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.impl.cats.CatsMonadAsyncError import sttp.client4.internal.NoStreams import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{Backend, BackendOptions, wrappers} +import sttp.client4.{wrappers, Backend, BackendOptions} import sttp.monad.MonadAsyncError private final class ArmeriaCatsBackend[F[_]: Async](client: WebClient, closeFactory: Boolean) @@ -44,13 +44,11 @@ object ArmeriaCatsBackend { def resource[F[_]: Async]( options: BackendOptions = BackendOptions.Default - ): Resource[F, Backend[F]] = { + ): Resource[F, Backend[F]] = Resource.make(Sync[F].delay(apply(newClient(options), closeFactory = true)))(_.close()) - } - def resourceUsingClient[F[_]: Async](client: WebClient): Resource[F, Backend[F]] = { + def resourceUsingClient[F[_]: Async](client: WebClient): Resource[F, Backend[F]] = Resource.make(Sync[F].delay(apply(client, closeFactory = true)))(_.close()) - } def usingDefaultClient[F[_]: Async](): Backend[F] = apply(newClient(), closeFactory = false) diff --git a/armeria-backend/fs2-ce2/src/main/scala/sttp/client4/armeria/fs2/ArmeriaFs2Backend.scala b/armeria-backend/fs2-ce2/src/main/scala/sttp/client4/armeria/fs2/ArmeriaFs2Backend.scala index 654959834c..7d8ccef6c5 100644 --- a/armeria-backend/fs2-ce2/src/main/scala/sttp/client4/armeria/fs2/ArmeriaFs2Backend.scala +++ b/armeria-backend/fs2-ce2/src/main/scala/sttp/client4/armeria/fs2/ArmeriaFs2Backend.scala @@ -12,7 +12,7 @@ import sttp.client4.armeria.ArmeriaWebClient.newClient import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.impl.cats.CatsMonadAsyncError import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, StreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, StreamBackend} import sttp.monad.MonadAsyncError private final class ArmeriaFs2Backend[F[_]: ConcurrentEffect](client: WebClient, closeFactory: Boolean) @@ -32,12 +32,10 @@ private final class ArmeriaFs2Backend[F[_]: ConcurrentEffect](client: WebClient, } override protected def streamToPublisher(stream: Stream[F, Byte]): Publisher[HttpData] = - stream.chunks - .map(chunk => { - val bytes = chunk.toBytes - HttpData.wrap(bytes.values, bytes.offset, bytes.length) - }) - .toUnicastPublisher + stream.chunks.map { chunk => + val bytes = chunk.toBytes + HttpData.wrap(bytes.values, bytes.offset, bytes.length) + }.toUnicastPublisher } object ArmeriaFs2Backend { @@ -53,13 +51,11 @@ object ArmeriaFs2Backend { def resource[F[_]: ConcurrentEffect]( options: BackendOptions = BackendOptions.Default - ): Resource[F, StreamBackend[F, Fs2Streams[F]]] = { + ): Resource[F, StreamBackend[F, Fs2Streams[F]]] = Resource.make(Sync[F].delay(apply(newClient(options), closeFactory = true)))(_.close()) - } - def resourceUsingClient[F[_]: ConcurrentEffect](client: WebClient): Resource[F, StreamBackend[F, Fs2Streams[F]]] = { + def resourceUsingClient[F[_]: ConcurrentEffect](client: WebClient): Resource[F, StreamBackend[F, Fs2Streams[F]]] = Resource.make(Sync[F].delay(apply(client, closeFactory = true)))(_.close()) - } def usingClient[F[_]: ConcurrentEffect](client: WebClient): StreamBackend[F, Fs2Streams[F]] = apply(client, closeFactory = false) diff --git a/armeria-backend/fs2/src/main/scala/sttp/client4/armeria/fs2/ArmeriaFs2Backend.scala b/armeria-backend/fs2/src/main/scala/sttp/client4/armeria/fs2/ArmeriaFs2Backend.scala index 7d564dc720..2a793702ee 100644 --- a/armeria-backend/fs2/src/main/scala/sttp/client4/armeria/fs2/ArmeriaFs2Backend.scala +++ b/armeria-backend/fs2/src/main/scala/sttp/client4/armeria/fs2/ArmeriaFs2Backend.scala @@ -13,7 +13,7 @@ import sttp.client4.armeria.ArmeriaWebClient.newClient import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.impl.cats.CatsMonadAsyncError import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, StreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, StreamBackend} import sttp.monad.MonadAsyncError private final class ArmeriaFs2Backend[F[_]: Async](client: WebClient, closeFactory: Boolean, dispatcher: Dispatcher[F]) diff --git a/armeria-backend/monix/src/main/scala/sttp/client4/armeria/monix/ArmeriaMonixBackend.scala b/armeria-backend/monix/src/main/scala/sttp/client4/armeria/monix/ArmeriaMonixBackend.scala index a0912fea7d..1005c9311a 100644 --- a/armeria-backend/monix/src/main/scala/sttp/client4/armeria/monix/ArmeriaMonixBackend.scala +++ b/armeria-backend/monix/src/main/scala/sttp/client4/armeria/monix/ArmeriaMonixBackend.scala @@ -12,7 +12,7 @@ import sttp.client4.armeria.ArmeriaWebClient.newClient import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.impl.monix.TaskMonadAsyncError import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, StreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, StreamBackend} import sttp.monad.MonadAsyncError private final class ArmeriaMonixBackend(client: WebClient, closeFactory: Boolean)(implicit scheduler: Scheduler) @@ -44,7 +44,7 @@ object ArmeriaMonixBackend { * The scheduler used for streaming request bodies. Defaults to the global scheduler. */ def apply(options: BackendOptions = BackendOptions.Default)(implicit - scheduler: Scheduler = Scheduler.global + scheduler: Scheduler = Scheduler.global ): StreamBackend[Task, MonixStreams] = apply(newClient(options), closeFactory = true) diff --git a/armeria-backend/scalaz/src/main/scala/sttp/client4/armeria/scalaz/ArmeriaScalazBackend.scala b/armeria-backend/scalaz/src/main/scala/sttp/client4/armeria/scalaz/ArmeriaScalazBackend.scala index 794127ea77..e46f19ab02 100644 --- a/armeria-backend/scalaz/src/main/scala/sttp/client4/armeria/scalaz/ArmeriaScalazBackend.scala +++ b/armeria-backend/scalaz/src/main/scala/sttp/client4/armeria/scalaz/ArmeriaScalazBackend.scala @@ -10,7 +10,7 @@ import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.impl.scalaz.TaskMonadAsyncError import sttp.client4.internal.NoStreams import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{Backend, BackendOptions, wrappers} +import sttp.client4.{wrappers, Backend, BackendOptions} import sttp.monad.MonadAsyncError private final class ArmeriaScalazBackend(client: WebClient, closeFactory: Boolean) diff --git a/armeria-backend/src/main/scala/sttp/client4/armeria/AbstractArmeriaBackend.scala b/armeria-backend/src/main/scala/sttp/client4/armeria/AbstractArmeriaBackend.scala index 71b988286b..bd44a2b350 100644 --- a/armeria-backend/src/main/scala/sttp/client4/armeria/AbstractArmeriaBackend.scala +++ b/armeria-backend/src/main/scala/sttp/client4/armeria/AbstractArmeriaBackend.scala @@ -17,8 +17,8 @@ import com.linecorp.armeria.common.{ HttpMethod, HttpResponse, HttpStatus, - ResponseHeaders, - MediaType => ArmeriaMediaType + MediaType => ArmeriaMediaType, + ResponseHeaders } import io.netty.buffer.Unpooled import io.netty.util.AsciiString @@ -34,7 +34,7 @@ import scala.util.{Failure, Success, Try} import sttp.capabilities.{Effect, Streams} import sttp.client4.SttpClientException.{ConnectException, ReadException, TimeoutException} import sttp.client4._ -import sttp.client4.armeria.AbstractArmeriaBackend.{RightUnit, noopCanceler} +import sttp.client4.armeria.AbstractArmeriaBackend.{noopCanceler, RightUnit} import sttp.client4.internal.toByteArray import sttp.model._ import sttp.monad.syntax._ @@ -68,7 +68,7 @@ abstract class AbstractArmeriaBackend[F[_], S <: Streams[S]]( armeriaRes .aggregate() .asInstanceOf[CompletableFuture[Void]] - .handle((_: Void, cause: Throwable) => { + .handle { (_: Void, cause: Throwable) => // Get an actual error from a response if (cause != null) { cb(Left(cause)) @@ -76,7 +76,7 @@ abstract class AbstractArmeriaBackend[F[_], S <: Streams[S]]( cb(Left(ex)) } null - }) + } noopCanceler } case Success(ctx) => @@ -84,9 +84,7 @@ abstract class AbstractArmeriaBackend[F[_], S <: Streams[S]]( } } catch { case NonFatal(ex) => monad.error(ex) - } finally { - captor.close() - } + } finally captor.close() } private def requestToArmeria(request: GenericRequest[_, Nothing]): WebClientRequestPreparation = { @@ -206,9 +204,9 @@ abstract class AbstractArmeriaBackend[F[_], S <: Streams[S]]( } private def fromArmeriaResponse[T]( - request: GenericRequest[T, R], - response: HttpResponse, - ctx: ClientRequestContext + request: GenericRequest[T, R], + response: HttpResponse, + ctx: ClientRequestContext ): F[Response[T]] = { val splitHttpResponse = response.split() val aggregatorRef = new AtomicReference[StreamMessageAggregator]() @@ -216,14 +214,14 @@ abstract class AbstractArmeriaBackend[F[_], S <: Streams[S]]( headers <- monad.async[ResponseHeaders] { cb => splitHttpResponse .headers() - .handle((headers: ResponseHeaders, cause: Throwable) => { + .handle { (headers: ResponseHeaders, cause: Throwable) => if (cause != null) { cb(Left(cause)) } else { cb(Right(headers)) } null - }) + } Canceler(() => response.abort()) } meta <- headersToResponseMeta(headers, ctx) @@ -252,38 +250,37 @@ abstract class AbstractArmeriaBackend[F[_], S <: Streams[S]]( } else { val builder = Seq.newBuilder[Header] builder.sizeHint(responseHeaders.size()) - responseHeaders.forEach((key: AsciiString, value) => { + responseHeaders.forEach { (key: AsciiString, value) => // Skip pseudo header if (key.charAt(0) != ':') { builder += new Header(key.toString(), value) } - }) + } monad.unit(ResponseMetadata(StatusCode.unsafeApply(status.code()), status.codeAsText(), builder.result())) } } - override def close(): F[Unit] = { + override def close(): F[Unit] = if (closeFactory) { - monad.async(cb => { + monad.async { cb => client .options() .factory() .closeAsync() .asInstanceOf[CompletableFuture[Void]] - .handle((_: Void, cause: Throwable) => { + .handle { (_: Void, cause: Throwable) => if (cause != null) { cb(Left(cause)) } else { cb(RightUnit) } null - }) + } noopCanceler - }) + } } else { monad.unit(()) } - } } private[armeria] object AbstractArmeriaBackend { diff --git a/armeria-backend/src/main/scala/sttp/client4/armeria/ArmeriaWebClient.scala b/armeria-backend/src/main/scala/sttp/client4/armeria/ArmeriaWebClient.scala index 1d81d28a7d..037c3ca18f 100644 --- a/armeria-backend/src/main/scala/sttp/client4/armeria/ArmeriaWebClient.scala +++ b/armeria-backend/src/main/scala/sttp/client4/armeria/ArmeriaWebClient.scala @@ -20,7 +20,7 @@ object ArmeriaWebClient { def newClient(): WebClient = newClient(identity[WebClientBuilder] _) /** Create a new [[WebClient]] which is adjusted for sttp client's needs. */ - def newClient(customizeWebClient: WebClientBuilder => WebClientBuilder): WebClient = { + def newClient(customizeWebClient: WebClientBuilder => WebClientBuilder): WebClient = customizeWebClient( WebClient .builder() @@ -33,15 +33,14 @@ object ArmeriaWebClient { ) ) .build() - } /** Create a new [[WebClient]] which is adjusted for sttp client's needs, and uses the timeouts/proxy specified in * `options`. */ def newClient( - options: BackendOptions, - customizeWebClient: WebClientBuilder => WebClientBuilder = identity - ): WebClient = { + options: BackendOptions, + customizeWebClient: WebClientBuilder => WebClientBuilder = identity + ): WebClient = customizeWebClient( WebClient .builder() @@ -55,5 +54,4 @@ object ArmeriaWebClient { .factory(newClientFactory(options)) ) .build() - } } diff --git a/armeria-backend/src/main/scala/sttp/client4/armeria/BodyFromStreamMessage.scala b/armeria-backend/src/main/scala/sttp/client4/armeria/BodyFromStreamMessage.scala index 60a1c67f00..03209d012b 100644 --- a/armeria-backend/src/main/scala/sttp/client4/armeria/BodyFromStreamMessage.scala +++ b/armeria-backend/src/main/scala/sttp/client4/armeria/BodyFromStreamMessage.scala @@ -9,7 +9,7 @@ import java.nio.file.Path import java.util.concurrent.atomic.AtomicReference import sttp.capabilities.Streams import sttp.client4.GenericWebSocketResponseAs -import sttp.client4.armeria.AbstractArmeriaBackend.{RightUnit, noopCanceler} +import sttp.client4.armeria.AbstractArmeriaBackend.{noopCanceler, RightUnit} import sttp.client4.internal.{BodyFromResponseAs, SttpFile} import sttp.client4.ws.{GotAWebSocketException, NotAWebSocketException} import sttp.model.ResponseMetadata @@ -36,8 +36,8 @@ private[armeria] trait BodyFromStreamMessage[F[_], S] { aggregator = aggregatorRef.get() } - monad.async(cb => { - aggregator.future.handle((data: HttpData, cause: Throwable) => { + monad.async { cb => + aggregator.future.handle { (data: HttpData, cause: Throwable) => if (cause == null) { val array = data.array() cb(Right(array)) @@ -45,38 +45,35 @@ private[armeria] trait BodyFromStreamMessage[F[_], S] { cb(Left(cause)) } null - }) + } Canceler(() => streamMessage.abort()) - }) + } } def publisherToFile( p: StreamMessage[HttpData], f: File, executor: EventExecutor - ): F[Unit] = { - monad.async[Unit](cb => { + ): F[Unit] = + monad.async[Unit] { cb => StreamMessages .writeTo(p, f.toPath, executor, CommonPools.blockingTaskExecutor()) - .handle((_: Void, cause: Throwable) => { + .handle { (_: Void, cause: Throwable) => if (cause != null) { cb(Left(cause)) } else { cb(RightUnit) } null - }) + } noopCanceler - }) - } + } - def bytesToPublisher(b: Array[Byte]): F[StreamMessage[HttpData]] = { + def bytesToPublisher(b: Array[Byte]): F[StreamMessage[HttpData]] = StreamMessage.of(Array(HttpData.wrap(b)): _*).unit - } - def pathToPublisher(f: Path): F[StreamMessage[HttpData]] = { + def pathToPublisher(f: Path): F[StreamMessage[HttpData]] = (StreamMessage.of(f): StreamMessage[HttpData]).unit - } def apply( executor: EventExecutor, @@ -103,9 +100,8 @@ private[armeria] trait BodyFromStreamMessage[F[_], S] { override protected def regularAsStream( response: StreamMessage[HttpData] - ): F[(streams.BinaryStream, () => F[Unit])] = { + ): F[(streams.BinaryStream, () => F[Unit])] = (publisherToStream(response), () => monad.eval(response.abort())).unit - } override protected def handleWS[T]( responseAs: GenericWebSocketResponseAs[T, _], diff --git a/armeria-backend/src/main/scala/sttp/client4/armeria/StreamMessageAggregator.scala b/armeria-backend/src/main/scala/sttp/client4/armeria/StreamMessageAggregator.scala index fc943f0a74..169b06b505 100644 --- a/armeria-backend/src/main/scala/sttp/client4/armeria/StreamMessageAggregator.scala +++ b/armeria-backend/src/main/scala/sttp/client4/armeria/StreamMessageAggregator.scala @@ -19,7 +19,7 @@ private final class StreamMessageAggregator extends Subscriber[HttpData] { override def onNext(o: HttpData): Unit = { var added = false - try { + try if (future.isDone) { // no-op } else { @@ -36,14 +36,13 @@ private final class StreamMessageAggregator extends Subscriber[HttpData] { added = true } } - } finally { + finally if (!added) { o.close() } - } } - override def onComplete(): Unit = { + override def onComplete(): Unit = if (future.isDone) { // no-op } else { @@ -53,21 +52,19 @@ private final class StreamMessageAggregator extends Subscriber[HttpData] { val merged = new Array[Byte](contentLength) var offset = 0 - contentList.foreach(data => { + contentList.foreach { data => val dataLength = data.length() System.arraycopy(data.array(), 0, merged, offset, dataLength) offset += dataLength - }) + } contentList.clear() HttpData.wrap(merged) } val _ = future.complete(content) } - } - override def onError(t: Throwable): Unit = { + override def onError(t: Throwable): Unit = fail(t) - } private def fail(cause: Throwable): Unit = { contentList.foreach(_.close) diff --git a/armeria-backend/src/main/scala/sttp/client4/armeria/future/ArmeriaFutureBackend.scala b/armeria-backend/src/main/scala/sttp/client4/armeria/future/ArmeriaFutureBackend.scala index 130d38bac1..bba0051620 100644 --- a/armeria-backend/src/main/scala/sttp/client4/armeria/future/ArmeriaFutureBackend.scala +++ b/armeria-backend/src/main/scala/sttp/client4/armeria/future/ArmeriaFutureBackend.scala @@ -8,7 +8,7 @@ import sttp.client4.armeria.ArmeriaWebClient.newClient import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.internal.NoStreams import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{Backend, BackendOptions, wrappers} +import sttp.client4.{wrappers, Backend, BackendOptions} import sttp.monad.{FutureMonad, MonadAsyncError} import scala.concurrent.ExecutionContext.Implicits.global diff --git a/armeria-backend/zio/src/main/scala/sttp/client4/armeria/zio/ArmeriaZioBackend.scala b/armeria-backend/zio/src/main/scala/sttp/client4/armeria/zio/ArmeriaZioBackend.scala index 3ff0f50e99..8d863b7875 100644 --- a/armeria-backend/zio/src/main/scala/sttp/client4/armeria/zio/ArmeriaZioBackend.scala +++ b/armeria-backend/zio/src/main/scala/sttp/client4/armeria/zio/ArmeriaZioBackend.scala @@ -1,6 +1,9 @@ package sttp.client4.armeria.zio -import _root_.zio.interop.reactivestreams.{publisherToStream => publisherToZioStream, streamToPublisher => zioStreamToPublisher} +import _root_.zio.interop.reactivestreams.{ + publisherToStream => publisherToZioStream, + streamToPublisher => zioStreamToPublisher +} import _root_.zio.{Chunk, Task, _} import com.linecorp.armeria.client.WebClient import com.linecorp.armeria.common.HttpData @@ -11,7 +14,7 @@ import sttp.client4.armeria.ArmeriaWebClient.newClient import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.impl.zio.RIOMonadAsyncError import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, StreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, StreamBackend} import sttp.monad.MonadAsyncError import zio.stream.Stream diff --git a/armeria-backend/zio1/src/main/scala/sttp/client4/armeria/zio/ArmeriaZioBackend.scala b/armeria-backend/zio1/src/main/scala/sttp/client4/armeria/zio/ArmeriaZioBackend.scala index f6883e1fd3..512f2e9853 100644 --- a/armeria-backend/zio1/src/main/scala/sttp/client4/armeria/zio/ArmeriaZioBackend.scala +++ b/armeria-backend/zio1/src/main/scala/sttp/client4/armeria/zio/ArmeriaZioBackend.scala @@ -7,12 +7,15 @@ import org.reactivestreams.Publisher import sttp.capabilities.zio.ZioStreams import sttp.client4.armeria.{AbstractArmeriaBackend, BodyFromStreamMessage} import sttp.client4.impl.zio.RIOMonadAsyncError -import sttp.client4.{BackendOptions, StreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, StreamBackend} import sttp.monad.MonadAsyncError import zio.{Chunk, Task} import zio.stream.Stream import _root_.zio._ -import _root_.zio.interop.reactivestreams.{publisherToStream => publisherToZioStream, streamToPublisher => zioStreamToPublisher} +import _root_.zio.interop.reactivestreams.{ + publisherToStream => publisherToZioStream, + streamToPublisher => zioStreamToPublisher +} import sttp.client4.armeria.ArmeriaWebClient.newClient import sttp.client4.wrappers.FollowRedirectsBackend diff --git a/async-http-client-backend/cats-ce2/src/main/scala/sttp/client4/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala b/async-http-client-backend/cats-ce2/src/main/scala/sttp/client4/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala index 78b530b6e9..34917f36f0 100644 --- a/async-http-client-backend/cats-ce2/src/main/scala/sttp/client4/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala +++ b/async-http-client-backend/cats-ce2/src/main/scala/sttp/client4/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala @@ -5,12 +5,18 @@ import java.nio.ByteBuffer import cats.effect.implicits._ import cats.effect.{Concurrent, ContextShift, Resource, Sync} import io.netty.buffer.ByteBuf -import org.asynchttpclient.{AsyncHttpClient, AsyncHttpClientConfig, BoundRequestBuilder, DefaultAsyncHttpClient, DefaultAsyncHttpClientConfig} +import org.asynchttpclient.{ + AsyncHttpClient, + AsyncHttpClientConfig, + BoundRequestBuilder, + DefaultAsyncHttpClient, + DefaultAsyncHttpClientConfig +} import org.reactivestreams.Publisher import sttp.client4.asynchttpclient.{AsyncHttpClientBackend, BodyFromAHC, BodyToAHC} import sttp.client4.impl.cats.CatsMonadAsyncError import sttp.client4.internal.{FileHelpers, NoStreams} -import sttp.client4.{Backend, BackendOptions, GenericRequest, Response, wrappers} +import sttp.client4.{wrappers, Backend, BackendOptions, GenericRequest, Response} import cats.implicits._ import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.BackendStub @@ -41,11 +47,10 @@ class AsyncHttpClientCatsBackend[F[_]: Concurrent: ContextShift] private ( throw new IllegalStateException("This backend does not support streaming") override def compileWebSocketPipe(ws: WebSocket[F], pipe: Nothing): F[Unit] = pipe // nothing is everything - override def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = { + override def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = publisherToBytes(p) .guarantee(implicitly[ContextShift[F]].shift) .map(bytes => FileHelpers.saveFile(f, new ByteArrayInputStream(bytes))) - } } override protected def bodyToAHC: BodyToAHC[F, Nothing] = @@ -69,8 +74,8 @@ object AsyncHttpClientCatsBackend { /** After sending a request, always shifts to the thread pool backing the given `ContextShift[F]`. */ def apply[F[_]: Concurrent: ContextShift]( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): F[Backend[F]] = Sync[F].delay( AsyncHttpClientCatsBackend(AsyncHttpClientBackend.defaultClient(options), closeClient = true, customizeRequest) @@ -80,8 +85,8 @@ object AsyncHttpClientCatsBackend { * the given `ContextShift[F]`. */ def resource[F[_]: Concurrent: ContextShift]( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): Resource[F, Backend[F]] = Resource.make(apply(options, customizeRequest))(_.close()) @@ -107,9 +112,9 @@ object AsyncHttpClientCatsBackend { * A function which updates the default configuration (created basing on `options`). */ def usingConfigBuilder[F[_]: Concurrent: ContextShift]( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): F[Backend[F]] = Sync[F].delay( AsyncHttpClientCatsBackend( @@ -125,9 +130,9 @@ object AsyncHttpClientCatsBackend { * A function which updates the default configuration (created basing on `options`). */ def resourceUsingConfigBuilder[F[_]: Concurrent: ContextShift]( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): Resource[F, Backend[F]] = Resource.make(usingConfigBuilder(updateConfig, options, customizeRequest))(_.close()) diff --git a/async-http-client-backend/cats/src/main/scala/sttp/client4/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala b/async-http-client-backend/cats/src/main/scala/sttp/client4/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala index 692302b35c..dfabbd0976 100644 --- a/async-http-client-backend/cats/src/main/scala/sttp/client4/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala +++ b/async-http-client-backend/cats/src/main/scala/sttp/client4/asynchttpclient/cats/AsyncHttpClientCatsBackend.scala @@ -4,12 +4,18 @@ import java.io.{ByteArrayInputStream, File} import java.nio.ByteBuffer import cats.effect.kernel.{Async, Resource, Sync} import io.netty.buffer.ByteBuf -import org.asynchttpclient.{AsyncHttpClient, AsyncHttpClientConfig, BoundRequestBuilder, DefaultAsyncHttpClient, DefaultAsyncHttpClientConfig} +import org.asynchttpclient.{ + AsyncHttpClient, + AsyncHttpClientConfig, + BoundRequestBuilder, + DefaultAsyncHttpClient, + DefaultAsyncHttpClientConfig +} import org.reactivestreams.Publisher import sttp.client4.asynchttpclient.{AsyncHttpClientBackend, BodyFromAHC, BodyToAHC} import sttp.client4.impl.cats.CatsMonadAsyncError import sttp.client4.internal.{FileHelpers, NoStreams} -import sttp.client4.{Backend, BackendOptions, wrappers} +import sttp.client4.{wrappers, Backend, BackendOptions} import cats.implicits._ import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.BackendStub @@ -37,10 +43,9 @@ class AsyncHttpClientCatsBackend[F[_]: Async] private ( throw new IllegalStateException("This backend does not support streaming") override def compileWebSocketPipe(ws: WebSocket[F], pipe: Nothing): F[Unit] = pipe // nothing is everything - override def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = { + override def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = publisherToBytes(p) .map(bytes => FileHelpers.saveFile(f, new ByteArrayInputStream(bytes))) - } } override protected def bodyToAHC: BodyToAHC[F, Nothing] = @@ -62,8 +67,8 @@ object AsyncHttpClientCatsBackend { wrappers.FollowRedirectsBackend(new AsyncHttpClientCatsBackend(asyncHttpClient, closeClient, customizeRequest)) def apply[F[_]: Async]( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): F[Backend[F]] = Sync[F].delay( AsyncHttpClientCatsBackend(AsyncHttpClientBackend.defaultClient(options), closeClient = true, customizeRequest) @@ -71,8 +76,8 @@ object AsyncHttpClientCatsBackend { /** Makes sure the backend is closed after usage. */ def resource[F[_]: Async]( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): Resource[F, Backend[F]] = Resource.make(apply(options, customizeRequest))(_.close()) @@ -91,9 +96,9 @@ object AsyncHttpClientCatsBackend { /** @param updateConfig A function which updates the default configuration (created basing on `options`). */ def usingConfigBuilder[F[_]: Async]( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): F[Backend[F]] = Sync[F].delay( AsyncHttpClientCatsBackend( @@ -108,9 +113,9 @@ object AsyncHttpClientCatsBackend { * A function which updates the default configuration (created basing on `options`). */ def resourceUsingConfigBuilder[F[_]: Async]( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): Resource[F, Backend[F]] = Resource.make(usingConfigBuilder(updateConfig, options, customizeRequest))(_.close()) diff --git a/async-http-client-backend/fs2-ce2/src/main/scala/sttp/client4/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala b/async-http-client-backend/fs2-ce2/src/main/scala/sttp/client4/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala index 40f80cb0c5..e09bece67d 100644 --- a/async-http-client-backend/fs2-ce2/src/main/scala/sttp/client4/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala +++ b/async-http-client-backend/fs2-ce2/src/main/scala/sttp/client4/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala @@ -53,20 +53,18 @@ class AsyncHttpClientFs2Backend[F[_]: ConcurrentEffect: ContextShift] private ( override def publisherToStream(p: Publisher[ByteBuffer]): Stream[F, Byte] = p.toStream[F].flatMap(buf => Stream.chunk(Chunk.byteBuffer(buf))) - override def publisherToBytes(p: Publisher[ByteBuffer]): F[Array[Byte]] = { + override def publisherToBytes(p: Publisher[ByteBuffer]): F[Array[Byte]] = p.toStream[F] .compile .fold(Queue.empty[Array[Byte]])(enqueueBytes) .map(concatBytes) - } - override def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = { + override def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = p.toStream[F] .flatMap(b => Stream.emits(b.array())) .through(fs2.io.file.writeAll(f.toPath, blocker)) .compile .drain - } override def bytesToPublisher(b: Array[Byte]): F[Publisher[ByteBuffer]] = (Stream.apply[F, ByteBuffer](ByteBuffer.wrap(b)).toUnicastPublisher: Publisher[ByteBuffer]).pure[F] @@ -109,10 +107,10 @@ object AsyncHttpClientFs2Backend { ) def apply[F[_]: ConcurrentEffect: ContextShift]( - blocker: Blocker, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + blocker: Blocker, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): F[WebSocketStreamBackend[F, Fs2Streams[F]]] = implicitly[Sync[F]] .delay( @@ -128,10 +126,10 @@ object AsyncHttpClientFs2Backend { /** Makes sure the backend is closed after usage. */ def resource[F[_]: ConcurrentEffect: ContextShift]( - blocker: Blocker, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + blocker: Blocker, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): Resource[F, WebSocketStreamBackend[F, Fs2Streams[F]]] = Resource.make(apply(blocker, options, customizeRequest, webSocketBufferCapacity))(_.close()) @@ -159,11 +157,11 @@ object AsyncHttpClientFs2Backend { * A function which updates the default configuration (created basing on `options`). */ def usingConfigBuilder[F[_]: ConcurrentEffect: ContextShift]( - blocker: Blocker, - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + blocker: Blocker, + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): F[WebSocketStreamBackend[F, Fs2Streams[F]]] = implicitly[Sync[F]].delay( AsyncHttpClientFs2Backend[F]( @@ -180,11 +178,11 @@ object AsyncHttpClientFs2Backend { * A function which updates the default configuration (created basing on `options`). */ def resourceUsingConfigBuilder[F[_]: ConcurrentEffect: ContextShift]( - blocker: Blocker, - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + blocker: Blocker, + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): Resource[F, WebSocketStreamBackend[F, Fs2Streams[F]]] = Resource.make(usingConfigBuilder(blocker, updateConfig, options, customizeRequest, webSocketBufferCapacity))( _.close() diff --git a/async-http-client-backend/fs2/src/main/scala/sttp/client4/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala b/async-http-client-backend/fs2/src/main/scala/sttp/client4/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala index f0a682b673..106f203385 100644 --- a/async-http-client-backend/fs2/src/main/scala/sttp/client4/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala +++ b/async-http-client-backend/fs2/src/main/scala/sttp/client4/asynchttpclient/fs2/AsyncHttpClientFs2Backend.scala @@ -18,7 +18,7 @@ import sttp.client4.internal._ import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.WebSocketStreamBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, WebSocketStreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketStreamBackend} import sttp.monad.MonadAsyncError import sttp.ws.{WebSocket, WebSocketFrame} @@ -49,20 +49,18 @@ class AsyncHttpClientFs2Backend[F[_]: Async] private ( override def publisherToStream(p: Publisher[ByteBuffer]): Stream[F, Byte] = p.toStream[F].flatMap(buf => Stream.chunk(Chunk.byteBuffer(buf))) - override def publisherToBytes(p: Publisher[ByteBuffer]): F[Array[Byte]] = { + override def publisherToBytes(p: Publisher[ByteBuffer]): F[Array[Byte]] = p.toStream[F] .compile .fold(scala.collection.immutable.Queue.empty[Array[Byte]])(enqueueBytes) .map(concatBytes) - } - override def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = { + override def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = p.toStream[F] .flatMap(b => Stream.emits(b.array())) .through(Files[F].writeAll(f.toPath)) .compile .drain - } override def bytesToPublisher(b: Array[Byte]): F[Publisher[ByteBuffer]] = (StreamUnicastPublisher(Stream.apply[F, ByteBuffer](ByteBuffer.wrap(b)), dispatcher): Publisher[ByteBuffer]) @@ -86,7 +84,7 @@ class AsyncHttpClientFs2Backend[F[_]: Async] private ( override val streams: Fs2Streams[F] = Fs2Streams[F] override protected def streamToPublisher(s: Stream[F, Byte]): Publisher[ByteBuf] = - (StreamUnicastPublisher(s.chunks.map(c => Unpooled.wrappedBuffer(c.toArray)), dispatcher): Publisher[ByteBuf]) + StreamUnicastPublisher(s.chunks.map(c => Unpooled.wrappedBuffer(c.toArray)), dispatcher): Publisher[ByteBuf] } override protected def createSimpleQueue[T]: F[SimpleQueue[F, T]] = @@ -108,10 +106,10 @@ object AsyncHttpClientFs2Backend { ) def apply[F[_]: Async]( - dispatcher: Dispatcher[F], - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + dispatcher: Dispatcher[F], + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): F[WebSocketStreamBackend[F, Fs2Streams[F]]] = Sync[F] .delay( @@ -126,9 +124,9 @@ object AsyncHttpClientFs2Backend { /** Makes sure the backend is closed after usage. */ def resource[F[_]: Async]( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): Resource[F, WebSocketStreamBackend[F, Fs2Streams[F]]] = Dispatcher .parallel[F] @@ -166,11 +164,11 @@ object AsyncHttpClientFs2Backend { /** @param updateConfig A function which updates the default configuration (created basing on `options`). */ def usingConfigBuilder[F[_]: Async]( - dispatcher: Dispatcher[F], - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + dispatcher: Dispatcher[F], + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): F[WebSocketStreamBackend[F, Fs2Streams[F]]] = Sync[F].delay( AsyncHttpClientFs2Backend[F]( @@ -187,10 +185,10 @@ object AsyncHttpClientFs2Backend { * A function which updates the default configuration (created basing on `options`). */ def resourceUsingConfigBuilder[F[_]: Async]( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): Resource[F, WebSocketStreamBackend[F, Fs2Streams[F]]] = Dispatcher .parallel[F] diff --git a/async-http-client-backend/future/src/main/scala/sttp/client4/asynchttpclient/future/AsyncHttpClientFutureBackend.scala b/async-http-client-backend/future/src/main/scala/sttp/client4/asynchttpclient/future/AsyncHttpClientFutureBackend.scala index 0c305892c1..c913d10a84 100644 --- a/async-http-client-backend/future/src/main/scala/sttp/client4/asynchttpclient/future/AsyncHttpClientFutureBackend.scala +++ b/async-http-client-backend/future/src/main/scala/sttp/client4/asynchttpclient/future/AsyncHttpClientFutureBackend.scala @@ -2,14 +2,20 @@ package sttp.client4.asynchttpclient.future import java.nio.ByteBuffer import io.netty.buffer.ByteBuf -import org.asynchttpclient.{AsyncHttpClient, AsyncHttpClientConfig, BoundRequestBuilder, DefaultAsyncHttpClient, DefaultAsyncHttpClientConfig} +import org.asynchttpclient.{ + AsyncHttpClient, + AsyncHttpClientConfig, + BoundRequestBuilder, + DefaultAsyncHttpClient, + DefaultAsyncHttpClientConfig +} import org.reactivestreams.Publisher import sttp.client4.asynchttpclient.{AsyncHttpClientBackend, BodyFromAHC, BodyToAHC} import sttp.client4.internal.NoStreams import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.BackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{Backend, BackendOptions, wrappers} +import sttp.client4.{wrappers, Backend, BackendOptions} import sttp.monad.{FutureMonad, MonadAsyncError} import sttp.ws.WebSocket @@ -64,8 +70,8 @@ object AsyncHttpClientFutureBackend { * execution context. */ def apply( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity )(implicit ec: ExecutionContext = ExecutionContext.global): Backend[Future] = AsyncHttpClientFutureBackend(AsyncHttpClientBackend.defaultClient(options), closeClient = true, customizeRequest) @@ -86,9 +92,9 @@ object AsyncHttpClientFutureBackend { * execution context. */ def usingConfigBuilder( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity )(implicit ec: ExecutionContext = ExecutionContext.global): Backend[Future] = AsyncHttpClientFutureBackend( AsyncHttpClientBackend.clientWithModifiedOptions(options, updateConfig), diff --git a/async-http-client-backend/monix/src/main/scala/sttp/client4/asynchttpclient/monix/AsyncHttpClientMonixBackend.scala b/async-http-client-backend/monix/src/main/scala/sttp/client4/asynchttpclient/monix/AsyncHttpClientMonixBackend.scala index 97964916d7..1cd8ac1db4 100644 --- a/async-http-client-backend/monix/src/main/scala/sttp/client4/asynchttpclient/monix/AsyncHttpClientMonixBackend.scala +++ b/async-http-client-backend/monix/src/main/scala/sttp/client4/asynchttpclient/monix/AsyncHttpClientMonixBackend.scala @@ -16,7 +16,7 @@ import sttp.client4.internal._ import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.WebSocketStreamBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, WebSocketStreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketStreamBackend} import sttp.monad.MonadAsyncError import sttp.ws.{WebSocket, WebSocketFrame} @@ -49,20 +49,18 @@ class AsyncHttpClientMonixBackend private ( override def publisherToStream(p: Publisher[ByteBuffer]): Observable[Array[Byte]] = Observable.fromReactivePublisher(p).map(_.safeRead()) - override def publisherToBytes(p: Publisher[ByteBuffer]): Task[Array[Byte]] = { + override def publisherToBytes(p: Publisher[ByteBuffer]): Task[Array[Byte]] = Observable .fromReactivePublisher(p) .foldLeftL(Queue.empty[Array[Byte]])(enqueueBytes) .map(concatBytes) - } - override def publisherToFile(p: Publisher[ByteBuffer], f: File): Task[Unit] = { + override def publisherToFile(p: Publisher[ByteBuffer], f: File): Task[Unit] = Observable .fromReactivePublisher(p) .map(_.array()) .consumeWith(writeAsync(f.toPath)) .map(_ => ()) - } override def bytesToPublisher(b: Array[Byte]): Task[Publisher[ByteBuffer]] = Task.now(Observable.eval(ByteBuffer.wrap(b)).toReactivePublisher) @@ -102,9 +100,9 @@ object AsyncHttpClientMonixBackend { /** @param s The scheduler used for streaming request bodies. Defaults to the global scheduler. */ def apply( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity )(implicit s: Scheduler = Scheduler.global ): Task[WebSocketStreamBackend[Task, MonixStreams]] = @@ -122,9 +120,9 @@ object AsyncHttpClientMonixBackend { * The scheduler used for streaming request bodies. Defaults to the global scheduler. */ def resource( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity )(implicit s: Scheduler = Scheduler.global ): Resource[Task, WebSocketStreamBackend[Task, MonixStreams]] = @@ -166,10 +164,10 @@ object AsyncHttpClientMonixBackend { * The scheduler used for streaming request bodies. Defaults to the global scheduler. */ def usingConfigBuilder( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity )(implicit s: Scheduler = Scheduler.global ): Task[WebSocketStreamBackend[Task, MonixStreams]] = @@ -189,10 +187,10 @@ object AsyncHttpClientMonixBackend { * The scheduler used for streaming request bodies. Defaults to the global scheduler. */ def resourceUsingConfigBuilder( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity )(implicit s: Scheduler = Scheduler.global ): Resource[Task, WebSocketStreamBackend[Task, MonixStreams]] = diff --git a/async-http-client-backend/scalaz/src/main/scala/sttp/client4/asynchttpclient/scalaz/AsyncHttpClientScalazBackend.scala b/async-http-client-backend/scalaz/src/main/scala/sttp/client4/asynchttpclient/scalaz/AsyncHttpClientScalazBackend.scala index fcec8136ef..3f130403f2 100644 --- a/async-http-client-backend/scalaz/src/main/scala/sttp/client4/asynchttpclient/scalaz/AsyncHttpClientScalazBackend.scala +++ b/async-http-client-backend/scalaz/src/main/scala/sttp/client4/asynchttpclient/scalaz/AsyncHttpClientScalazBackend.scala @@ -2,7 +2,13 @@ package sttp.client4.asynchttpclient.scalaz import java.nio.ByteBuffer import io.netty.buffer.ByteBuf -import org.asynchttpclient.{AsyncHttpClient, AsyncHttpClientConfig, BoundRequestBuilder, DefaultAsyncHttpClient, DefaultAsyncHttpClientConfig} +import org.asynchttpclient.{ + AsyncHttpClient, + AsyncHttpClientConfig, + BoundRequestBuilder, + DefaultAsyncHttpClient, + DefaultAsyncHttpClientConfig +} import org.reactivestreams.Publisher import scalaz.concurrent.Task import sttp.client4.asynchttpclient.{AsyncHttpClientBackend, BodyFromAHC, BodyToAHC} @@ -11,7 +17,7 @@ import sttp.client4.internal.NoStreams import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.BackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{Backend, BackendOptions, wrappers} +import sttp.client4.{wrappers, Backend, BackendOptions} import sttp.monad.MonadAsyncError import sttp.ws.WebSocket @@ -55,8 +61,8 @@ object AsyncHttpClientScalazBackend { wrappers.FollowRedirectsBackend(new AsyncHttpClientScalazBackend(asyncHttpClient, closeClient, customizeRequest)) def apply( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): Task[Backend[Task]] = Task.delay( AsyncHttpClientScalazBackend(AsyncHttpClientBackend.defaultClient(options), closeClient = true, customizeRequest) @@ -72,9 +78,9 @@ object AsyncHttpClientScalazBackend { * A function which updates the default configuration (created basing on `options`). */ def usingConfigBuilder( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity ): Task[Backend[Task]] = Task.delay( AsyncHttpClientScalazBackend( diff --git a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/AsyncHttpClientBackend.scala b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/AsyncHttpClientBackend.scala index 9ac907e2ca..309acb06d0 100644 --- a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/AsyncHttpClientBackend.scala +++ b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/AsyncHttpClientBackend.scala @@ -6,7 +6,7 @@ import io.netty.handler.codec.http.HttpHeaders import org.asynchttpclient.AsyncHandler.State import org.asynchttpclient.handler.StreamedAsyncHandler import org.asynchttpclient.proxy.ProxyServer -import org.asynchttpclient.ws.{WebSocketListener, WebSocketUpgradeHandler, WebSocket => AHCWebSocket} +import org.asynchttpclient.ws.{WebSocket => AHCWebSocket, WebSocketListener, WebSocketUpgradeHandler} import org.asynchttpclient.{ AsyncHandler, AsyncHttpClient, @@ -16,8 +16,8 @@ import org.asynchttpclient.{ HttpResponseBodyPart, HttpResponseStatus, Realm, - RequestBuilder, Request => AsyncRequest, + RequestBuilder, Response => AsyncResponse } import org.reactivestreams.{Publisher, Subscriber, Subscription} @@ -27,7 +27,7 @@ import sttp.client4.BackendOptions.ProxyType.{Http, Socks} import sttp.client4.internal.ws.{SimpleQueue, WebSocketEvent} import sttp.monad.syntax._ import sttp.monad.{Canceler, MonadAsyncError, MonadError} -import sttp.client4.{GenericBackend, Response, BackendOptions, _} +import sttp.client4.{BackendOptions, GenericBackend, Response, _} import sttp.model._ import scala.collection.JavaConverters._ @@ -52,7 +52,7 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( } } - private def sendRegular[T](r: GenericRequest[T, R], ahcRequest: BoundRequestBuilder): F[Response[T]] = { + private def sendRegular[T](r: GenericRequest[T, R], ahcRequest: BoundRequestBuilder): F[Response[T]] = monad.flatten(monad.async[F[Response[T]]] { cb => def success(r: F[Response[T]]): Unit = cb(Right(r)) def error(t: Throwable): Unit = cb(Left(t)) @@ -60,7 +60,6 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( val lf = ahcRequest.execute(streamingAsyncHandler(r, success, error)) Canceler(() => lf.cancel(true)) }) - } private def sendWebSocket[T, R](r: GenericRequest[T, R], ahcRequest: BoundRequestBuilder): F[Response[T]] = createSimpleQueue[WebSocketEvent].flatMap { queue => @@ -83,10 +82,10 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( protected def createSimpleQueue[T]: F[SimpleQueue[F, T]] private def streamingAsyncHandler[T, R]( - request: GenericRequest[T, R], - success: F[Response[T]] => Unit, - error: Throwable => Unit - ): AsyncHandler[Unit] = { + request: GenericRequest[T, R], + success: F[Response[T]] => Unit, + error: Throwable => Unit + ): AsyncHandler[Unit] = new StreamedAsyncHandler[Unit] { private val builder = new AsyncResponse.ResponseBuilder() private var publisher: Option[Publisher[ByteBuffer]] = None @@ -129,12 +128,11 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( State.CONTINUE } - override def onCompleted(): Unit = { + override def onCompleted(): Unit = // if the request had no body, onStream() will never be called doComplete() - } - private def doComplete(): Unit = { + private def doComplete(): Unit = if (!completed) { completed = true @@ -144,19 +142,16 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( success(b.map(t => baseResponse.copy(body = t))) } - } - override def onThrowable(t: Throwable): Unit = { + override def onThrowable(t: Throwable): Unit = error(t) - } } - } private class WebSocketInitListener[T]( - request: GenericRequest[T, _], - queue: SimpleQueue[F, WebSocketEvent], - success: F[Response[T]] => Unit, - error: Throwable => Unit + request: GenericRequest[T, _], + queue: SimpleQueue[F, WebSocketEvent], + success: F[Response[T]] => Unit, + error: Throwable => Unit ) extends WebSocketListener { override def onOpen(ahcWebSocket: AHCWebSocket): Unit = { ahcWebSocket.removeWebSocketListener(this) @@ -175,16 +170,14 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( success(bf.map(b => baseResponse.copy(body = b))) } - override def onClose(webSocket: AHCWebSocket, code: Int, reason: String): Unit = { + override def onClose(webSocket: AHCWebSocket, code: Int, reason: String): Unit = throw new IllegalStateException("Should never be called, as the listener should be removed after onOpen") - } override def onError(t: Throwable): Unit = error(t) } - private def preparedRequest[R](r: GenericRequest[_, R]): F[BoundRequestBuilder] = { + private def preparedRequest[R](r: GenericRequest[_, R]): F[BoundRequestBuilder] = monad.fromTry(Try(asyncHttpClient.prepareRequest(requestToAsync(r)))).map(customizeRequest) - } private def requestToAsync[R](r: GenericRequest[_, R]): AsyncRequest = { val readTimeout = r.options.readTimeout @@ -192,12 +185,12 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( .setUrl(r.uri.toString) .setReadTimeout(if (readTimeout.isFinite) readTimeout.toMillis.toInt else -1) .setRequestTimeout(if (readTimeout.isFinite) readTimeout.toMillis.toInt else -1) - r.headers.foreach { header => rb.setHeader(header.name, header.value) } + r.headers.foreach(header => rb.setHeader(header.name, header.value)) bodyToAHC(r, r.body, rb) rb.build() } - private def readResponseNoBody(request: GenericRequest[_, _], response: AsyncResponse): Response[Unit] = { + private def readResponseNoBody(request: GenericRequest[_, _], response: AsyncResponse): Response[Unit] = client4.Response( (), StatusCode.unsafeApply(response.getStatusCode), @@ -206,7 +199,6 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( Nil, request.onlyMetadata ) - } private def readHeaders(h: HttpHeaders): Seq[Header] = h.iteratorAsString() @@ -214,9 +206,8 @@ abstract class AsyncHttpClientBackend[F[_], S <: Streams[S], P]( .map(e => Header(e.getKey, e.getValue)) .toList - override def close(): F[Unit] = { + override def close(): F[Unit] = if (closeClient) monad.eval(asyncHttpClient.close()) else monad.unit(()) - } private def adjustExceptions[T](request: GenericRequest[_, _])(t: => F[T]): F[T] = SttpClientException.adjustExceptions(monad)(t)( @@ -259,14 +250,12 @@ object AsyncHttpClientBackend { } } - private[asynchttpclient] def defaultClient(options: BackendOptions): AsyncHttpClient = { + private[asynchttpclient] def defaultClient(options: BackendOptions): AsyncHttpClient = new DefaultAsyncHttpClient(defaultConfigBuilder(options).build()) - } private[asynchttpclient] def clientWithModifiedOptions( - options: BackendOptions, - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder - ): AsyncHttpClient = { + options: BackendOptions, + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder + ): AsyncHttpClient = new DefaultAsyncHttpClient(updateConfig(defaultConfigBuilder(options)).build()) - } } diff --git a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/BodyFromAHC.scala b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/BodyFromAHC.scala index d4ee85d3ae..437b9277e1 100644 --- a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/BodyFromAHC.scala +++ b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/BodyFromAHC.scala @@ -18,7 +18,7 @@ private[asynchttpclient] trait BodyFromAHC[F[_], S] { def publisherToStream(p: Publisher[ByteBuffer]): streams.BinaryStream - def publisherToBytes(p: Publisher[ByteBuffer]): F[Array[Byte]] = { + def publisherToBytes(p: Publisher[ByteBuffer]): F[Array[Byte]] = monad.async { cb => def success(r: ByteBuffer): Unit = cb(Right(r.array())) def error(t: Throwable): Unit = cb(Left(t)) @@ -28,11 +28,9 @@ private[asynchttpclient] trait BodyFromAHC[F[_], S] { Canceler(() => subscriber.cancel()) } - } - def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = { + def publisherToFile(p: Publisher[ByteBuffer], f: File): F[Unit] = publisherToBytes(p).map(bytes => FileHelpers.saveFile(f, new ByteArrayInputStream(bytes))) - } def bytesToPublisher(b: Array[Byte]): F[Publisher[ByteBuffer]] = (new SingleElementPublisher(ByteBuffer.wrap(b)): Publisher[ByteBuffer]).unit @@ -55,10 +53,9 @@ private[asynchttpclient] trait BodyFromAHC[F[_], S] { case Right(file) => fileToPublisher(file.toFile) } - override protected def regularIgnore(response: Publisher[ByteBuffer]): F[Unit] = { + override protected def regularIgnore(response: Publisher[ByteBuffer]): F[Unit] = // getting the body and discarding it publisherToBytes(response).map(_ => ((), nonReplayableBody)) - } override protected def regularAsByteArray(response: Publisher[ByteBuffer]): F[Array[Byte]] = publisherToBytes(response) @@ -87,10 +84,10 @@ private[asynchttpclient] trait BodyFromAHC[F[_], S] { } def apply[TT]( - response: Either[Publisher[ByteBuffer], WebSocket[F]], - responseAs: ResponseAsDelegate[TT, _], - responseMetadata: ResponseMetadata, - isSubscribed: () => Boolean + response: Either[Publisher[ByteBuffer], WebSocket[F]], + responseAs: ResponseAsDelegate[TT, _], + responseMetadata: ResponseMetadata, + isSubscribed: () => Boolean ): F[TT] = bodyFromResponseAs(isSubscribed)(responseAs, responseMetadata, response) private def bodyFromWs[TT](r: GenericWebSocketResponseAs[TT, _], ws: WebSocket[F], meta: ResponseMetadata): F[TT] = @@ -102,15 +99,13 @@ private[asynchttpclient] trait BodyFromAHC[F[_], S] { compileWebSocketPipe(ws, p.asInstanceOf[streams.Pipe[WebSocketFrame.Data[_], WebSocketFrame]]) } - private def ignoreIfNotSubscribed(p: Publisher[ByteBuffer], isSubscribed: () => Boolean): F[Unit] = { + private def ignoreIfNotSubscribed(p: Publisher[ByteBuffer], isSubscribed: () => Boolean): F[Unit] = monad.eval(isSubscribed()).flatMap(is => if (is) monad.unit(()) else ignorePublisher(p)) - } - private def ignorePublisher(p: Publisher[ByteBuffer]): F[Unit] = { + private def ignorePublisher(p: Publisher[ByteBuffer]): F[Unit] = monad.async { cb => val subscriber = new IgnoreSubscriber(() => cb(Right(())), t => cb(Left(t))) p.subscribe(subscriber) Canceler(() => ()) } - } } diff --git a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/BodyToAHC.scala b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/BodyToAHC.scala index 0f3e1b6a51..4b2c27dd83 100644 --- a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/BodyToAHC.scala +++ b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/BodyToAHC.scala @@ -17,7 +17,7 @@ private[asynchttpclient] trait BodyToAHC[F[_], S] { val streams: Streams[S] protected def streamToPublisher(s: streams.BinaryStream): Publisher[ByteBuf] - def apply[R](r: GenericRequest[_, R], body: GenericRequestBody[R], rb: RequestBuilder): Unit = { + def apply[R](r: GenericRequest[_, R], body: GenericRequestBody[R], rb: RequestBuilder): Unit = body match { case NoBody => // skip case StringBody(b, encoding, _) => @@ -45,14 +45,13 @@ private[asynchttpclient] trait BodyToAHC[F[_], S] { case m: MultipartBody[_] => m.parts.foreach(addMultipartBody(rb, _)) } - } private def addMultipartBody(rb: RequestBuilder, mp: Part[BodyPart[_]]): 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""" } + def nameWithFilename = mp.fileName.fold(mp.name)(fn => s"""${mp.name}"; ${Part.FileNameDispositionParam}="$fn""") val ctOrNull = mp.contentType.orNull diff --git a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/WebSocketImpl.scala b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/WebSocketImpl.scala index b248592c7f..27fa253d11 100644 --- a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/WebSocketImpl.scala +++ b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/WebSocketImpl.scala @@ -19,7 +19,7 @@ private[asynchttpclient] class WebSocketImpl[F[_]]( implicit val monad: MonadAsyncError[F] ) extends WebSocket[F] { - override def receive(): F[WebSocketFrame] = { + override def receive(): F[WebSocketFrame] = queue.poll.flatMap { case WebSocketEvent.Open() => receive() case WebSocketEvent.Frame(c: WebSocketFrame.Close) => @@ -32,7 +32,6 @@ private[asynchttpclient] class WebSocketImpl[F[_]]( case WebSocketEvent.Error(t) => throw t case WebSocketEvent.Frame(f: WebSocketFrame) => monad.unit(f) } - } override def send(f: WebSocketFrame, isContinuation: Boolean = false): F[Unit] = monad.flatten(monad.eval(f match { @@ -60,17 +59,15 @@ private[asynchttpclient] class WebSocketImpl[F[_]]( override def isOpen(): F[Boolean] = monad.eval(_isOpen.get()) - private def fromNettyFuture(f: io.netty.util.concurrent.Future[Void]): F[Unit] = { + private def fromNettyFuture(f: io.netty.util.concurrent.Future[Void]): F[Unit] = monad.async { cb => val f2 = f.addListener(new FutureListener[Void] { - override def operationComplete(future: Future[Void]): Unit = { + override def operationComplete(future: Future[Void]): Unit = if (future.isSuccess) cb(Right(())) else cb(Left(future.cause())) - } }) Canceler(() => f2.cancel(true)) } - } } object WebSocketImpl { @@ -86,27 +83,23 @@ object WebSocketImpl { class AddToQueueListener[F[_]](queue: SimpleQueue[F, WebSocketEvent], isOpen: AtomicBoolean) extends AHCWebSocketListener { - override def onOpen(websocket: AHCWebSocket): Unit = { + override def onOpen(websocket: AHCWebSocket): Unit = throw new IllegalStateException("Should never be called!") - } - override def onClose(websocket: AHCWebSocket, code: Int, reason: String): Unit = { + override def onClose(websocket: AHCWebSocket, code: Int, reason: String): Unit = if (isOpen.getAndSet(false)) { queue.offer(WebSocketEvent.Frame(WebSocketFrame.Close(code, reason))) } - } - override def onError(t: Throwable): Unit = { + override def onError(t: Throwable): Unit = if (isOpen.getAndSet(false)) { queue.offer(WebSocketEvent.Error(t)) } - } override def onBinaryFrame(payload: Array[Byte], finalFragment: Boolean, rsv: Int): Unit = onFrame(WebSocketFrame.Binary(payload, finalFragment, rsvToOption(rsv))) - override def onTextFrame(payload: String, finalFragment: Boolean, rsv: Int): Unit = { + override def onTextFrame(payload: String, finalFragment: Boolean, rsv: Int): Unit = onFrame(WebSocketFrame.Text(payload, finalFragment, rsvToOption(rsv))) - } override def onPingFrame(payload: Array[Byte]): Unit = onFrame(WebSocketFrame.Ping(payload)) override def onPongFrame(payload: Array[Byte]): Unit = onFrame(WebSocketFrame.Pong(payload)) diff --git a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/reactive.scala b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/reactive.scala index bcfc9e0e54..e6267b8460 100644 --- a/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/reactive.scala +++ b/async-http-client-backend/src/main/scala/sttp/client4/asynchttpclient/reactive.scala @@ -10,9 +10,8 @@ import org.reactivestreams.{Publisher, Subscriber, Subscription} import scala.collection.JavaConverters._ private[asynchttpclient] object EmptyPublisher extends Publisher[ByteBuffer] { - override def subscribe(s: Subscriber[_ >: ByteBuffer]): Unit = { + override def subscribe(s: Subscriber[_ >: ByteBuffer]): Unit = s.onComplete() - } } // based on org.asynchttpclient.request.body.generator.ReactiveStreamsBodyGenerator.SimpleSubscriber @@ -68,7 +67,7 @@ private[asynchttpclient] class SimpleSubscriber(success: ByteBuffer => Unit, err success(result) } - def cancel(): Unit = { + def cancel(): Unit = // subscription.cancel is idempotent: // https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#specification // so the following can be safely retried @@ -78,7 +77,6 @@ private[asynchttpclient] class SimpleSubscriber(success: ByteBuffer => Unit, err (true, null) } }) - } } /** A subscriber which does its best to signal that it's not interested in the data being sent. @@ -94,17 +92,15 @@ private[asynchttpclient] class IgnoreSubscriber(success: () => Unit, error: Thro // ignore } - override def onError(t: Throwable): Unit = { + override def onError(t: Throwable): Unit = error(t) - } - override def onComplete(): Unit = { + override def onComplete(): Unit = success() - } } private[asynchttpclient] class SingleElementPublisher[T](v: T) extends Publisher[T] { - override def subscribe(s: Subscriber[_ >: T]): Unit = { + override def subscribe(s: Subscriber[_ >: T]): Unit = s.onSubscribe(new Subscription { override def request(n: Long): Unit = if (n > 0) { @@ -114,5 +110,4 @@ private[asynchttpclient] class SingleElementPublisher[T](v: T) extends Publisher override def cancel(): Unit = {} }) - } } diff --git a/async-http-client-backend/zio/src/main/scala/sttp/client4/asynchttpclient/zio/AsyncHttpClientZioBackend.scala b/async-http-client-backend/zio/src/main/scala/sttp/client4/asynchttpclient/zio/AsyncHttpClientZioBackend.scala index b948d07e86..bd2df69028 100644 --- a/async-http-client-backend/zio/src/main/scala/sttp/client4/asynchttpclient/zio/AsyncHttpClientZioBackend.scala +++ b/async-http-client-backend/zio/src/main/scala/sttp/client4/asynchttpclient/zio/AsyncHttpClientZioBackend.scala @@ -1,7 +1,10 @@ package sttp.client4.asynchttpclient.zio import _root_.zio._ -import _root_.zio.interop.reactivestreams.{publisherToStream => publisherToZioStream, streamToPublisher => zioStreamToPublisher} +import _root_.zio.interop.reactivestreams.{ + publisherToStream => publisherToZioStream, + streamToPublisher => zioStreamToPublisher +} import _root_.zio.stream._ import io.netty.buffer.{ByteBuf, Unpooled} import org.asynchttpclient._ @@ -15,7 +18,7 @@ import sttp.client4.internal._ import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.WebSocketStreamBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, WebSocketStreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketStreamBackend} import sttp.monad.MonadAsyncError import sttp.ws.{WebSocket, WebSocketFrame} @@ -54,13 +57,12 @@ class AsyncHttpClientZioBackend private ( .runFold(immutable.Queue.empty[Array[Byte]])(enqueueBytes) .map(concatBytes) - override def publisherToFile(p: Publisher[ByteBuffer], f: File): Task[Unit] = { + override def publisherToFile(p: Publisher[ByteBuffer], f: File): Task[Unit] = p.toZIOStream(bufferSize) .map(Chunk.fromByteBuffer) .flattenChunks .run(ZSink.fromOutputStream(new FileOutputStream(f))) .unit - } override def bytesToPublisher(b: Array[Byte]): Task[Publisher[ByteBuffer]] = ZStream.apply(ByteBuffer.wrap(b)).toPublisher diff --git a/async-http-client-backend/zio1/src/main/scala/sttp/client4/asynchttpclient/zio/AsyncHttpClientZioBackend.scala b/async-http-client-backend/zio1/src/main/scala/sttp/client4/asynchttpclient/zio/AsyncHttpClientZioBackend.scala index 1a765bcbce..a983f3d315 100644 --- a/async-http-client-backend/zio1/src/main/scala/sttp/client4/asynchttpclient/zio/AsyncHttpClientZioBackend.scala +++ b/async-http-client-backend/zio1/src/main/scala/sttp/client4/asynchttpclient/zio/AsyncHttpClientZioBackend.scala @@ -2,7 +2,10 @@ package sttp.client4.asynchttpclient.zio import _root_.zio._ import _root_.zio.blocking.Blocking -import _root_.zio.interop.reactivestreams.{publisherToStream => publisherToZioStream, streamToPublisher => zioStreamToPublisher} +import _root_.zio.interop.reactivestreams.{ + publisherToStream => publisherToZioStream, + streamToPublisher => zioStreamToPublisher +} import _root_.zio.stream._ import io.netty.buffer.{ByteBuf, Unpooled} import org.asynchttpclient._ @@ -15,7 +18,7 @@ import sttp.client4.internal._ import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.WebSocketStreamBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, WebSocketStreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketStreamBackend} import sttp.monad.MonadAsyncError import sttp.ws.{WebSocket, WebSocketFrame} @@ -54,14 +57,13 @@ class AsyncHttpClientZioBackend private ( .fold(immutable.Queue.empty[Array[Byte]])(enqueueBytes) .map(concatBytes) - override def publisherToFile(p: Publisher[ByteBuffer], f: File): Task[Unit] = { + override def publisherToFile(p: Publisher[ByteBuffer], f: File): Task[Unit] = p.toStream(bufferSize) .map(Chunk.fromByteBuffer) .flattenChunks .run(ZSink.fromOutputStream(new FileOutputStream(f))) .unit .provideLayer(Blocking.live) - } override def bytesToPublisher(b: Array[Byte]): Task[Publisher[ByteBuffer]] = Stream.apply(ByteBuffer.wrap(b)).toPublisher @@ -109,9 +111,9 @@ object AsyncHttpClientZioBackend { ) def apply( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): Task[WebSocketStreamBackend[Task, ZioStreams]] = ZIO .runtime[Any] @@ -128,16 +130,16 @@ object AsyncHttpClientZioBackend { ) def managed( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): TaskManaged[WebSocketStreamBackend[Task, ZioStreams]] = ZManaged.make(apply(options, customizeRequest, webSocketBufferCapacity))(_.close().ignore) def layer( - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): Layer[Throwable, SttpClient] = ZLayer.fromManaged(managed(options, customizeRequest, webSocketBufferCapacity)) @@ -178,10 +180,10 @@ object AsyncHttpClientZioBackend { * A function which updates the default configuration (created basing on `options`). */ def usingConfigBuilder( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): Task[WebSocketStreamBackend[Task, ZioStreams]] = ZIO .runtime[Any] @@ -201,10 +203,10 @@ object AsyncHttpClientZioBackend { * A function which updates the default configuration (created basing on `options`). */ def managedUsingConfigBuilder( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): TaskManaged[WebSocketStreamBackend[Task, ZioStreams]] = ZManaged.make(usingConfigBuilder(updateConfig, options, customizeRequest, webSocketBufferCapacity))( _.close().ignore @@ -214,10 +216,10 @@ object AsyncHttpClientZioBackend { * A function which updates the default configuration (created basing on `options`). */ def layerUsingConfigBuilder( - updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, - options: BackendOptions = BackendOptions.Default, - customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, - webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity + updateConfig: DefaultAsyncHttpClientConfig.Builder => DefaultAsyncHttpClientConfig.Builder, + options: BackendOptions = BackendOptions.Default, + customizeRequest: BoundRequestBuilder => BoundRequestBuilder = identity, + webSocketBufferCapacity: Option[Int] = AsyncHttpClientBackend.DefaultWebSocketBufferCapacity ): Layer[Throwable, SttpClient] = ZLayer.fromManaged(managedUsingConfigBuilder(updateConfig, options, customizeRequest, webSocketBufferCapacity)) diff --git a/core/src/main/scala/sttp/client4/BackendOptions.scala b/core/src/main/scala/sttp/client4/BackendOptions.scala index 0f03bfd4ee..3ff520812e 100644 --- a/core/src/main/scala/sttp/client4/BackendOptions.scala +++ b/core/src/main/scala/sttp/client4/BackendOptions.scala @@ -40,7 +40,7 @@ object BackendOptions { onlyProxyHosts: List[String] = Nil ) { // only matches prefix or suffix wild card(*) - private def isWildCardMatch(targetHost: String, nonProxyHost: String): Boolean = { + private def isWildCardMatch(targetHost: String, nonProxyHost: String): Boolean = if (nonProxyHost.length > 1) { if (nonProxyHost.charAt(0) == '*') { targetHost.regionMatches( @@ -58,7 +58,6 @@ object BackendOptions { } else { nonProxyHost.equalsIgnoreCase(targetHost) } - } def ignoreProxy(host: String): Boolean = matchesNonProxyHost(host) || doesNotMatchAnyHostToProxy(host) @@ -82,12 +81,11 @@ object BackendOptions { proxyList } - override def connectFailed(uri: net.URI, sa: SocketAddress, ioe: IOException): Unit = { + override def connectFailed(uri: net.URI, sa: SocketAddress, ioe: IOException): Unit = throw new UnsupportedOperationException( s"Couldn't connect to the proxy server, uri: $uri, socket: $sa", ioe ) - } } def asJavaProxy = new java.net.Proxy(proxyType.asJava, inetSocketAddress) def inetSocketAddress: InetSocketAddress = @@ -140,14 +138,13 @@ object BackendOptions { ) = { val host = Option(System.getProperty(hostProp)) def port = Try(System.getProperty(portProp).toInt).getOrElse(defaultPort) - def nonProxyHosts: List[String] = { + def nonProxyHosts: List[String] = nonProxyHostsPropOption .map(nonProxyHostsProp => Try(Option(System.getProperty(nonProxyHostsProp))).toOption.flatten.getOrElse("localhost|127.*") ) .map(_.split('|').toList) .getOrElse(Nil) - } host.map(make(_, port, nonProxyHosts)) } diff --git a/core/src/main/scala/sttp/client4/ResponseAs.scala b/core/src/main/scala/sttp/client4/ResponseAs.scala index 3e4f893e8f..484a25855b 100644 --- a/core/src/main/scala/sttp/client4/ResponseAs.scala +++ b/core/src/main/scala/sttp/client4/ResponseAs.scala @@ -249,7 +249,7 @@ sealed trait GenericResponseAs[+T, -R] { } object GenericResponseAs { - private[client4] def parseParams(s: String, charset: String): Seq[(String, String)] = { + private[client4] def parseParams(s: String, charset: String): Seq[(String, String)] = s.split("&") .toList .flatMap(kv => @@ -259,7 +259,6 @@ object GenericResponseAs { case _ => None } ) - } def isWebSocket(ra: GenericResponseAs[_, _]): Boolean = ra match { diff --git a/core/src/main/scala/sttp/client4/RetryWhen.scala b/core/src/main/scala/sttp/client4/RetryWhen.scala index 1f0e5b38dd..e475bf862d 100644 --- a/core/src/main/scala/sttp/client4/RetryWhen.scala +++ b/core/src/main/scala/sttp/client4/RetryWhen.scala @@ -3,7 +3,7 @@ package sttp.client4 import sttp.model.Method object RetryWhen { - def isBodyRetryable(body: GenericRequestBody[_]): Boolean = { + def isBodyRetryable(body: GenericRequestBody[_]): Boolean = body match { case NoBody => true case _: StringBody => true @@ -14,7 +14,6 @@ object RetryWhen { case StreamBody(_) => false case m: MultipartBody[_] => m.parts.forall(p => isBodyRetryable(p.body)) } - } val Default: RetryWhen = { case (_, Left(_: SttpClientException.ConnectException)) => true diff --git a/core/src/main/scala/sttp/client4/SpecifyAuthScheme.scala b/core/src/main/scala/sttp/client4/SpecifyAuthScheme.scala index 00e7ac40a0..81e8e3be2c 100644 --- a/core/src/main/scala/sttp/client4/SpecifyAuthScheme.scala +++ b/core/src/main/scala/sttp/client4/SpecifyAuthScheme.scala @@ -20,7 +20,6 @@ class SpecifyAuthScheme[+R <: PartialRequestBuilder[R, _]]( def bearer(token: String): R = req.header(hn, s"Bearer $token") - def digest(user: String, password: String): R = { + def digest(user: String, password: String): R = req.tag(digestTag, DigestAuthenticator.DigestAuthData(user, password)) - } } diff --git a/core/src/main/scala/sttp/client4/SttpApi.scala b/core/src/main/scala/sttp/client4/SttpApi.scala index a9cddcf445..a043972527 100644 --- a/core/src/main/scala/sttp/client4/SttpApi.scala +++ b/core/src/main/scala/sttp/client4/SttpApi.scala @@ -106,7 +106,8 @@ trait SttpApi extends SttpExtensions with UriInterpolator { fromMetadata(onError.map(Left(_)), ConditionalResponseAs(_.isSuccess, onSuccess.map(Right(_)))) .showAs(s"either(${onError.show}, ${onSuccess.show})") - /** Use both `l` and `r` to read the response body. Neither response specifications may use streaming or web sockets. */ + /** Use both `l` and `r` to read the response body. Neither response specifications may use streaming or web sockets. + */ def asBoth[A, B](l: ResponseAs[A], r: ResponseAs[B]): ResponseAs[(A, B)] = asBothOption(l, r) .map { case (a, bo) => @@ -132,9 +133,8 @@ trait SttpApi extends SttpExtensions with UriInterpolator { /** Content type will be set to `text/plain` with given encoding, can be overridden later using the `contentType` * method. */ - def multipart(name: String, data: String, encoding: String): Part[BasicBodyPart] = { + def multipart(name: String, data: String, encoding: String): Part[BasicBodyPart] = Part(name, StringBody(data, encoding), contentType = Some(MediaType.TextPlain.charset(encoding))) - } /** Content type will be set to `application/octet-stream`, can be overridden later using the `contentType` method. */ def multipart(name: String, data: Array[Byte]): Part[BasicBodyPart] = diff --git a/core/src/main/scala/sttp/client4/SttpClientException.scala b/core/src/main/scala/sttp/client4/SttpClientException.scala index 408565ad49..2290950030 100644 --- a/core/src/main/scala/sttp/client4/SttpClientException.scala +++ b/core/src/main/scala/sttp/client4/SttpClientException.scala @@ -30,9 +30,8 @@ object SttpClientException extends SttpClientExceptionExtensions { def adjustExceptions[F[_], T]( monadError: MonadError[F] - )(t: => F[T])(usingFn: Exception => Option[Exception]): F[T] = { + )(t: => F[T])(usingFn: Exception => Option[Exception]): F[T] = monadError.handleError(t) { case e: Exception => monadError.error(usingFn(e).getOrElse(e)) } - } } diff --git a/core/src/main/scala/sttp/client4/backend.scala b/core/src/main/scala/sttp/client4/backend.scala index c16ea6aaff..5f237000fc 100644 --- a/core/src/main/scala/sttp/client4/backend.scala +++ b/core/src/main/scala/sttp/client4/backend.scala @@ -7,8 +7,8 @@ import sttp.client4.monad.IdMonad /** A specific implementation of HTTP request sending logic. * * The [[send]] method '''should not''' be used directly by client code, if possible. Instead, the [[Request.send]], - * [[StreamRequest.send]], [[WebSocketRequest.send]] or [[WebSocketStreamRequest.send]] methods (depending on the - * type of the request) should be used, providing a specific backend instance as a parameter. + * [[StreamRequest.send]], [[WebSocketRequest.send]] or [[WebSocketStreamRequest.send]] methods (depending on the type + * of the request) should be used, providing a specific backend instance as a parameter. * * When creating an instance of a backend, one of the [[Backend]] traits should be mixed in, reflecting the effect type * and the `P` capabilities: [[Backend]], [[SyncBackend]], [[WebSocketBackend]], [[StreamBackend]], diff --git a/core/src/main/scala/sttp/client4/internal/BodyFromResponseAs.scala b/core/src/main/scala/sttp/client4/internal/BodyFromResponseAs.scala index 97afcbd2c8..17f3af1d06 100644 --- a/core/src/main/scala/sttp/client4/internal/BodyFromResponseAs.scala +++ b/core/src/main/scala/sttp/client4/internal/BodyFromResponseAs.scala @@ -8,17 +8,16 @@ import sttp.monad.syntax._ abstract class BodyFromResponseAs[F[_], RegularResponse, WSResponse, Stream](implicit m: MonadError[F]) { def apply[T]( - responseAs: ResponseAsDelegate[T, _], - meta: ResponseMetadata, - response: Either[RegularResponse, WSResponse] + responseAs: ResponseAsDelegate[T, _], + meta: ResponseMetadata, + response: Either[RegularResponse, WSResponse] ): F[T] = doApply(responseAs.delegate, meta, response).map(_._1) private def doApply[T]( responseAs: GenericResponseAs[T, _], meta: ResponseMetadata, response: Either[RegularResponse, WSResponse] - ): F[(T, ReplayableBody)] = { - + ): F[(T, ReplayableBody)] = (responseAs, response) match { case (MappedResponseAs(raw, g, _), _) => doApply(raw, meta, response).flatMap { case (result, replayableBody) => @@ -74,7 +73,6 @@ abstract class BodyFromResponseAs[F[_], RegularResponse, WSResponse, Stream](imp val e = new GotAWebSocketException() cleanupWhenGotWebSocket(ws, e).flatMap(_ => m.error(e)) } - } protected def withReplayableBody( response: RegularResponse, diff --git a/core/src/main/scala/sttp/client4/internal/DigestAuthenticator.scala b/core/src/main/scala/sttp/client4/internal/DigestAuthenticator.scala index 589d58e6b1..891957b7d2 100644 --- a/core/src/main/scala/sttp/client4/internal/DigestAuthenticator.scala +++ b/core/src/main/scala/sttp/client4/internal/DigestAuthenticator.scala @@ -8,22 +8,21 @@ import sttp.model.{Header, HeaderNames, StatusCode} import scala.util.Random -private[client4] class DigestAuthenticator private( +private[client4] class DigestAuthenticator private ( digestAuthData: DigestAuthData, requestHeaderName: String, responseHeaderName: String, unauthorizedStatusCode: StatusCode, clientNonceGenerator: () => String ) { - def authenticate[T](request: GenericRequest[T, _], response: Response[T]): Option[Header] = { + def authenticate[T](request: GenericRequest[T, _], response: Response[T]): Option[Header] = responseHeaderValue(response.headers(requestHeaderName), request, response.code) .map(Header(responseHeaderName, _)) - } private def responseHeaderValue( - authHeaderValues: Seq[String], - request: GenericRequest[_, _], - statusCode: StatusCode + authHeaderValues: Seq[String], + request: GenericRequest[_, _], + statusCode: StatusCode ): Option[String] = { val wwwAuthRawHeaders = authHeaderValues wwwAuthRawHeaders.find(_.contains("Digest")).flatMap { inputHeader => @@ -43,11 +42,11 @@ private[client4] class DigestAuthenticator private( } private def responseHeaderValue( - request: GenericRequest[_, _], - digestAuthData: DigestAuthData, - wwwAuthHeader: WwwAuthHeaderValue, - realmMatch: String, - nonceMatch: String + request: GenericRequest[_, _], + digestAuthData: DigestAuthData, + wwwAuthHeader: WwwAuthHeaderValue, + realmMatch: String, + nonceMatch: String ): Option[String] = { val isFirstOrShouldRetry = if ( @@ -104,16 +103,16 @@ private[client4] class DigestAuthenticator private( } private def calculateResponseChallenge( - request: GenericRequest[_, _], - digestAuthData: DigestAuthData, - realm: String, - qop: Option[String], - nonce: String, - digestUri: String, - clientNonce: String, - nonceCount: String, - messageDigest: MessageDigestCompatibility, - algorithm: String + request: GenericRequest[_, _], + digestAuthData: DigestAuthData, + realm: String, + qop: Option[String], + nonce: String, + digestUri: String, + clientNonce: String, + nonceCount: String, + messageDigest: MessageDigestCompatibility, + algorithm: String ) = { val ha1 = calculateHa1(digestAuthData, realm, messageDigest, algorithm, nonce, clientNonce) val ha2 = calculateHa2(request, qop, digestUri, messageDigest) @@ -144,20 +143,19 @@ private[client4] class DigestAuthenticator private( messageDigest: MessageDigestCompatibility, ha1: String, ha2: String - ) = { + ) = qop match { case Some(v) if v == QualityOfProtectionAuth || v == QualityOfProtectionAuthInt => md5HexString(s"$ha1:$nonce:$nonceCount:$clientNonce:$v:$ha2", messageDigest) case _ => md5HexString(s"$ha1:$nonce:$ha2", messageDigest) } - } private def calculateHa2( - request: GenericRequest[_, _], - qop: Option[String], - digestUri: String, - messageDigest: MessageDigestCompatibility - ) = { + request: GenericRequest[_, _], + qop: Option[String], + digestUri: String, + messageDigest: MessageDigestCompatibility + ) = qop match { case Some(QualityOfProtectionAuth) => md5HexString(s"${request.method.method}:$digestUri", messageDigest) case None => md5HexString(s"${request.method.method}:$digestUri", messageDigest) @@ -179,7 +177,6 @@ private[client4] class DigestAuthenticator private( ) case Some(q) => throw new IllegalArgumentException(s"Unknown qop: $q") } - } private def createAuthHeaderValue[T]( digestAuthData: DigestAuthData, @@ -215,15 +212,13 @@ private[client4] object DigestAuthenticator { val QualityOfProtectionAuthInt = "auth-int" case class DigestAuthData(username: String, password: String) - private def md5HexString(text: String, messageDigest: MessageDigestCompatibility) = { + private def md5HexString(text: String, messageDigest: MessageDigestCompatibility) = byteArrayToHexString(messageDigest.digest(text.getBytes(Charset.forName("UTF-8")))) - } private def byteArrayToHexString(bytes: Seq[Byte]): String = { val sb = new StringBuilder - for (b <- bytes) { + for (b <- bytes) sb.append(String.format("%02x", Byte.box(b))) - } sb.toString } diff --git a/core/src/main/scala/sttp/client4/internal/ToCurlConverter.scala b/core/src/main/scala/sttp/client4/internal/ToCurlConverter.scala index 4146491db6..39977ea894 100644 --- a/core/src/main/scala/sttp/client4/internal/ToCurlConverter.scala +++ b/core/src/main/scala/sttp/client4/internal/ToCurlConverter.scala @@ -22,23 +22,20 @@ object ToCurlConverter { s"""curl$params""" } - private def extractMethod(r: GenericRequest[_, _]): String = { + private def extractMethod(r: GenericRequest[_, _]): String = s"--request ${r.method.method}" - } - private def extractUrl(r: GenericRequest[_, _]): String = { + private def extractUrl(r: GenericRequest[_, _]): String = s"--url '${r.uri}'" - } - private def extractHeaders(r: GenericRequest[_, _], sensitiveHeaders: Set[String]): String = { + private def extractHeaders(r: GenericRequest[_, _], sensitiveHeaders: Set[String]): String = r.headers // filtering out compression headers so that the results are human-readable, if possible .filterNot(_.name.equalsIgnoreCase(HeaderNames.AcceptEncoding)) .map(h => s"--header '${h.toStringSafe(sensitiveHeaders)}'") .mkString(newline) - } - private def extractBody(r: GenericRequest[_, _]): String = { + private def extractBody(r: GenericRequest[_, _]): String = r.body match { case StringBody(text, _, _) => s"""--data-raw '${text.replace("'", "\\'")}'""" case ByteArrayBody(_, _) => s"--data-binary " @@ -49,9 +46,8 @@ object ToCurlConverter { case FileBody(file, _) => s"""--data-binary @${file.name}""" case NoBody => "" } - } - def handleMultipartBody(parts: Seq[Part[GenericRequestBody[_]]]): String = { + def handleMultipartBody(parts: Seq[Part[GenericRequestBody[_]]]): String = parts .map { p => p.body match { @@ -61,15 +57,13 @@ object ToCurlConverter { } } .mkString(newline) - } - private def extractOptions(r: GenericRequest[_, _]): String = { + private def extractOptions(r: GenericRequest[_, _]): String = if (r.options.followRedirects) { s"--location${newline}--max-redirs ${r.options.maxRedirects}" } else { "" } - } private def addSpaceIfNotEmpty(fInput: GenericRequest[_, _] => String): GenericRequest[_, _] => String = t => if (fInput(t).isEmpty) "" else s"${newline}${fInput(t)}" diff --git a/core/src/main/scala/sttp/client4/internal/ToRfc2616Converter.scala b/core/src/main/scala/sttp/client4/internal/ToRfc2616Converter.scala index 4b83283351..18c334324c 100644 --- a/core/src/main/scala/sttp/client4/internal/ToRfc2616Converter.scala +++ b/core/src/main/scala/sttp/client4/internal/ToRfc2616Converter.scala @@ -27,7 +27,7 @@ object ToRfc2616Converter { if (body.isEmpty) resultWithHeaders else resultWithHeaders + s"\n\n$body" } - private def extractBody(r: GenericRequest[_, _]): String = { + private def extractBody(r: GenericRequest[_, _]): String = r.body match { case StringBody(text, _, _) => s"$text" case ByteArrayBody(_, _) => "" @@ -38,7 +38,6 @@ object ToRfc2616Converter { case FileBody(file, _) => s"<${file.name}" case NoBody => "" } - } def handleMultipartBody(parts: Seq[Part[GenericRequestBody[_]]]): String = { val boundary = generateBoundary() @@ -61,13 +60,12 @@ object ToRfc2616Converter { .mkString("") + s"--$boundary--" } - private def extractHeaders(r: GenericRequest[_, _], sensitiveHeaders: Set[String]): String = { + private def extractHeaders(r: GenericRequest[_, _], sensitiveHeaders: Set[String]): String = r.headers // filtering out compression headers so that the results are human-readable, if possible .filterNot(_.name.equalsIgnoreCase(HeaderNames.AcceptEncoding)) .map(h => h.toStringSafe(sensitiveHeaders)) .mkString("\n") - } private def generateBoundary(): String = { val random = Random diff --git a/core/src/main/scala/sttp/client4/internal/WwwAuthHeaderParser.scala b/core/src/main/scala/sttp/client4/internal/WwwAuthHeaderParser.scala index 768c3828bf..33d4d1e778 100644 --- a/core/src/main/scala/sttp/client4/internal/WwwAuthHeaderParser.scala +++ b/core/src/main/scala/sttp/client4/internal/WwwAuthHeaderParser.scala @@ -1,13 +1,12 @@ package sttp.client4.internal object WwwAuthHeaderParser { - def parse(text: String): WwwAuthHeaderValue = { + def parse(text: String): WwwAuthHeaderValue = WwwAuthHeaderValue( text - .foldLeft(KeyParser(Map.empty): Parser) { (parser, char) => parser.parseNext(char) } + .foldLeft(KeyParser(Map.empty): Parser)((parser, char) => parser.parseNext(char)) .close() ) - } } case class WwwAuthHeaderValue(values: Map[String, String]) { @@ -20,7 +19,7 @@ case class WwwAuthHeaderValue(values: Map[String, String]) { } private case class KeyParser private (currentKey: String, parsed: Map[String, String]) extends Parser { - override def parseNext(input: Char): Parser = { + override def parseNext(input: Char): Parser = if (input == '=') { ValueParser(currentKey, parsed) } else if (input == ' ') { @@ -28,7 +27,6 @@ private case class KeyParser private (currentKey: String, parsed: Map[String, St } else { this.copy(currentKey = currentKey + input) } - } override def close(): Map[String, String] = throw new IllegalStateException(this.toString) } @@ -42,13 +40,12 @@ private case class ValueParser private ( currentValue: String, parsed: Map[String, String] ) extends Parser { - override def parseNext(input: Char): Parser = { + override def parseNext(input: Char): Parser = if (input == '"') { QuotedValueParser(currentKey, parsed) } else { UnquotedValueParser(currentKey, input.toString, parsed) } - } override def close(): Map[String, String] = throw new IllegalStateException(this.toString) } @@ -62,13 +59,12 @@ private case class QuotedValueParser private ( currentValue: String, parsed: Map[String, String] ) extends Parser { - override def parseNext(input: Char): Parser = { + override def parseNext(input: Char): Parser = if (input == '"') { UnquotedValueParser(currentKey, currentValue, parsed) } else { this.copy(currentValue = currentValue + input) } - } override def close(): Map[String, String] = throw new IllegalStateException(this.toString) } @@ -81,24 +77,22 @@ private case class UnquotedValueParser( currentValue: String, parsed: Map[String, String] ) extends Parser { - override def parseNext(input: Char): Parser = { + override def parseNext(input: Char): Parser = if (input == ',') { SeparatorParser(parsed + (currentKey -> currentValue)).parseNext(input) } else { this.copy(currentValue = currentValue + input) } - } override def close(): Map[String, String] = parsed + (currentKey -> currentValue) } private case class SeparatorParser(parsed: Map[String, String]) extends Parser { - override def parseNext(input: Char): Parser = { + override def parseNext(input: Char): Parser = input match { case ',' => this case ' ' => KeyParser(parsed) case o => KeyParser(parsed).parseNext(o) } - } override def close(): Map[String, String] = parsed } diff --git a/core/src/main/scala/sttp/client4/internal/ws/FutureSimpleQueue.scala b/core/src/main/scala/sttp/client4/internal/ws/FutureSimpleQueue.scala index 4724f6b2c6..acb75edbc0 100644 --- a/core/src/main/scala/sttp/client4/internal/ws/FutureSimpleQueue.scala +++ b/core/src/main/scala/sttp/client4/internal/ws/FutureSimpleQueue.scala @@ -4,7 +4,7 @@ import java.util.concurrent.{ArrayBlockingQueue, BlockingQueue, LinkedBlockingQu import sttp.ws.WebSocketBufferFull -import scala.concurrent.{ExecutionContext, Future, blocking} +import scala.concurrent.{blocking, ExecutionContext, Future} class FutureSimpleQueue[T](capacity: Option[Int])(implicit ec: ExecutionContext) extends SimpleQueue[Future, T] { @@ -15,11 +15,10 @@ class FutureSimpleQueue[T](capacity: Option[Int])(implicit ec: ExecutionContext) /** Eagerly adds the given item to the queue. */ - override def offer(t: T): Unit = { + override def offer(t: T): Unit = if (!queue.offer(t)) { throw WebSocketBufferFull(capacity.getOrElse(Int.MaxValue)) } - } /** Takes an element from the queue or suspends, until one is available. May be eager or lazy, depending on `F`. */ diff --git a/core/src/main/scala/sttp/client4/internal/ws/SyncQueue.scala b/core/src/main/scala/sttp/client4/internal/ws/SyncQueue.scala index cbb3fc11f2..494f07184b 100644 --- a/core/src/main/scala/sttp/client4/internal/ws/SyncQueue.scala +++ b/core/src/main/scala/sttp/client4/internal/ws/SyncQueue.scala @@ -14,11 +14,10 @@ class SyncQueue[T](capacity: Option[Int]) extends SimpleQueue[Identity, T] { /** Eagerly adds the given item to the queue. */ - override def offer(t: T): Unit = { + override def offer(t: T): Unit = if (!queue.offer(t)) { throw WebSocketBufferFull(capacity.getOrElse(Int.MaxValue)) } - } /** Takes an element from the queue or suspends, until one is available. May be eager or lazy, depending on `F`. */ diff --git a/core/src/main/scala/sttp/client4/listener/ListenerBackend.scala b/core/src/main/scala/sttp/client4/listener/ListenerBackend.scala index b3bbcaa57d..798a1a5146 100644 --- a/core/src/main/scala/sttp/client4/listener/ListenerBackend.scala +++ b/core/src/main/scala/sttp/client4/listener/ListenerBackend.scala @@ -8,18 +8,17 @@ import sttp.client4.wrappers.DelegateBackend /** A backend wrapper which notifies the given [[RequestListener]] when a request starts and completes. */ abstract class ListenerBackend[F[_], P, L]( - delegate: GenericBackend[F, P], - listener: RequestListener[F, L] + delegate: GenericBackend[F, P], + listener: RequestListener[F, L] ) extends DelegateBackend(delegate) { - override def send[T](request: GenericRequest[T, P with Effect[F]]): F[Response[T]] = { + override def send[T](request: GenericRequest[T, P with Effect[F]]): F[Response[T]] = listener.beforeRequest(request).flatMap { t => monad .handleError(delegate.send(request)) { case e: Exception => listener.requestException(request, t, e).flatMap(_ => monad.error(e)) } - .flatMap { response => listener.requestSuccessful(request, response, t).map(_ => response) } + .flatMap(response => listener.requestSuccessful(request, response, t).map(_ => response)) } - } } object ListenerBackend { diff --git a/core/src/main/scala/sttp/client4/logging/Log.scala b/core/src/main/scala/sttp/client4/logging/Log.scala index f7cd3c8a63..b16b7a2454 100644 --- a/core/src/main/scala/sttp/client4/logging/Log.scala +++ b/core/src/main/scala/sttp/client4/logging/Log.scala @@ -10,15 +10,15 @@ import scala.concurrent.duration.Duration trait Log[F[_]] { def beforeRequestSend(request: GenericRequest[_, _]): F[Unit] def response( - request: GenericRequest[_, _], - response: Response[_], - responseBody: Option[String], - elapsed: Option[Duration] + request: GenericRequest[_, _], + response: Response[_], + responseBody: Option[String], + elapsed: Option[Duration] ): F[Unit] def requestException( - request: GenericRequest[_, _], - elapsed: Option[Duration], - e: Exception + request: GenericRequest[_, _], + elapsed: Option[Duration], + e: Exception ): F[Unit] } @@ -65,22 +65,20 @@ class DefaultLog[F[_]]( case None => before(request, logRequestBody, logRequestHeaders) } - private def before(request: GenericRequest[_, _], _logRequestBody: Boolean, _logRequestHeaders: Boolean): F[Unit] = { + private def before(request: GenericRequest[_, _], _logRequestBody: Boolean, _logRequestHeaders: Boolean): F[Unit] = logger( - beforeRequestSendLogLevel, { - s"Sending request: ${ - if (beforeCurlInsteadOfShow && _logRequestBody && _logRequestHeaders) request.toCurl(sensitiveHeaders) - else request.show(includeBody = _logRequestBody, _logRequestHeaders, sensitiveHeaders) - }" - } + beforeRequestSendLogLevel, + s"Sending request: ${ + if (beforeCurlInsteadOfShow && _logRequestBody && _logRequestHeaders) request.toCurl(sensitiveHeaders) + else request.show(includeBody = _logRequestBody, _logRequestHeaders, sensitiveHeaders) + }" ) - } override def response( - request: GenericRequest[_, _], - response: Response[_], - responseBody: Option[String], - elapsed: Option[Duration] + request: GenericRequest[_, _], + response: Response[_], + responseBody: Option[String], + elapsed: Option[Duration] ): F[Unit] = request.loggingOptions match { case Some(options) => handleResponse( @@ -102,7 +100,7 @@ class DefaultLog[F[_]]( logResponseBody: Boolean, _logResponseHeaders: Boolean, elapsed: Option[Duration] - ): F[Unit] = { + ): F[Unit] = logger( responseLogLevel(response.code), { val responseAsString = @@ -112,7 +110,6 @@ class DefaultLog[F[_]]( s"Request: $showBasic${took(elapsed)}, response: $responseAsString" } ) - } override def requestException(request: GenericRequest[_, _], elapsed: Option[Duration], e: Exception): F[Unit] = { val logLevel = HttpError.find(e) match { diff --git a/core/src/main/scala/sttp/client4/logging/LoggingListener.scala b/core/src/main/scala/sttp/client4/logging/LoggingListener.scala index 903f78de57..d1f94364fa 100644 --- a/core/src/main/scala/sttp/client4/logging/LoggingListener.scala +++ b/core/src/main/scala/sttp/client4/logging/LoggingListener.scala @@ -13,15 +13,12 @@ class LoggingListener[F[_]](log: Log[F], includeTiming: Boolean)(implicit m: Mon private def now(): Long = System.currentTimeMillis() private def elapsed(from: Option[Long]): Option[Duration] = from.map(f => Duration(now() - f, TimeUnit.MILLISECONDS)) - override def beforeRequest(request: GenericRequest[_, _]): F[Option[Long]] = { + override def beforeRequest(request: GenericRequest[_, _]): F[Option[Long]] = log.beforeRequestSend(request).map(_ => if (includeTiming) Some(now()) else None) - } - override def requestException(request: GenericRequest[_, _], tag: Option[Long], e: Exception): F[Unit] = { + override def requestException(request: GenericRequest[_, _], tag: Option[Long], e: Exception): F[Unit] = log.requestException(request, elapsed(tag), e) - } - override def requestSuccessful(request: GenericRequest[_, _], response: Response[_], tag: Option[Long]): F[Unit] = { + override def requestSuccessful(request: GenericRequest[_, _], response: Response[_], tag: Option[Long]): F[Unit] = log.response(request, response, None, elapsed(tag)) - } } diff --git a/core/src/main/scala/sttp/client4/logging/LoggingWithResponseBodyBackend.scala b/core/src/main/scala/sttp/client4/logging/LoggingWithResponseBodyBackend.scala index 4e27532d22..c8aeca1bff 100644 --- a/core/src/main/scala/sttp/client4/logging/LoggingWithResponseBodyBackend.scala +++ b/core/src/main/scala/sttp/client4/logging/LoggingWithResponseBodyBackend.scala @@ -17,15 +17,14 @@ abstract class LoggingWithResponseBodyBackend[F[_], P]( private def now(): Long = System.currentTimeMillis() private def elapsed(from: Option[Long]): Option[Duration] = from.map(f => Duration(now() - f, TimeUnit.MILLISECONDS)) - override def send[T](request: GenericRequest[T, P with Effect[F]]): F[Response[T]] = { + override def send[T](request: GenericRequest[T, P with Effect[F]]): F[Response[T]] = log.beforeRequestSend(request).flatMap { _ => val start = if (includeTiming) Some(now()) else None - def sendAndLog(request: GenericRequest[(T, Option[String]), P with Effect[F]]): F[Response[T]] = { + def sendAndLog(request: GenericRequest[(T, Option[String]), P with Effect[F]]): F[Response[T]] = for { r <- delegate.send(request) _ <- log.response(request, r, r.body._2, elapsed(start)) } yield r.copy(body = r.body._1) - } val response = request match { case request: Request[T] => sendAndLog(request.response(asBothOption(request.response, asStringAlways))) @@ -43,7 +42,6 @@ abstract class LoggingWithResponseBodyBackend[F[_], P]( .flatMap(_ => monad.error(e)) } } - } } object LoggingWithResponseBodyBackend { diff --git a/core/src/main/scala/sttp/client4/monad/MapEffect.scala b/core/src/main/scala/sttp/client4/monad/MapEffect.scala index 88bc293979..579fd0bd36 100644 --- a/core/src/main/scala/sttp/client4/monad/MapEffect.scala +++ b/core/src/main/scala/sttp/client4/monad/MapEffect.scala @@ -51,7 +51,7 @@ object MapEffect { gk: FunctionK[G, F], fm: MonadError[F], gm: MonadError[G] - ): GenericResponseAs[_, _] = { + ): GenericResponseAs[_, _] = r match { case IgnoreResponse => IgnoreResponse case ResponseAsByteArray => ResponseAsByteArray @@ -81,7 +81,6 @@ object MapEffect { case ResponseAsBoth(l, r) => ResponseAsBoth(apply(l, fk, gk, fm, gm), apply(r, fk, gk, fm, gm).asInstanceOf[GenericResponseAs[_, Any]]) } - } /* private def apply[TT, R0, F[_], G[_]]( r: InternalResponseAs[TT, R0 with Effect[F]], diff --git a/core/src/main/scala/sttp/client4/request.scala b/core/src/main/scala/sttp/client4/request.scala index 4c6b753030..68b20a2f02 100644 --- a/core/src/main/scala/sttp/client4/request.scala +++ b/core/src/main/scala/sttp/client4/request.scala @@ -187,13 +187,13 @@ object Request { * The capabilities required to send this request: a subtype of [[Streams]], and optionally an [[Effect]]. */ final case class StreamRequest[T, R]( - method: Method, - uri: Uri, - body: GenericRequestBody[R], - headers: Seq[Header], - response: StreamResponseAs[T, R], - options: RequestOptions, - tags: Map[String, Any] + method: Method, + uri: Uri, + body: GenericRequestBody[R], + headers: Seq[Header], + response: StreamResponseAs[T, R], + options: RequestOptions, + tags: Map[String, Any] ) extends GenericRequest[T, R] with RequestBuilder[StreamRequest[T, R]] { @@ -336,13 +336,13 @@ final case class WebSocketRequest[F[_], T]( * The stream capability required to send this request, a subtype of [[Streams]]. */ final case class WebSocketStreamRequest[T, S]( - method: Method, - uri: Uri, - body: GenericRequestBody[S], - headers: Seq[Header], - response: WebSocketStreamResponseAs[T, S], - options: RequestOptions, - tags: Map[String, Any] + method: Method, + uri: Uri, + body: GenericRequestBody[S], + headers: Seq[Header], + response: WebSocketStreamResponseAs[T, S], + options: RequestOptions, + tags: Map[String, Any] ) extends GenericRequest[T, S with WebSockets] with RequestBuilder[WebSocketStreamRequest[T, S]] { diff --git a/core/src/main/scala/sttp/client4/requestBuilder.scala b/core/src/main/scala/sttp/client4/requestBuilder.scala index 4bc0bec9b7..cf65bc31fc 100644 --- a/core/src/main/scala/sttp/client4/requestBuilder.scala +++ b/core/src/main/scala/sttp/client4/requestBuilder.scala @@ -107,7 +107,7 @@ trait PartialRequestBuilder[+PR <: PartialRequestBuilder[PR, R], +R] * If there's already a header with the same name, should it be replaced? */ def headers(hs: Map[String, String], replaceExisting: Boolean): PR = - if (replaceExisting) hs.foldLeft(this) { (s, h) => s.header(h._1, h._2, replaceExisting) } + if (replaceExisting) hs.foldLeft(this)((s, h) => s.header(h._1, h._2, replaceExisting)) else headers(hs) /** Adds the given headers to the end of the headers sequence. */ @@ -115,7 +115,7 @@ trait PartialRequestBuilder[+PR <: PartialRequestBuilder[PR, R], +R] /** Adds the given headers to the end of the headers sequence. */ def headers(hs: Seq[Header], replaceExisting: Boolean): PR = - if (replaceExisting) hs.foldLeft(this) { (s, h) => s.header(h, replaceExisting) } + if (replaceExisting) hs.foldLeft(this)((s, h) => s.header(h, replaceExisting)) else headers(hs: _*) def auth: SpecifyAuthScheme[PR] = @@ -129,13 +129,12 @@ trait PartialRequestBuilder[+PR <: PartialRequestBuilder[PR, R], +R] def cookie(n: String, v: String): PR = cookies((n, v)) def cookies(r: Response[_]): PR = cookies(r.cookies.collect { case Right(c) => c }.map(c => (c.name, c.value)): _*) def cookies(cs: Iterable[CookieWithMeta]): PR = cookies(cs.map(c => (c.name, c.value)).toSeq: _*) - def cookies(nvs: (String, String)*): PR = { + def cookies(nvs: (String, String)*): PR = header( HeaderNames.Cookie, (headers.find(_.name == HeaderNames.Cookie).map(_.value).toSeq ++ nvs.map(p => p._1 + "=" + p._2)).mkString("; "), replaceExisting = true ) - } private[client4] def hasContentType: Boolean = headers.exists(_.is(HeaderNames.ContentType)) private[client4] def setContentTypeIfMissing(mt: MediaType): PR = diff --git a/core/src/main/scala/sttp/client4/testing/AbstractBackendStub.scala b/core/src/main/scala/sttp/client4/testing/AbstractBackendStub.scala index 4c034d4b9e..7e11ebd24e 100644 --- a/core/src/main/scala/sttp/client4/testing/AbstractBackendStub.scala +++ b/core/src/main/scala/sttp/client4/testing/AbstractBackendStub.scala @@ -14,9 +14,9 @@ import sttp.ws.testing.WebSocketStub import scala.util.{Failure, Success, Try} abstract class AbstractBackendStub[F[_], P]( - _monad: MonadError[F], - matchers: PartialFunction[GenericRequest[_, _], F[Response[_]]], - fallback: Option[GenericBackend[F, P]] + _monad: MonadError[F], + matchers: PartialFunction[GenericRequest[_, _], F[Response[_]]], + fallback: Option[GenericBackend[F, P]] ) extends GenericBackend[F, P] { type Self @@ -48,7 +48,7 @@ abstract class AbstractBackendStub[F[_], P]( withMatchers(matchers.orElse(wrappedPartial)) } - override def send[T](request: GenericRequest[T, P with Effect[F]]): F[Response[T]] = { + override def send[T](request: GenericRequest[T, P with Effect[F]]): F[Response[T]] = Try(matchers.lift(request)) match { case Success(Some(response)) => adjustExceptions(request)(tryAdjustResponseType(request.response, response.asInstanceOf[F[Response[T]]])(monad)) @@ -59,7 +59,6 @@ abstract class AbstractBackendStub[F[_], P]( } case Failure(e) => adjustExceptions(request)(monad.error(e)) } - } private def adjustExceptions[T](request: GenericRequest[_, _])(t: => F[T]): F[T] = SttpClientException.adjustExceptions(monad)(t)( @@ -108,21 +107,20 @@ abstract class AbstractBackendStub[F[_], P]( object AbstractBackendStub { private[client4] def tryAdjustResponseType[DesiredRType, RType, F[_]]( - ra: ResponseAsDelegate[DesiredRType, _], - m: F[Response[RType]] - )(implicit monad: MonadError[F]): F[Response[DesiredRType]] = { + ra: ResponseAsDelegate[DesiredRType, _], + m: F[Response[RType]] + )(implicit monad: MonadError[F]): F[Response[DesiredRType]] = monad.flatMap[Response[RType], Response[DesiredRType]](m) { r => tryAdjustResponseBody(ra.delegate, r.body, r).getOrElse(monad.unit(r.body)).map { nb => r.copy(body = nb.asInstanceOf[DesiredRType]) } } - } private[client4] def tryAdjustResponseBody[F[_], T, U]( - ra: GenericResponseAs[T, _], - b: U, - meta: ResponseMetadata - )(implicit monad: MonadError[F]): Option[F[T]] = { + ra: GenericResponseAs[T, _], + b: U, + meta: ResponseMetadata + )(implicit monad: MonadError[F]): Option[F[T]] = ra match { case IgnoreResponse => Some(().unit.asInstanceOf[F[T]]) case ResponseAsByteArray => @@ -173,5 +171,4 @@ object AbstractBackendStub { } } } - } } diff --git a/core/src/main/scala/sttp/client4/testing/AtomicCyclicIterator.scala b/core/src/main/scala/sttp/client4/testing/AtomicCyclicIterator.scala index 99a8505fd1..a7d287d1de 100644 --- a/core/src/main/scala/sttp/client4/testing/AtomicCyclicIterator.scala +++ b/core/src/main/scala/sttp/client4/testing/AtomicCyclicIterator.scala @@ -16,12 +16,11 @@ final class AtomicCyclicIterator[+T] private (val elements: Seq[T]) { object AtomicCyclicIterator { - def tryFrom[T](elements: Seq[T]): Try[AtomicCyclicIterator[T]] = { + def tryFrom[T](elements: Seq[T]): Try[AtomicCyclicIterator[T]] = if (elements.nonEmpty) Success(new AtomicCyclicIterator(elements)) else Failure(new IllegalArgumentException("Argument must be a non-empty collection.")) - } def unsafeFrom[T](elements: Seq[T]): AtomicCyclicIterator[T] = tryFrom(elements).get diff --git a/core/src/main/scala/sttp/client4/testing/RecordingBackend.scala b/core/src/main/scala/sttp/client4/testing/RecordingBackend.scala index ce0c9a85d1..e94189bca5 100644 --- a/core/src/main/scala/sttp/client4/testing/RecordingBackend.scala +++ b/core/src/main/scala/sttp/client4/testing/RecordingBackend.scala @@ -20,13 +20,12 @@ abstract class AbstractRecordingBackend[F[_], P](delegate: GenericBackend[F, P]) private val _allInteractions = new AtomicReference[Vector[RequestAndResponse]](Vector()) - private def addInteraction(request: GenericRequest[_, _], response: Try[Response[_]]): Unit = { + private def addInteraction(request: GenericRequest[_, _], response: Try[Response[_]]): Unit = _allInteractions.updateAndGet(new UnaryOperator[Vector[RequestAndResponse]] { override def apply(t: Vector[RequestAndResponse]): Vector[RequestAndResponse] = t.:+((request, response)) }) - } - override def send[T](request: GenericRequest[T, P with Effect[F]]): F[Response[T]] = { + override def send[T](request: GenericRequest[T, P with Effect[F]]): F[Response[T]] = delegate .send(request) .map { response => @@ -37,7 +36,6 @@ abstract class AbstractRecordingBackend[F[_], P](delegate: GenericBackend[F, P]) addInteraction(request, Failure(e)) monad.error(e) } - } override def allInteractions: List[RequestAndResponse] = _allInteractions.get().toList } diff --git a/core/src/main/scala/sttp/client4/testing/StreamBackendStub.scala b/core/src/main/scala/sttp/client4/testing/StreamBackendStub.scala index e4eda0f5bf..a8ea4f59d5 100644 --- a/core/src/main/scala/sttp/client4/testing/StreamBackendStub.scala +++ b/core/src/main/scala/sttp/client4/testing/StreamBackendStub.scala @@ -24,9 +24,9 @@ import scala.concurrent.ExecutionContext * request, a response is specified with the incorrect or inconvertible body type. */ class StreamBackendStub[F[_], S]( - monad: MonadError[F], - matchers: PartialFunction[GenericRequest[_, _], F[Response[_]]], - fallback: Option[StreamBackend[F, S]] + monad: MonadError[F], + matchers: PartialFunction[GenericRequest[_, _], F[Response[_]]], + fallback: Option[StreamBackend[F, S]] ) extends AbstractBackendStub[F, S](monad, matchers, fallback) with StreamBackend[F, S] { diff --git a/core/src/main/scala/sttp/client4/testing/SyncBackendStub.scala b/core/src/main/scala/sttp/client4/testing/SyncBackendStub.scala index c388e38fcc..6d195e2011 100644 --- a/core/src/main/scala/sttp/client4/testing/SyncBackendStub.scala +++ b/core/src/main/scala/sttp/client4/testing/SyncBackendStub.scala @@ -17,8 +17,8 @@ import sttp.client4.monad.IdMonad * request, a response is specified with the incorrect or inconvertible body type. */ class SyncBackendStub( - matchers: PartialFunction[GenericRequest[_, _], Response[_]], - fallback: Option[SyncBackend] + matchers: PartialFunction[GenericRequest[_, _], Response[_]], + fallback: Option[SyncBackend] ) extends AbstractBackendStub[Identity, Any](IdMonad, matchers, fallback) with SyncBackend { diff --git a/core/src/main/scala/sttp/client4/testing/WebSocketBackendStub.scala b/core/src/main/scala/sttp/client4/testing/WebSocketBackendStub.scala index 87d95250ca..a124da61bf 100644 --- a/core/src/main/scala/sttp/client4/testing/WebSocketBackendStub.scala +++ b/core/src/main/scala/sttp/client4/testing/WebSocketBackendStub.scala @@ -25,9 +25,9 @@ import sttp.capabilities.WebSockets * request, a response is specified with the incorrect or inconvertible body type. */ class WebSocketBackendStub[F[_]]( - monad: MonadError[F], - matchers: PartialFunction[GenericRequest[_, _], F[Response[_]]], - fallback: Option[WebSocketBackend[F]] + monad: MonadError[F], + matchers: PartialFunction[GenericRequest[_, _], F[Response[_]]], + fallback: Option[WebSocketBackend[F]] ) extends AbstractBackendStub[F, WebSockets](monad, matchers, fallback) with WebSocketBackend[F] { diff --git a/core/src/main/scala/sttp/client4/testing/WebSocketStreamBackendStub.scala b/core/src/main/scala/sttp/client4/testing/WebSocketStreamBackendStub.scala index 12d1a77ee0..0a270536cd 100644 --- a/core/src/main/scala/sttp/client4/testing/WebSocketStreamBackendStub.scala +++ b/core/src/main/scala/sttp/client4/testing/WebSocketStreamBackendStub.scala @@ -18,8 +18,8 @@ import sttp.capabilities.WebSockets * conversions will be attempted (e.g. from a `String` to a custom mapped type, as specified in the request, see the * documentation for more details). * - * For web socket requests, the stub can be configured to returned both custom [[sttp.ws.WebSocket]] implementations, as well - * as [[sttp.ws.testing.WebSocketStub]] instances. + * For web socket requests, the stub can be configured to returned both custom [[sttp.ws.WebSocket]] implementations, + * as well as [[sttp.ws.testing.WebSocketStub]] instances. * * For requests which return the response as a stream, if the stub should return a raw stream value (which should then * be passed to the stream-consuming function, or mapped to another value), it should be wrapped with [[RawStream]]. @@ -28,9 +28,9 @@ import sttp.capabilities.WebSockets * request, a response is specified with the incorrect or inconvertible body type. */ class WebSocketStreamBackendStub[F[_], S]( - monad: MonadError[F], - matchers: PartialFunction[GenericRequest[_, _], F[Response[_]]], - fallback: Option[WebSocketStreamBackend[F, S]] + monad: MonadError[F], + matchers: PartialFunction[GenericRequest[_, _], F[Response[_]]], + fallback: Option[WebSocketStreamBackend[F, S]] ) extends AbstractBackendStub[F, S with WebSockets](monad, matchers, fallback) with WebSocketStreamBackend[F, S] { type Self = WebSocketStreamBackendStub[F, S] diff --git a/core/src/main/scala/sttp/client4/wrappers/DelegateBackend.scala b/core/src/main/scala/sttp/client4/wrappers/DelegateBackend.scala index f3167ad0ab..b90f25a1d7 100644 --- a/core/src/main/scala/sttp/client4/wrappers/DelegateBackend.scala +++ b/core/src/main/scala/sttp/client4/wrappers/DelegateBackend.scala @@ -3,8 +3,8 @@ package sttp.client4.wrappers import sttp.client4.GenericBackend import sttp.monad.MonadError -/** A base class for delegate backends, which includes delegating implementations for `close` and `monad`, so - * that only `send` needs to be defined. +/** A base class for delegate backends, which includes delegating implementations for `close` and `monad`, so that only + * `send` needs to be defined. */ abstract class DelegateBackend[F[_], +P](delegate: GenericBackend[F, P]) extends GenericBackend[F, P] { override def close(): F[Unit] = delegate.close() diff --git a/core/src/main/scala/sttp/client4/wrappers/DigestAuthenticationBackend.scala b/core/src/main/scala/sttp/client4/wrappers/DigestAuthenticationBackend.scala index ccca6edb7b..543ed4e670 100644 --- a/core/src/main/scala/sttp/client4/wrappers/DigestAuthenticationBackend.scala +++ b/core/src/main/scala/sttp/client4/wrappers/DigestAuthenticationBackend.scala @@ -4,7 +4,16 @@ import sttp.capabilities.Effect import sttp.client4.internal.DigestAuthenticator import sttp.client4.internal.DigestAuthenticator.DigestAuthData import sttp.client4.wrappers.DigestAuthenticationBackend._ -import sttp.client4.{Backend, GenericBackend, GenericRequest, Response, StreamBackend, SyncBackend, WebSocketBackend, WebSocketStreamBackend} +import sttp.client4.{ + Backend, + GenericBackend, + GenericRequest, + Response, + StreamBackend, + SyncBackend, + WebSocketBackend, + WebSocketStreamBackend +} import sttp.model.Header import sttp.monad.syntax._ @@ -29,11 +38,11 @@ abstract class DigestAuthenticationBackend[F[_], P] private ( } private def handleResponse[T]( - request: GenericRequest[T, P with Effect[F]], - response: Response[T], - digestTag: String, - digestAuthenticator: DigestAuthData => DigestAuthenticator - ): F[(Response[T], Option[Header])] = { + request: GenericRequest[T, P with Effect[F]], + response: Response[T], + digestTag: String, + digestAuthenticator: DigestAuthData => DigestAuthenticator + ): F[(Response[T], Option[Header])] = request .tag(digestTag) .map(_.asInstanceOf[DigestAuthData]) @@ -42,7 +51,6 @@ abstract class DigestAuthenticationBackend[F[_], P] private ( header.map(h => delegate.send(request.header(h)).map(_ -> Option(h))) } .getOrElse((response -> Option.empty[Header]).unit) - } } object DigestAuthenticationBackend { diff --git a/core/src/main/scala/sttp/client4/wrappers/FollowRedirectsBackend.scala b/core/src/main/scala/sttp/client4/wrappers/FollowRedirectsBackend.scala index f418577b9d..ea5341fc0c 100644 --- a/core/src/main/scala/sttp/client4/wrappers/FollowRedirectsBackend.scala +++ b/core/src/main/scala/sttp/client4/wrappers/FollowRedirectsBackend.scala @@ -31,10 +31,10 @@ abstract class FollowRedirectsBackend[F[_], P] private ( } private def followRedirect[T]( - request: GenericRequest[T, R], - response: Response[T], - redirects: Int - ): F[Response[T]] = { + request: GenericRequest[T, R], + response: Response[T], + redirects: Int + ): F[Response[T]] = response.header(HeaderNames.Location).fold(monad.unit(response)) { loc => if (redirects >= request.options.maxRedirects) { monad.error(TooManyRedirectsException(request.uri, redirects)) @@ -42,13 +42,12 @@ abstract class FollowRedirectsBackend[F[_], P] private ( followRedirect(request, response, redirects, loc) } } - } private def followRedirect[T]( - request: GenericRequest[T, R], - response: Response[T], - redirects: Int, - loc: String + request: GenericRequest[T, R], + response: Response[T], + redirects: Int, + loc: String ): F[Response[T]] = { val uri = if (FollowRedirectsBackend.isRelative(loc)) config.transformUri(request.uri.resolve(uri"$loc")) @@ -66,11 +65,10 @@ abstract class FollowRedirectsBackend[F[_], P] private ( } } - private def stripSensitiveHeaders[T](request: GenericRequest[T, R]): GenericRequest[T, R] = { + private def stripSensitiveHeaders[T](request: GenericRequest[T, R]): GenericRequest[T, R] = request.withHeaders( request.headers.filterNot(h => config.sensitiveHeaders.contains(h.name.toLowerCase())) ) - } private def changePostPutToGet[T](r: GenericRequest[T, R], statusCode: StatusCode): GenericRequest[T, R] = { val applicable = r.method == Method.POST || r.method == Method.PUT diff --git a/core/src/main/scala/sttp/client4/wrappers/MappedEffectBackend.scala b/core/src/main/scala/sttp/client4/wrappers/MappedEffectBackend.scala index 34cfee1f1c..59b22c36bc 100644 --- a/core/src/main/scala/sttp/client4/wrappers/MappedEffectBackend.scala +++ b/core/src/main/scala/sttp/client4/wrappers/MappedEffectBackend.scala @@ -2,7 +2,15 @@ package sttp.client4.wrappers import sttp.capabilities.Effect import sttp.client4.monad.{FunctionK, MapEffect} -import sttp.client4.{Backend, GenericBackend, GenericRequest, Response, StreamBackend, WebSocketBackend, WebSocketStreamBackend} +import sttp.client4.{ + Backend, + GenericBackend, + GenericRequest, + Response, + StreamBackend, + WebSocketBackend, + WebSocketStreamBackend +} import sttp.monad.MonadError abstract class MappedEffectBackend[F[_], G[_], P]( diff --git a/core/src/main/scala/sttp/client4/wrappers/ResolveRelativeUrisBackend.scala b/core/src/main/scala/sttp/client4/wrappers/ResolveRelativeUrisBackend.scala index 388bfec8bf..12b053d356 100644 --- a/core/src/main/scala/sttp/client4/wrappers/ResolveRelativeUrisBackend.scala +++ b/core/src/main/scala/sttp/client4/wrappers/ResolveRelativeUrisBackend.scala @@ -1,7 +1,17 @@ package sttp.client4.wrappers import sttp.capabilities.Effect -import sttp.client4.{Backend, GenericBackend, GenericRequest, Identity, Response, StreamBackend, SyncBackend, WebSocketBackend, WebSocketStreamBackend} +import sttp.client4.{ + Backend, + GenericBackend, + GenericRequest, + Identity, + Response, + StreamBackend, + SyncBackend, + WebSocketBackend, + WebSocketStreamBackend +} import sttp.model.Uri import sttp.monad.syntax._ @@ -23,8 +33,7 @@ object ResolveRelativeUrisBackend { def apply(delegate: SyncBackend, baseUri: Uri): SyncBackend = new ResolveRelativeUrisBackend[Identity, Any](delegate, baseUri.resolve) with SyncBackend {} def apply[F[_]](delegate: Backend[F], baseUri: Uri): Backend[F] = - new ResolveRelativeUrisBackend(delegate, uri => delegate.monad.unit(baseUri.resolve(uri))) - with Backend[F] {} + new ResolveRelativeUrisBackend(delegate, uri => delegate.monad.unit(baseUri.resolve(uri))) with Backend[F] {} def apply[F[_]](delegate: WebSocketBackend[F], baseUri: Uri): WebSocketBackend[F] = new ResolveRelativeUrisBackend(delegate, uri => delegate.monad.unit(baseUri.resolve(uri))) with WebSocketBackend[F] {} diff --git a/core/src/main/scalajs/sttp/client4/WebSocketImpl.scala b/core/src/main/scalajs/sttp/client4/WebSocketImpl.scala index 1a2556961d..7430a1a763 100644 --- a/core/src/main/scalajs/sttp/client4/WebSocketImpl.scala +++ b/core/src/main/scalajs/sttp/client4/WebSocketImpl.scala @@ -11,7 +11,7 @@ import sttp.ws.{WebSocket, WebSocketClosed, WebSocketFrame} import scala.scalajs.js.typedarray.{ArrayBuffer, _} -private[client4] class WebSocketImpl[F[_]] private( +private[client4] class WebSocketImpl[F[_]] private ( ws: JSWebSocket, queue: JSSimpleQueue[F, WebSocketEvent], implicit val monad: MonadError[F] diff --git a/core/src/main/scalajs/sttp/client4/fetch/AbstractFetchBackend.scala b/core/src/main/scalajs/sttp/client4/fetch/AbstractFetchBackend.scala index 2d40cca165..af09ce1a9e 100644 --- a/core/src/main/scalajs/sttp/client4/fetch/AbstractFetchBackend.scala +++ b/core/src/main/scalajs/sttp/client4/fetch/AbstractFetchBackend.scala @@ -4,16 +4,16 @@ import org.scalajs.dom.experimental.{ AbortController, BodyInit, Fetch, + Headers => JSHeaders, HttpMethod, + Request => FetchRequest, RequestCredentials, RequestInit, RequestMode, RequestRedirect, + Response => FetchResponse, ResponseInit, - ResponseType, - Headers => JSHeaders, - Request => FetchRequest, - Response => FetchResponse + ResponseType } import org.scalajs.dom.raw._ import org.scalajs.dom.{FormData, WebSocket => JSWebSocket} @@ -140,7 +140,7 @@ abstract class AbstractFetchBackend[F[_], S <: Streams[S]]( } val result = req - .flatMap { r => convertFromFuture(Fetch.fetch(customizeRequest(r)).toFuture) } + .flatMap(r => convertFromFuture(Fetch.fetch(customizeRequest(r)).toFuture)) .flatMap { resp => if (resp.`type` == ResponseType.opaqueredirect) { monad.error[FetchResponse](new RuntimeException("Unexpected redirect")) @@ -170,7 +170,7 @@ abstract class AbstractFetchBackend[F[_], S <: Streams[S]]( protected def addCancelTimeoutHook[T](result: F[T], cancel: () => Unit): F[T] - private def convertResponseHeaders(headers: JSHeaders): Seq[Header] = { + private def convertResponseHeaders(headers: JSHeaders): Seq[Header] = headers .jsIterator() .toIterator @@ -184,9 +184,8 @@ abstract class AbstractFetchBackend[F[_], S <: Streams[S]]( } } .toList - } - private def createBody(body: GenericRequestBody[R]): F[js.UndefOr[BodyInit]] = { + private def createBody(body: GenericRequestBody[R]): F[js.UndefOr[BodyInit]] = body match { case NoBody => monad.unit(js.undefined) // skip @@ -221,9 +220,8 @@ abstract class AbstractFetchBackend[F[_], S <: Streams[S]]( } monad.unit(formData) } - } - private def writeBasicBody(body: BasicBodyPart): BodyInit = { + private def writeBasicBody(body: BasicBodyPart): BodyInit = body match { case StringBody(b, encoding, _) => if (encoding.compareToIgnoreCase(Utf8) == 0) b @@ -241,7 +239,6 @@ abstract class AbstractFetchBackend[F[_], S <: Streams[S]]( case FileBody(f, _) => f.toDomFile } - } // https://stackoverflow.com/questions/679298/gets-byte-array-from-a-bytebuffer-in-java private def byteBufferToArray(bb: ByteBuffer): Array[Byte] = { @@ -311,7 +308,7 @@ abstract class AbstractFetchBackend[F[_], S <: Streams[S]]( convertFromFuture(response.arrayBuffer().toFuture).map(_ => ()) override protected def regularAsByteArray(response: FetchResponse): F[Array[Byte]] = - convertFromFuture(response.arrayBuffer().toFuture).map { ab => new Int8Array(ab).toArray } + convertFromFuture(response.arrayBuffer().toFuture).map(ab => new Int8Array(ab).toArray) override protected def regularAsFile(response: FetchResponse, file: SttpFile): F[SttpFile] = convertFromFuture(response.arrayBuffer().toFuture) @@ -329,9 +326,9 @@ abstract class AbstractFetchBackend[F[_], S <: Streams[S]]( handleResponseAsStream(response) override protected def handleWS[T]( - responseAs: GenericWebSocketResponseAs[T, _], - meta: ResponseMetadata, - ws: WebSocket[F] + responseAs: GenericWebSocketResponseAs[T, _], + meta: ResponseMetadata, + ws: WebSocket[F] ): F[T] = responseAs match { case ResponseAsWebSocket(f) => diff --git a/core/src/main/scalajs/sttp/client4/fetch/FetchBackend.scala b/core/src/main/scalajs/sttp/client4/fetch/FetchBackend.scala index fc17efe389..912bf0379a 100644 --- a/core/src/main/scalajs/sttp/client4/fetch/FetchBackend.scala +++ b/core/src/main/scalajs/sttp/client4/fetch/FetchBackend.scala @@ -21,10 +21,9 @@ class FetchBackend private (fetchOptions: FetchOptions, customizeRequest: FetchR result } - override protected def handleStreamBody(s: Nothing): Future[js.UndefOr[BodyInit]] = { + override protected def handleStreamBody(s: Nothing): Future[js.UndefOr[BodyInit]] = // we have an instance of nothing - everything's possible! Future.successful(js.undefined) - } override protected def handleResponseAsStream( response: FetchResponse diff --git a/core/src/main/scalajvm/sttp/client4/DefaultFutureBackend.scala b/core/src/main/scalajvm/sttp/client4/DefaultFutureBackend.scala index 954cbf7a61..967b78996d 100644 --- a/core/src/main/scalajvm/sttp/client4/DefaultFutureBackend.scala +++ b/core/src/main/scalajvm/sttp/client4/DefaultFutureBackend.scala @@ -12,9 +12,8 @@ object DefaultFutureBackend { */ def apply( options: BackendOptions = BackendOptions.Default - )(implicit ec: ExecutionContext = ExecutionContext.global): WebSocketBackend[Future] = { + )(implicit ec: ExecutionContext = ExecutionContext.global): WebSocketBackend[Future] = HttpClientFutureBackend(options, identity, PartialFunction.empty) - } /** Create a stub backend for testing, which uses [[Future]] to represent side effects, and doesn't support streaming. * diff --git a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientAsyncBackend.scala b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientAsyncBackend.scala index f7ec09d7ee..42a46e6c43 100644 --- a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientAsyncBackend.scala +++ b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientAsyncBackend.scala @@ -45,7 +45,7 @@ abstract class HttpClientAsyncBackend[F[_], S, BH, B]( protected def bodyHandlerBodyToBody(p: BH): B protected def emptyBody(): B - private def sendRegular[T](request: GenericRequest[T, R]): F[Response[T]] = { + private def sendRegular[T](request: GenericRequest[T, R]): F[Response[T]] = monad.flatMap(convertRequest(request)) { convertedRequest => val jRequest = customizeRequest(convertedRequest) @@ -54,7 +54,7 @@ abstract class HttpClientAsyncBackend[F[_], S, BH, B]( def error(t: Throwable): Unit = cb(Left(t)) var cf = client.sendAsync(jRequest, createBodyHandler) - val consumer = toJavaBiConsumer((t: HttpResponse[BH], u: Throwable) => { + val consumer = toJavaBiConsumer { (t: HttpResponse[BH], u: Throwable) => if (t != null) { try success(readResponse(t, Left(bodyHandlerBodyToBody(t.body())), request)) catch { @@ -64,7 +64,7 @@ abstract class HttpClientAsyncBackend[F[_], S, BH, B]( if (u != null) { error(u) } - }) + } cf = client.executor().orElse(null) match { case null => cf.whenComplete(consumer) @@ -74,9 +74,8 @@ abstract class HttpClientAsyncBackend[F[_], S, BH, B]( Canceler(() => cf.cancel(true)) }) } - } - private def sendWebSocket[T](request: GenericRequest[T, R]): F[Response[T]] = { + private def sendWebSocket[T](request: GenericRequest[T, R]): F[Response[T]] = (for { queue <- createSimpleQueue[WebSocketEvent] sequencer <- createSequencer @@ -89,7 +88,6 @@ abstract class HttpClientAsyncBackend[F[_], S, BH, B]( request ) } - } private def sendWebSocket[T]( request: GenericRequest[T, R], @@ -136,9 +134,8 @@ abstract class HttpClientAsyncBackend[F[_], S, BH, B]( }) } - private def filterIllegalWsHeaders[T](request: GenericRequest[T, R]): GenericRequest[T, R] = { + private def filterIllegalWsHeaders[T](request: GenericRequest[T, R]): GenericRequest[T, R] = request.withHeaders(request.headers.filter(h => !wsIllegalHeaders.contains(h.name.toLowerCase))) - } private def adjustExceptions[T](request: GenericRequest[_, _])(t: => F[T]): F[T] = SttpClientException.adjustExceptions(monad)(t)( diff --git a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientBackend.scala b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientBackend.scala index db39ac5397..57969241a7 100644 --- a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientBackend.scala +++ b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientBackend.scala @@ -105,7 +105,7 @@ abstract class HttpClientBackend[F[_], S, P, B]( protected def standardEncoding: (B, String) => B - override def close(): F[Unit] = { + override def close(): F[Unit] = if (closeClient) { monad.eval( client @@ -120,7 +120,6 @@ abstract class HttpClientBackend[F[_], S, P, B]( } else { monad.unit(()) } - } } object HttpClientBackend { @@ -128,9 +127,8 @@ object HttpClientBackend { type EncodingHandler[B] = PartialFunction[(B, String), B] // TODO not sure if it works private class ProxyAuthenticator(auth: BackendOptions.ProxyAuth) extends Authenticator { - override def getPasswordAuthentication: PasswordAuthentication = { + override def getPasswordAuthentication: PasswordAuthentication = new PasswordAuthentication(auth.username, auth.password.toCharArray) - } } private[client4] def defaultClient(options: BackendOptions, executor: Option[Executor]): HttpClient = { diff --git a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientFutureBackend.scala b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientFutureBackend.scala index 3e37ddde45..fcf538c074 100644 --- a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientFutureBackend.scala +++ b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientFutureBackend.scala @@ -4,9 +4,9 @@ import sttp.client4.httpclient.HttpClientBackend.EncodingHandler import sttp.client4.httpclient.HttpClientFutureBackend.InputStreamEncodingHandler import sttp.client4.internal.httpclient._ import sttp.client4.internal.ws.{FutureSimpleQueue, SimpleQueue} -import sttp.client4.internal.{NoStreams, emptyInputStream} +import sttp.client4.internal.{emptyInputStream, NoStreams} import sttp.client4.testing.WebSocketBackendStub -import sttp.client4.{BackendOptions, WebSocketBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketBackend} import sttp.monad.{FutureMonad, MonadError} import sttp.ws.{WebSocket, WebSocketFrame} diff --git a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientSyncBackend.scala b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientSyncBackend.scala index 8c116eddb2..aa609df5a7 100644 --- a/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientSyncBackend.scala +++ b/core/src/main/scalajvm/sttp/client4/httpclient/HttpClientSyncBackend.scala @@ -6,7 +6,7 @@ import sttp.client4.internal.NoStreams import sttp.client4.internal.httpclient.{BodyFromHttpClient, BodyToHttpClient, InputStreamBodyFromHttpClient} import sttp.client4.monad.IdMonad import sttp.client4.testing.SyncBackendStub -import sttp.client4.{BackendOptions, GenericRequest, Identity, Response, SttpClientException, SyncBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, GenericRequest, Identity, Response, SttpClientException, SyncBackend} import sttp.monad.MonadError import sttp.ws.{WebSocket, WebSocketFrame} @@ -79,12 +79,14 @@ object HttpClientSyncBackend { customizeRequest: HttpRequest => HttpRequest, customEncodingHandler: SyncEncodingHandler ): SyncBackend = - wrappers.FollowRedirectsBackend(new HttpClientSyncBackend(client, closeClient, customizeRequest, customEncodingHandler)) + wrappers.FollowRedirectsBackend( + new HttpClientSyncBackend(client, closeClient, customizeRequest, customEncodingHandler) + ) def apply( - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: SyncEncodingHandler = PartialFunction.empty + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: SyncEncodingHandler = PartialFunction.empty ): SyncBackend = HttpClientSyncBackend( HttpClientBackend.defaultClient(options, None), diff --git a/core/src/main/scalajvm/sttp/client4/httpurlconnection/HttpURLConnectionBackend.scala b/core/src/main/scalajvm/sttp/client4/httpurlconnection/HttpURLConnectionBackend.scala index 39ee2cf9a6..a1b6c66445 100644 --- a/core/src/main/scalajvm/sttp/client4/httpurlconnection/HttpURLConnectionBackend.scala +++ b/core/src/main/scalajvm/sttp/client4/httpurlconnection/HttpURLConnectionBackend.scala @@ -6,7 +6,26 @@ import sttp.client4.internal._ import sttp.client4.monad.IdMonad import sttp.client4.testing.SyncBackendStub import sttp.client4.ws.{GotAWebSocketException, NotAWebSocketException} -import sttp.client4.{BackendOptions, BasicBodyPart, BasicMultipartBody, ByteArrayBody, ByteBufferBody, FileBody, GenericRequest, GenericWebSocketResponseAs, Identity, InputStreamBody, MultipartStreamBody, NoBody, Response, StreamBody, StringBody, SttpClientException, SyncBackend, wrappers} +import sttp.client4.{ + wrappers, + BackendOptions, + BasicBodyPart, + BasicMultipartBody, + ByteArrayBody, + ByteBufferBody, + FileBody, + GenericRequest, + GenericWebSocketResponseAs, + Identity, + InputStreamBody, + MultipartStreamBody, + NoBody, + Response, + StreamBody, + StringBody, + SttpClientException, + SyncBackend +} import sttp.model._ import sttp.monad.MonadError @@ -21,11 +40,11 @@ import scala.collection.JavaConverters._ import scala.concurrent.duration.Duration class HttpURLConnectionBackend private ( - opts: BackendOptions, - customizeConnection: HttpURLConnection => Unit, - createURL: String => URL, - openConnection: (URL, Option[java.net.Proxy]) => URLConnection, - customEncodingHandler: EncodingHandler + opts: BackendOptions, + customizeConnection: HttpURLConnection => Unit, + createURL: String => URL, + openConnection: (URL, Option[java.net.Proxy]) => URLConnection, + customEncodingHandler: EncodingHandler ) extends SyncBackend { type R = Any with Effect[Identity] @@ -33,7 +52,7 @@ class HttpURLConnectionBackend private ( adjustExceptions(r) { val c = openConnection(r.uri) c.setRequestMethod(r.method.method) - r.headers.foreach { h => c.setRequestProperty(h.name, h.value) } + r.headers.foreach(h => c.setRequestProperty(h.name, h.value)) c.setDoInput(true) c.setReadTimeout(timeout(r.options.readTimeout)) c.setConnectTimeout(timeout(opts.connectionTimeout)) @@ -74,9 +93,8 @@ class HttpURLConnectionBackend private ( case Some(p) if uri.host.forall(!p.ignoreProxy(_)) => p.auth.foreach { proxyAuth => Authenticator.setDefault(new Authenticator() { - override def getPasswordAuthentication: PasswordAuthentication = { + override def getPasswordAuthentication: PasswordAuthentication = new PasswordAuthentication(proxyAuth.username, proxyAuth.password.toCharArray) - } }) } @@ -87,7 +105,7 @@ class HttpURLConnectionBackend private ( conn.asInstanceOf[HttpURLConnection] } - private def writeBody(r: GenericRequest[_, R], c: HttpURLConnection): Option[OutputStream] = { + private def writeBody(r: GenericRequest[_, R], c: HttpURLConnection): Option[OutputStream] = r.body match { case NoBody => // skip @@ -105,13 +123,12 @@ class HttpURLConnectionBackend private ( case mp: BasicMultipartBody => setMultipartBody(r, mp, c) } - } private def timeout(t: Duration): Int = if (t.isFinite) t.toMillis.toInt else 0 - private def writeBasicBody(body: BasicBodyPart, os: OutputStream): Unit = { + private def writeBasicBody(body: BasicBodyPart, os: OutputStream): Unit = body match { case StringBody(b, encoding, _) => val writer = new OutputStreamWriter(os, encoding) @@ -133,15 +150,14 @@ class HttpURLConnectionBackend private ( case FileBody(f, _) => Files.copy(f.toPath, os) } - } private val BoundaryChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray private def setMultipartBody( - r: GenericRequest[_, R], - mp: BasicMultipartBody, - c: HttpURLConnection + r: GenericRequest[_, R], + mp: BasicMultipartBody, + c: HttpURLConnection ): Option[OutputStream] = { val boundary = { val tlr = ThreadLocalRandom.current() @@ -261,9 +277,9 @@ class HttpURLConnectionBackend private ( override protected def regularAsStream(response: InputStream): (Nothing, () => Identity[Unit]) = throw new IllegalStateException() override protected def handleWS[T]( - responseAs: GenericWebSocketResponseAs[T, _], - meta: ResponseMetadata, - ws: Nothing + responseAs: GenericWebSocketResponseAs[T, _], + meta: ResponseMetadata, + ws: Nothing ): Identity[T] = ws override protected def cleanupWhenNotAWebSocket(response: InputStream, e: NotAWebSocketException): Identity[Unit] = () @@ -304,14 +320,14 @@ object HttpURLConnectionBackend { } def apply( - options: BackendOptions = BackendOptions.Default, - customizeConnection: HttpURLConnection => Unit = _ => (), - createURL: String => URL = new URL(_), - openConnection: (URL, Option[java.net.Proxy]) => URLConnection = { + options: BackendOptions = BackendOptions.Default, + customizeConnection: HttpURLConnection => Unit = _ => (), + createURL: String => URL = new URL(_), + openConnection: (URL, Option[java.net.Proxy]) => URLConnection = { case (url, None) => url.openConnection() case (url, Some(proxy)) => url.openConnection(proxy) }, - customEncodingHandler: EncodingHandler = PartialFunction.empty + customEncodingHandler: EncodingHandler = PartialFunction.empty ): SyncBackend = wrappers.FollowRedirectsBackend( new HttpURLConnectionBackend(options, customizeConnection, createURL, openConnection, customEncodingHandler) diff --git a/core/src/main/scalajvm/sttp/client4/internal/FileHelpers.scala b/core/src/main/scalajvm/sttp/client4/internal/FileHelpers.scala index e0cf6e2e94..b807f789f7 100644 --- a/core/src/main/scalajvm/sttp/client4/internal/FileHelpers.scala +++ b/core/src/main/scalajvm/sttp/client4/internal/FileHelpers.scala @@ -16,36 +16,33 @@ private[client4] object FileHelpers { withResources(new FileOutputStream(file))(os => transfer(is, os)) } - def readFile(file: File): Array[Byte] = { + def readFile(file: File): Array[Byte] = withResources(new BufferedInputStream(new FileInputStream(file)))(toByteArray) - } // based on https://medium.com/@dkomanov/scala-try-with-resources-735baad0fd7d private def withResources[T <: AutoCloseable, V](r: => T)(f: T => V): V = { val resource: T = r require(resource != null, "resource is null") var exception: Throwable = null - try { + try f(resource) - } catch { + catch { case NonFatal(e) => exception = e throw e - } finally { + } finally closeAndAddSuppressed(exception, resource) - } } - private def closeAndAddSuppressed(e: Throwable, resource: AutoCloseable): Unit = { + private def closeAndAddSuppressed(e: Throwable, resource: AutoCloseable): Unit = if (e != null) { - try { + try resource.close() - } catch { + catch { case NonFatal(suppressed) => e.addSuppressed(suppressed) } } else { resource.close() } - } } diff --git a/core/src/main/scalajvm/sttp/client4/internal/httpclient/BodyFromHttpClient.scala b/core/src/main/scalajvm/sttp/client4/internal/httpclient/BodyFromHttpClient.scala index 162a49dc62..bc929771e5 100644 --- a/core/src/main/scalajvm/sttp/client4/internal/httpclient/BodyFromHttpClient.scala +++ b/core/src/main/scalajvm/sttp/client4/internal/httpclient/BodyFromHttpClient.scala @@ -14,9 +14,9 @@ private[client4] trait BodyFromHttpClient[F[_], S, B] { def compileWebSocketPipe(ws: WebSocket[F], pipe: streams.Pipe[WebSocketFrame.Data[_], WebSocketFrame]): F[Unit] def apply[T]( - response: Either[B, WebSocket[F]], - responseAs: ResponseAsDelegate[T, _], - responseMetadata: ResponseMetadata + response: Either[B, WebSocket[F]], + responseAs: ResponseAsDelegate[T, _], + responseMetadata: ResponseMetadata ): F[T] = bodyFromResponseAs(responseAs, responseMetadata, response) protected def bodyFromResponseAs: BodyFromResponseAs[F, B, WebSocket[F], streams.BinaryStream] diff --git a/core/src/main/scalajvm/sttp/client4/internal/httpclient/BodyToHttpClient.scala b/core/src/main/scalajvm/sttp/client4/internal/httpclient/BodyToHttpClient.scala index 2f2209cee2..7bdc750acf 100644 --- a/core/src/main/scalajvm/sttp/client4/internal/httpclient/BodyToHttpClient.scala +++ b/core/src/main/scalajvm/sttp/client4/internal/httpclient/BodyToHttpClient.scala @@ -2,7 +2,7 @@ package sttp.client4.internal.httpclient import sttp.capabilities.Streams import sttp.client4.internal.SttpToJavaConverters.toJavaSupplier -import sttp.client4.internal.{Utf8, throwNestedMultipartNotAllowed} +import sttp.client4.internal.{throwNestedMultipartNotAllowed, Utf8} import sttp.client4._ import sttp.model.{Header, HeaderNames, Part} import sttp.monad.MonadError @@ -21,9 +21,9 @@ private[client4] trait BodyToHttpClient[F[_], S] { implicit def monad: MonadError[F] def apply[T]( - request: GenericRequest[T, _], - builder: HttpRequest.Builder, - contentType: Option[String] + request: GenericRequest[T, _], + builder: HttpRequest.Builder, + contentType: Option[String] ): F[BodyPublisher] = { val body = request.body match { case NoBody => BodyPublishers.noBody().unit @@ -81,12 +81,11 @@ private[client4] trait BodyToHttpClient[F[_], S] { override def get(): InputStream = t } - private def withKnownContentLength(delegate: HttpRequest.BodyPublisher, cl: Long): HttpRequest.BodyPublisher = { + private def withKnownContentLength(delegate: HttpRequest.BodyPublisher, cl: Long): HttpRequest.BodyPublisher = new HttpRequest.BodyPublisher { override def contentLength(): Long = cl override def subscribe(subscriber: Flow.Subscriber[_ >: ByteBuffer]): Unit = delegate.subscribe(subscriber) } - } // https://stackoverflow.com/a/6603018/362531 private class ByteBufferBackedInputStream(buf: ByteBuffer) extends InputStream { diff --git a/core/src/main/scalajvm/sttp/client4/internal/httpclient/DelegatingWebSocketListener.scala b/core/src/main/scalajvm/sttp/client4/internal/httpclient/DelegatingWebSocketListener.scala index f5cddbe774..5272fe84f1 100644 --- a/core/src/main/scalajvm/sttp/client4/internal/httpclient/DelegatingWebSocketListener.scala +++ b/core/src/main/scalajvm/sttp/client4/internal/httpclient/DelegatingWebSocketListener.scala @@ -20,25 +20,20 @@ private[client4] class DelegatingWebSocketListener[WS_RESULT]( delegate.onOpen(webSocket) } - override def onText(webSocket: WebSocket, data: CharSequence, last: Boolean): CompletionStage[_] = { + override def onText(webSocket: WebSocket, data: CharSequence, last: Boolean): CompletionStage[_] = delegate.onText(webSocket, data, last) - } - override def onBinary(webSocket: WebSocket, data: ByteBuffer, last: Boolean): CompletionStage[_] = { + override def onBinary(webSocket: WebSocket, data: ByteBuffer, last: Boolean): CompletionStage[_] = delegate.onBinary(webSocket, data, last) - } - override def onPing(webSocket: WebSocket, message: ByteBuffer): CompletionStage[_] = { + override def onPing(webSocket: WebSocket, message: ByteBuffer): CompletionStage[_] = delegate.onPing(webSocket, message) - } - override def onPong(webSocket: WebSocket, message: ByteBuffer): CompletionStage[_] = { + override def onPong(webSocket: WebSocket, message: ByteBuffer): CompletionStage[_] = delegate.onPong(webSocket, message) - } - override def onClose(webSocket: WebSocket, statusCode: Int, reason: String): CompletionStage[_] = { + override def onClose(webSocket: WebSocket, statusCode: Int, reason: String): CompletionStage[_] = delegate.onClose(webSocket, statusCode, reason) - } override def onError(webSocket: WebSocket, error: Throwable): Unit = { if (!initialised.getAndSet(true)) { onInitialError(error) diff --git a/core/src/main/scalajvm/sttp/client4/internal/httpclient/FutureSequencer.scala b/core/src/main/scalajvm/sttp/client4/internal/httpclient/FutureSequencer.scala index cd9c9a93de..34928f9ae2 100644 --- a/core/src/main/scalajvm/sttp/client4/internal/httpclient/FutureSequencer.scala +++ b/core/src/main/scalajvm/sttp/client4/internal/httpclient/FutureSequencer.scala @@ -1,7 +1,7 @@ package sttp.client4.internal.httpclient import java.util.concurrent.Semaphore -import scala.concurrent.{ExecutionContext, Future, blocking} +import scala.concurrent.{blocking, ExecutionContext, Future} private[client4] class FutureSequencer(implicit ec: ExecutionContext) extends Sequencer[Future] { private val semaphore = new Semaphore(1) diff --git a/core/src/main/scalajvm/sttp/client4/internal/httpclient/InputStreamBodyFromHttpClient.scala b/core/src/main/scalajvm/sttp/client4/internal/httpclient/InputStreamBodyFromHttpClient.scala index 65988bf5db..711ec3f52b 100644 --- a/core/src/main/scalajvm/sttp/client4/internal/httpclient/InputStreamBodyFromHttpClient.scala +++ b/core/src/main/scalajvm/sttp/client4/internal/httpclient/InputStreamBodyFromHttpClient.scala @@ -16,12 +16,11 @@ private[client4] trait InputStreamBodyFromHttpClient[F[_], S] extends BodyFromHt override protected def withReplayableBody( response: InputStream, replayableBody: Either[Array[Byte], SttpFile] - ): F[InputStream] = { + ): F[InputStream] = (replayableBody match { case Left(byteArray) => new ByteArrayInputStream(byteArray) case Right(file) => new BufferedInputStream(new FileInputStream(file.toFile)) }).unit - } override protected def regularIgnore(response: InputStream): F[Unit] = monad.blocking(response.close()) diff --git a/core/src/main/scalajvm/sttp/client4/internal/httpclient/WebSocketImpl.scala b/core/src/main/scalajvm/sttp/client4/internal/httpclient/WebSocketImpl.scala index 6976fa0709..aae9f75b5f 100644 --- a/core/src/main/scalajvm/sttp/client4/internal/httpclient/WebSocketImpl.scala +++ b/core/src/main/scalajvm/sttp/client4/internal/httpclient/WebSocketImpl.scala @@ -21,7 +21,7 @@ private[client4] class WebSocketImpl[F[_]]( _monad: MonadAsyncError[F], sequencer: Sequencer[F] ) extends WebSocket[F] { - override def receive(): F[WebSocketFrame] = { + override def receive(): F[WebSocketFrame] = queue.poll.flatMap { case WebSocketEvent.Open() => receive() case WebSocketEvent.Frame(c: WebSocketFrame.Close) => @@ -38,9 +38,8 @@ private[client4] class WebSocketImpl[F[_]]( f } } - } - override def send(f: WebSocketFrame, isContinuation: Boolean = false): F[Unit] = { + override def send(f: WebSocketFrame, isContinuation: Boolean = false): F[Unit] = // ws.send* is not thread-safe - at least one can run at a time. Hence, adding a sequencer to ensure that // even if called concurrently, these will be run in sequence. sequencer(monad.flatten(monad.eval { @@ -57,7 +56,6 @@ private[client4] class WebSocketImpl[F[_]]( if (wasOpen) fromCompletableFuture(ws.sendClose(statusCode, reasonText)) else ().unit } })) - } override lazy val upgradeHeaders: Headers = Headers(Nil) @@ -65,23 +63,21 @@ private[client4] class WebSocketImpl[F[_]]( override implicit def monad: MonadError[F] = _monad - private def fromCompletableFuture(cf: CompletableFuture[JWebSocket]): F[Unit] = { + private def fromCompletableFuture(cf: CompletableFuture[JWebSocket]): F[Unit] = _monad.async { cb => cf.whenComplete(new BiConsumer[JWebSocket, Throwable] { - override def accept(t: JWebSocket, error: Throwable): Unit = { + override def accept(t: JWebSocket, error: Throwable): Unit = if (error != null) { cb(Left(error)) } else { cb(Right(())) } - } }) Canceler { () => cf.cancel(true) () } } - } } private[client4] class AddToQueueListener[F[_]]( diff --git a/core/src/main/scalanative/sttp/client4/FileHelpers.scala b/core/src/main/scalanative/sttp/client4/FileHelpers.scala index 86d9af3223..5fbb087c1a 100644 --- a/core/src/main/scalanative/sttp/client4/FileHelpers.scala +++ b/core/src/main/scalanative/sttp/client4/FileHelpers.scala @@ -11,9 +11,9 @@ object FileHelpers { if (file.getParentFile != null) { file.getParentFile.mkdirs() } - try { + try file.createNewFile() - } catch { + catch { case e: AccessDeniedException => throw new IOException("Permission denied", e) // aligns SN bahavior with Java } } @@ -24,7 +24,7 @@ object FileHelpers { file } - private[client4] def getFilePath[T, R](response: GenericResponseAs[T, R]): Option[SttpFile] = { + private[client4] def getFilePath[T, R](response: GenericResponseAs[T, R]): Option[SttpFile] = response match { case MappedResponseAs(raw, g, _) => getFilePath(raw) case rfm: ResponseAsFromMetadata[T, _] => @@ -34,5 +34,4 @@ object FileHelpers { case ResponseAsFile(file) => Some(file) case _ => None } - } } diff --git a/core/src/main/scalanative/sttp/client4/curl/AbstractCurlBackend.scala b/core/src/main/scalanative/sttp/client4/curl/AbstractCurlBackend.scala index 19bc3ae965..47a557e39a 100644 --- a/core/src/main/scalanative/sttp/client4/curl/AbstractCurlBackend.scala +++ b/core/src/main/scalanative/sttp/client4/curl/AbstractCurlBackend.scala @@ -15,7 +15,7 @@ import sttp.monad.syntax._ import scala.collection.immutable.Seq import scala.io.Source -import scala.scalanative.libc.stdio.{FILE, fclose, fopen} +import scala.scalanative.libc.stdio.{fclose, fopen, FILE} import scala.scalanative.libc.stdlib._ import scala.scalanative.libc.string._ import scala.scalanative.unsafe @@ -70,7 +70,7 @@ abstract class AbstractCurlBackend[F[_]](_monad: MonadError[F], verbose: Boolean ) private def handleBase[T](request: GenericRequest[T, R], curl: CurlHandle, spaces: CurlSpaces)(implicit - z: unsafe.Zone + z: unsafe.Zone ) = { curl.option(WriteFunction, AbstractCurlBackend.wdFunc) curl.option(WriteData, spaces.bodyResp) @@ -240,9 +240,9 @@ abstract class AbstractCurlBackend[F[_]](_monad: MonadError[F], verbose: Boolean throw new IllegalStateException("CurlBackend does not support streaming responses") override protected def handleWS[T]( - responseAs: GenericWebSocketResponseAs[T, _], - meta: ResponseMetadata, - ws: Nothing + responseAs: GenericWebSocketResponseAs[T, _], + meta: ResponseMetadata, + ws: Nothing ): F[T] = ws override protected def cleanupWhenNotAWebSocket( @@ -253,22 +253,20 @@ abstract class AbstractCurlBackend[F[_]](_monad: MonadError[F], verbose: Boolean override protected def cleanupWhenGotWebSocket(response: Nothing, e: GotAWebSocketException): F[Unit] = response } - private def transformHeaders(reqHeaders: Iterable[Header])(implicit z: Zone): CurlList = { + private def transformHeaders(reqHeaders: Iterable[Header])(implicit z: Zone): CurlList = reqHeaders - .map { header => s"${header.name}: ${header.value}" } + .map(header => s"${header.name}: ${header.value}") .foldLeft(new CurlList(null)) { case (acc, h) => new CurlList(acc.ptr.append(h)) } - } private def toByteArray(str: String): F[Array[Byte]] = monad.unit(str.getBytes) - private def lift(code: CurlCode): F[CurlCode] = { + private def lift(code: CurlCode): F[CurlCode] = code match { case CurlCode.Ok => monad.unit(code) case _ => monad.error(new RuntimeException(s"Command failed with status $code")) } - } } object AbstractCurlBackend { diff --git a/core/src/main/scalanative/sttp/client4/curl/internal/CurlApi.scala b/core/src/main/scalanative/sttp/client4/curl/internal/CurlApi.scala index 092bef2729..112c5181d6 100644 --- a/core/src/main/scalanative/sttp/client4/curl/internal/CurlApi.scala +++ b/core/src/main/scalanative/sttp/client4/curl/internal/CurlApi.scala @@ -26,29 +26,23 @@ private[client4] object CurlApi { def cleanup(): Unit = CCurl.cleanup(handle) - def option(option: CurlOption, parameter: String)(implicit z: Zone): CurlCode = { + def option(option: CurlOption, parameter: String)(implicit z: Zone): CurlCode = setopt(handle, option, toCString(parameter)) - } - def option(option: CurlOption, parameter: Long)(implicit z: Zone): CurlCode = { + def option(option: CurlOption, parameter: Long)(implicit z: Zone): CurlCode = setopt(handle, option, parameter) - } - def option(option: CurlOption, parameter: Int)(implicit z: Zone): CurlCode = { + def option(option: CurlOption, parameter: Int)(implicit z: Zone): CurlCode = setopt(handle, option, parameter) - } - def option(option: CurlOption, parameter: Boolean)(implicit z: Zone): CurlCode = { + def option(option: CurlOption, parameter: Boolean)(implicit z: Zone): CurlCode = setopt(handle, option, if (parameter) 1L else 0L) - } - def option(option: CurlOption, parameter: Ptr[_]): CurlCode = { + def option(option: CurlOption, parameter: Ptr[_]): CurlCode = setopt(handle, option, parameter) - } - def option[FuncPtr <: CFuncPtr](option: CurlOption, parameter: FuncPtr)(implicit z: Zone): CurlCode = { + def option[FuncPtr <: CFuncPtr](option: CurlOption, parameter: FuncPtr)(implicit z: Zone): CurlCode = setopt(handle, option, Boxes.boxToPtr[Byte](Boxes.unboxToCFuncPtr0(parameter))) - } def info(curlInfo: CurlInfo, parameter: Long)(implicit z: Zone): CurlCode = { val lPtr = alloc[Long](sizeof[Long]) @@ -56,26 +50,21 @@ private[client4] object CurlApi { getInfo(handle, curlInfo, lPtr) } - def info(curlInfo: CurlInfo, parameter: String)(implicit z: Zone): CurlCode = { + def info(curlInfo: CurlInfo, parameter: String)(implicit z: Zone): CurlCode = getInfo(handle, curlInfo, toCString(parameter)) - } - def info(curlInfo: CurlInfo, parameter: Ptr[_]): CurlCode = { + def info(curlInfo: CurlInfo, parameter: Ptr[_]): CurlCode = getInfo(handle, curlInfo, parameter) - } } - private def setopt(handle: CurlHandle, option: CurlOption, parameter: Ptr[_]): CurlCode = { + private def setopt(handle: CurlHandle, option: CurlOption, parameter: Ptr[_]): CurlCode = CurlCode(CCurl.setopt(handle, option.id, parameter)) - } - private def setopt(handle: CurlHandle, option: CurlOption, parameter: CVarArg)(implicit z: Zone): CurlCode = { + private def setopt(handle: CurlHandle, option: CurlOption, parameter: CVarArg)(implicit z: Zone): CurlCode = CurlCode(CCurl.setopt(handle, option.id, toCVarArgList(Seq(parameter)))) - } - private def getInfo(handle: CurlHandle, curlInfo: CurlInfo, parameter: Ptr[_]): CurlCode = { + private def getInfo(handle: CurlHandle, curlInfo: CurlInfo, parameter: Ptr[_]): CurlCode = CurlCode(CCurl.getInfo(handle, curlInfo.id, parameter)) - } implicit class MimeHandleOps(handle: MimeHandle) { def free(): Unit = CCurl.mimeFree(handle) @@ -104,12 +93,10 @@ private[client4] object CurlApi { } implicit class SlistHandleOps(handle: SlistHandle) { - def append(string: String)(implicit z: Zone): Ptr[CurlSlist] = { + def append(string: String)(implicit z: Zone): Ptr[CurlSlist] = CCurl.slistAppend(handle, toCString(string)(z)) - } - def free(): Unit = { + def free(): Unit = CCurl.slistFree(handle) - } } } diff --git a/effects/cats/src/main/scalajvm/sttp/client4/httpclient/cats/CatsSimpleQueue.scala b/effects/cats/src/main/scalajvm/sttp/client4/httpclient/cats/CatsSimpleQueue.scala index 65b0871714..10d96056a0 100644 --- a/effects/cats/src/main/scalajvm/sttp/client4/httpclient/cats/CatsSimpleQueue.scala +++ b/effects/cats/src/main/scalajvm/sttp/client4/httpclient/cats/CatsSimpleQueue.scala @@ -9,7 +9,7 @@ import sttp.ws.WebSocketBufferFull class CatsSimpleQueue[F[_], A](queue: Queue[F, A], capacity: Option[Int], dispatcher: Dispatcher[F])(implicit F: MonadError[F, Throwable] ) extends SimpleQueue[F, A] { - override def offer(t: A): Unit = { + override def offer(t: A): Unit = dispatcher.unsafeRunSync( queue .tryOffer(t) @@ -18,7 +18,6 @@ class CatsSimpleQueue[F[_], A](queue: Queue[F, A], capacity: Option[Int], dispat case false => F.raiseError(new WebSocketBufferFull(capacity.getOrElse(Int.MaxValue))) } ) - } override def poll: F[A] = queue.take } diff --git a/effects/cats/src/main/scalajvm/sttp/client4/httpclient/cats/HttpClientCatsBackend.scala b/effects/cats/src/main/scalajvm/sttp/client4/httpclient/cats/HttpClientCatsBackend.scala index ceb7be8ed9..77e7fadaaa 100644 --- a/effects/cats/src/main/scalajvm/sttp/client4/httpclient/cats/HttpClientCatsBackend.scala +++ b/effects/cats/src/main/scalajvm/sttp/client4/httpclient/cats/HttpClientCatsBackend.scala @@ -8,10 +8,10 @@ import sttp.client4.httpclient.HttpClientBackend.EncodingHandler import sttp.client4.impl.cats.CatsMonadAsyncError import sttp.client4.internal.httpclient._ import sttp.client4.internal.ws.SimpleQueue -import sttp.client4.internal.{NoStreams, emptyInputStream} +import sttp.client4.internal.{emptyInputStream, NoStreams} import sttp.client4.testing.WebSocketBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, WebSocketBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketBackend} import sttp.monad.MonadError import sttp.ws.{WebSocket, WebSocketFrame} @@ -89,11 +89,11 @@ object HttpClientCatsBackend { ) def apply[F[_]: Async]( - dispatcher: Dispatcher[F], - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: EncodingHandler[InputStream] = PartialFunction.empty - ): F[WebSocketBackend[F]] = { + dispatcher: Dispatcher[F], + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: EncodingHandler[InputStream] = PartialFunction.empty + ): F[WebSocketBackend[F]] = Async[F].executor.flatMap(executor => Sync[F].delay( HttpClientCatsBackend( @@ -105,12 +105,11 @@ object HttpClientCatsBackend { ) ) ) - } def resource[F[_]: Async]( - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: EncodingHandler[InputStream] = PartialFunction.empty + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: EncodingHandler[InputStream] = PartialFunction.empty ): Resource[F, WebSocketBackend[F]] = Dispatcher .parallel[F] diff --git a/effects/fs2-ce2/src/main/scala/sttp/client4/impl/fs2/Fs2SimpleQueue.scala b/effects/fs2-ce2/src/main/scala/sttp/client4/impl/fs2/Fs2SimpleQueue.scala index 5788d3dcd4..47b0649f65 100644 --- a/effects/fs2-ce2/src/main/scala/sttp/client4/impl/fs2/Fs2SimpleQueue.scala +++ b/effects/fs2-ce2/src/main/scala/sttp/client4/impl/fs2/Fs2SimpleQueue.scala @@ -7,14 +7,13 @@ import sttp.ws.WebSocketBufferFull class Fs2SimpleQueue[F[_], A](queue: InspectableQueue[F, A], capacity: Option[Int])(implicit F: Effect[F]) extends SimpleQueue[F, A] { - override def offer(t: A): Unit = { + override def offer(t: A): Unit = F.toIO(queue.offer1(t)) .flatMap { case true => IO.unit case false => IO.raiseError(WebSocketBufferFull(capacity.getOrElse(Int.MaxValue))) } .unsafeRunSync() - } override def poll: F[A] = queue.dequeue1 } diff --git a/effects/fs2-ce2/src/main/scala/sttp/client4/impl/fs2/Fs2WebSockets.scala b/effects/fs2-ce2/src/main/scala/sttp/client4/impl/fs2/Fs2WebSockets.scala index be8d79f0ff..ad382a0cce 100644 --- a/effects/fs2-ce2/src/main/scala/sttp/client4/impl/fs2/Fs2WebSockets.scala +++ b/effects/fs2-ce2/src/main/scala/sttp/client4/impl/fs2/Fs2WebSockets.scala @@ -23,7 +23,7 @@ object Fs2WebSockets { */ def handleThroughPipe[F[_]: ConcurrentEffect]( ws: WebSocket[F] - )(pipe: Pipe[F, WebSocketFrame.Data[_], WebSocketFrame]): F[Unit] = { + )(pipe: Pipe[F, WebSocketFrame.Data[_], WebSocketFrame]): F[Unit] = Stream .eval(Ref.of[F, Option[WebSocketFrame.Close]](None)) .flatMap { closeRef => @@ -51,7 +51,6 @@ object Fs2WebSockets { .compile .drain .guarantee(ws.close()) - } def fromTextPipe[F[_]]: (String => WebSocketFrame) => fs2.Pipe[F, WebSocketFrame, WebSocketFrame] = f => fromTextPipeF(_.map(f)) diff --git a/effects/fs2-ce2/src/main/scalajvm/sttp/client4/httpclient/fs2/Fs2BodyFromHttpClient.scala b/effects/fs2-ce2/src/main/scalajvm/sttp/client4/httpclient/fs2/Fs2BodyFromHttpClient.scala index d651cb5edd..18e8e96de4 100644 --- a/effects/fs2-ce2/src/main/scalajvm/sttp/client4/httpclient/fs2/Fs2BodyFromHttpClient.scala +++ b/effects/fs2-ce2/src/main/scalajvm/sttp/client4/httpclient/fs2/Fs2BodyFromHttpClient.scala @@ -28,33 +28,30 @@ private[fs2] class Fs2BodyFromHttpClient[F[_]: ConcurrentEffect: ContextShift](b override protected def withReplayableBody( response: Stream[F, Byte], replayableBody: Either[Array[Byte], SttpFile] - ): F[Stream[F, Byte]] = { + ): F[Stream[F, Byte]] = replayableBody match { case Left(value) => Stream.evalSeq[F, List, Byte](value.toList.unit).unit case Right(sttpFile) => fs2.io.file.readAll(sttpFile.toPath, blocker, 32 * 1024).unit } - } override protected def regularIgnore(response: Stream[F, Byte]): F[Unit] = response.compile.drain override protected def regularAsByteArray(response: Stream[F, Byte]): F[Array[Byte]] = response.chunkAll.compile.last.map(_.map(_.toArray).getOrElse(Array())) - override protected def regularAsFile(response: Stream[F, Byte], file: SttpFile): F[SttpFile] = { + override protected def regularAsFile(response: Stream[F, Byte], file: SttpFile): F[SttpFile] = response .through(fs2.io.file.writeAll(file.toPath, blocker)) .compile .drain .map(_ => file) - } - override protected def regularAsStream(response: Stream[F, Byte]): F[(Stream[F, Byte], () => F[Unit])] = { + override protected def regularAsStream(response: Stream[F, Byte]): F[(Stream[F, Byte], () => F[Unit])] = ( response, // ignoring exceptions that occur when draining (i.e. the stream is already drained) () => response.compile.drain.handleError { case _: Exception => ().unit } ).unit - } override protected def handleWS[T]( responseAs: GenericWebSocketResponseAs[T, _], diff --git a/effects/fs2-ce2/src/main/scalajvm/sttp/client4/httpclient/fs2/HttpClientFs2Backend.scala b/effects/fs2-ce2/src/main/scalajvm/sttp/client4/httpclient/fs2/HttpClientFs2Backend.scala index c6680b9c18..dc9488480e 100644 --- a/effects/fs2-ce2/src/main/scalajvm/sttp/client4/httpclient/fs2/HttpClientFs2Backend.scala +++ b/effects/fs2-ce2/src/main/scalajvm/sttp/client4/httpclient/fs2/HttpClientFs2Backend.scala @@ -72,12 +72,11 @@ class HttpClientFs2Backend[F[_]: ConcurrentEffect: ContextShift] private ( override protected def createSequencer: F[Sequencer[F]] = Fs2Sequencer.create - override protected def bodyHandlerBodyToBody(p: Publisher[ju.List[ByteBuffer]]): Stream[F, Byte] = { + override protected def bodyHandlerBodyToBody(p: Publisher[ju.List[ByteBuffer]]): Stream[F, Byte] = FlowAdapters .toPublisher(p) .toStream[F] .flatMap(data => Stream.emits(data.asScala.map(Chunk.byteBuffer)).flatMap(Stream.chunk)) - } override protected def emptyBody(): Stream[F, Byte] = Stream.empty @@ -103,10 +102,10 @@ object HttpClientFs2Backend { ) def apply[F[_]: ConcurrentEffect: ContextShift]( - blocker: Blocker, - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: Fs2EncodingHandler[F] = PartialFunction.empty + blocker: Blocker, + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: Fs2EncodingHandler[F] = PartialFunction.empty ): F[WebSocketStreamBackend[F, Fs2Streams[F]]] = Sync[F].delay( HttpClientFs2Backend( @@ -119,10 +118,10 @@ object HttpClientFs2Backend { ) def resource[F[_]: ConcurrentEffect: ContextShift]( - blocker: Blocker, - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: Fs2EncodingHandler[F] = PartialFunction.empty + blocker: Blocker, + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: Fs2EncodingHandler[F] = PartialFunction.empty ): Resource[F, WebSocketStreamBackend[F, Fs2Streams[F]]] = Resource.make(apply(blocker, options, customizeRequest, customEncodingHandler))(_.close()) diff --git a/effects/fs2/src/main/scala/sttp/client4/impl/fs2/Fs2WebSockets.scala b/effects/fs2/src/main/scala/sttp/client4/impl/fs2/Fs2WebSockets.scala index 46a5ae1dbd..6a823992fb 100644 --- a/effects/fs2/src/main/scala/sttp/client4/impl/fs2/Fs2WebSockets.scala +++ b/effects/fs2/src/main/scala/sttp/client4/impl/fs2/Fs2WebSockets.scala @@ -22,7 +22,7 @@ object Fs2WebSockets { */ def handleThroughPipe[F[_]: Concurrent]( ws: WebSocket[F] - )(pipe: Pipe[F, WebSocketFrame.Data[_], WebSocketFrame]): F[Unit] = { + )(pipe: Pipe[F, WebSocketFrame.Data[_], WebSocketFrame]): F[Unit] = Stream .eval(Ref.of[F, Option[WebSocketFrame.Close]](None)) .flatMap { closeRef => @@ -50,7 +50,6 @@ object Fs2WebSockets { .compile .drain .guarantee(ws.close()) - } def fromTextPipe[F[_]]: (String => WebSocketFrame) => fs2.Pipe[F, WebSocketFrame, WebSocketFrame] = f => fromTextPipeF(_.map(f)) diff --git a/effects/fs2/src/main/scalajs/sttp.client4.impl.fs2/Fs2SimpleQueue.scala b/effects/fs2/src/main/scalajs/sttp.client4.impl.fs2/Fs2SimpleQueue.scala index 389cddaeb2..46d70e9430 100644 --- a/effects/fs2/src/main/scalajs/sttp.client4.impl.fs2/Fs2SimpleQueue.scala +++ b/effects/fs2/src/main/scalajs/sttp.client4.impl.fs2/Fs2SimpleQueue.scala @@ -9,7 +9,7 @@ import sttp.ws.WebSocketBufferFull class Fs2SimpleQueue[F[_], A](queue: Queue[F, A], capacity: Option[Int], dispatcher: Dispatcher[F])(implicit F: MonadError[F, Throwable] ) extends SimpleQueue[F, A] { - override def offer(t: A): Unit = { + override def offer(t: A): Unit = // On the JVM, we do unsafeRunSync. Here this is not possible, so just starting a future and leaving it running, // without waiting for it to complete (the `offer` contract allows that). dispatcher.unsafeToFuture( @@ -20,7 +20,6 @@ class Fs2SimpleQueue[F[_], A](queue: Queue[F, A], capacity: Option[Int], dispatc case false => F.raiseError(new WebSocketBufferFull(capacity.getOrElse(Int.MaxValue))) } ) - } override def poll: F[A] = queue.take } diff --git a/effects/fs2/src/main/scalajvm/sttp/client4/httpclient/fs2/Fs2BodyFromHttpClient.scala b/effects/fs2/src/main/scalajvm/sttp/client4/httpclient/fs2/Fs2BodyFromHttpClient.scala index 683d0565eb..1e70578071 100644 --- a/effects/fs2/src/main/scalajvm/sttp/client4/httpclient/fs2/Fs2BodyFromHttpClient.scala +++ b/effects/fs2/src/main/scalajvm/sttp/client4/httpclient/fs2/Fs2BodyFromHttpClient.scala @@ -28,33 +28,30 @@ private[fs2] class Fs2BodyFromHttpClient[F[_]: Async]() extends BodyFromHttpClie override protected def withReplayableBody( response: Stream[F, Byte], replayableBody: Either[Array[Byte], SttpFile] - ): F[Stream[F, Byte]] = { + ): F[Stream[F, Byte]] = replayableBody match { case Left(value) => Stream.evalSeq[F, List, Byte](value.toList.unit).unit case Right(sttpFile) => Files[F].readAll(sttpFile.toPath, 32 * 1024).unit } - } override protected def regularIgnore(response: Stream[F, Byte]): F[Unit] = response.compile.drain override protected def regularAsByteArray(response: Stream[F, Byte]): F[Array[Byte]] = response.chunkAll.compile.last.map(_.map(_.toArray).getOrElse(Array())) - override protected def regularAsFile(response: Stream[F, Byte], file: SttpFile): F[SttpFile] = { + override protected def regularAsFile(response: Stream[F, Byte], file: SttpFile): F[SttpFile] = response .through(Files[F].writeAll(file.toPath)) .compile .drain .map(_ => file) - } - override protected def regularAsStream(response: Stream[F, Byte]): F[(Stream[F, Byte], () => F[Unit])] = { + override protected def regularAsStream(response: Stream[F, Byte]): F[(Stream[F, Byte], () => F[Unit])] = ( response, // ignoring exceptions that occur when draining (i.e. the stream is already drained) () => response.compile.drain.handleError { case _: Exception => ().unit } ).unit - } override protected def handleWS[T]( responseAs: GenericWebSocketResponseAs[T, _], diff --git a/effects/fs2/src/main/scalajvm/sttp/client4/httpclient/fs2/HttpClientFs2Backend.scala b/effects/fs2/src/main/scalajvm/sttp/client4/httpclient/fs2/HttpClientFs2Backend.scala index dddc0add6a..38d967a350 100644 --- a/effects/fs2/src/main/scalajvm/sttp/client4/httpclient/fs2/HttpClientFs2Backend.scala +++ b/effects/fs2/src/main/scalajvm/sttp/client4/httpclient/fs2/HttpClientFs2Backend.scala @@ -73,12 +73,11 @@ class HttpClientFs2Backend[F[_]: Async] private ( override protected def createSequencer: F[Sequencer[F]] = Fs2Sequencer.create - override protected def bodyHandlerBodyToBody(p: Publisher[util.List[ByteBuffer]]): Stream[F, Byte] = { + override protected def bodyHandlerBodyToBody(p: Publisher[util.List[ByteBuffer]]): Stream[F, Byte] = FlowAdapters .toPublisher(p) .toStream[F] .flatMap(data => Stream.emits(data.asScala.map(Chunk.byteBuffer)).flatMap(Stream.chunk)) - } override protected def emptyBody(): Stream[F, Byte] = Stream.empty @@ -104,11 +103,11 @@ object HttpClientFs2Backend { ) def apply[F[_]: Async]( - dispatcher: Dispatcher[F], - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: Fs2EncodingHandler[F] = PartialFunction.empty - ): F[WebSocketStreamBackend[F, Fs2Streams[F]]] = { + dispatcher: Dispatcher[F], + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: Fs2EncodingHandler[F] = PartialFunction.empty + ): F[WebSocketStreamBackend[F, Fs2Streams[F]]] = Async[F].executor.flatMap(executor => Sync[F].delay( HttpClientFs2Backend( @@ -120,12 +119,11 @@ object HttpClientFs2Backend { ) ) ) - } def resource[F[_]: Async]( - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: Fs2EncodingHandler[F] = PartialFunction.empty + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: Fs2EncodingHandler[F] = PartialFunction.empty ): Resource[F, WebSocketStreamBackend[F, Fs2Streams[F]]] = Dispatcher .parallel[F] diff --git a/effects/fs2/src/main/scalajvm/sttp/client4/impl/fs2/Fs2SimpleQueue.scala b/effects/fs2/src/main/scalajvm/sttp/client4/impl/fs2/Fs2SimpleQueue.scala index 958a99caff..f0ffa85608 100644 --- a/effects/fs2/src/main/scalajvm/sttp/client4/impl/fs2/Fs2SimpleQueue.scala +++ b/effects/fs2/src/main/scalajvm/sttp/client4/impl/fs2/Fs2SimpleQueue.scala @@ -9,7 +9,7 @@ import sttp.ws.WebSocketBufferFull class Fs2SimpleQueue[F[_], A](queue: Queue[F, A], capacity: Option[Int], dispatcher: Dispatcher[F])(implicit F: MonadError[F, Throwable] ) extends SimpleQueue[F, A] { - override def offer(t: A): Unit = { + override def offer(t: A): Unit = dispatcher.unsafeRunSync( queue .tryOffer(t) @@ -18,7 +18,6 @@ class Fs2SimpleQueue[F[_], A](queue: Queue[F, A], capacity: Option[Int], dispatc case false => F.raiseError(new WebSocketBufferFull(capacity.getOrElse(Int.MaxValue))) } ) - } override def poll: F[A] = queue.take } diff --git a/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixServerSentEvents.scala b/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixServerSentEvents.scala index 7f9e87f967..42b917c80b 100644 --- a/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixServerSentEvents.scala +++ b/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixServerSentEvents.scala @@ -22,13 +22,12 @@ object MonixServerSentEvents { // ------- // this part is utf8 incomplete byte detection from monix from monix.nio.text.UTF8Codec // it is added to this file instead of called from library for compatibility with scalajs that does not have nio - private def indexIncrement(b: Byte): Int = { + private def indexIncrement(b: Byte): Int = if ((b & 0x80) == 0) 0 // ASCII byte else if ((b & 0xe0) == 0xc0) 2 // first of a 2 byte seq else if ((b & 0xf0) == 0xe0) 3 // first of a 3 byte seq else if ((b & 0xf8) == 0xf0) 4 // first of a 4 byte seq else 0 // following char - } private def missingBytes(bytes: Array[Byte]): Option[Int] = { val lastThree = bytes.drop(0 max bytes.length - 3) val addBytesFromLast3 = lastThree.zipWithIndex.foldLeft(Option.empty[Int]) { (acc, elem) => diff --git a/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixSimpleQueue.scala b/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixSimpleQueue.scala index 24628c1149..add8b0b585 100644 --- a/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixSimpleQueue.scala +++ b/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixSimpleQueue.scala @@ -1,7 +1,7 @@ package sttp.client4.impl.monix import monix.eval.Task -import monix.execution.{Scheduler, AsyncQueue => MAsyncQueue} +import monix.execution.{AsyncQueue => MAsyncQueue, Scheduler} import sttp.client4.internal.ws.SimpleQueue import sttp.ws.WebSocketBufferFull @@ -11,10 +11,9 @@ class MonixSimpleQueue[A](bufferCapacity: Option[Int])(implicit s: Scheduler) ex case None => MAsyncQueue.unbounded[A]() } - override def offer(t: A): Unit = { + override def offer(t: A): Unit = if (!queue.tryOffer(t)) { throw WebSocketBufferFull(bufferCapacity.getOrElse(Int.MaxValue)) } - } override def poll: Task[A] = Task.deferFuture(queue.poll()) } diff --git a/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixWebSockets.scala b/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixWebSockets.scala index 551d4fe35a..47447eb70d 100644 --- a/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixWebSockets.scala +++ b/effects/monix/src/main/scala/sttp/client4/impl/monix/MonixWebSockets.scala @@ -10,7 +10,7 @@ object MonixWebSockets { def compilePipe( ws: WebSocket[Task], pipe: Observable[WebSocketFrame.Data[_]] => Observable[WebSocketFrame] - ): Task[Unit] = { + ): Task[Unit] = Task(BooleanCancelable()).flatMap { wsClosed => val onClose = Task(wsClosed.cancel()).map(_ => None) pipe( @@ -32,7 +32,6 @@ object MonixWebSockets { .completedL .guarantee(ws.close()) } - } def fromTextPipe: (String => WebSocketFrame) => MonixStreams.Pipe[WebSocketFrame, WebSocketFrame] = f => fromTextPipeF(_.map(f)) diff --git a/effects/monix/src/main/scalajs/sttp/client4/impl/monix/FetchMonixBackend.scala b/effects/monix/src/main/scalajs/sttp/client4/impl/monix/FetchMonixBackend.scala index 19b7760e5d..63e702aabb 100644 --- a/effects/monix/src/main/scalajs/sttp/client4/impl/monix/FetchMonixBackend.scala +++ b/effects/monix/src/main/scalajs/sttp/client4/impl/monix/FetchMonixBackend.scala @@ -43,14 +43,14 @@ class FetchMonixBackend private (fetchOptions: FetchOptions, customizeRequest: F override protected def handleResponseAsStream( response: FetchResponse - ): Task[(Observable[Array[Byte]], () => Task[Unit])] = { + ): Task[(Observable[Array[Byte]], () => Task[Unit])] = Task .delay { lazy val reader = response.body.getReader() def read() = convertFromFuture(reader.read().toFuture) - def go(): Observable[Array[Byte]] = { + def go(): Observable[Array[Byte]] = Observable.fromTask(read()).flatMap { chunk => if (chunk.done) Observable.empty else { @@ -58,11 +58,9 @@ class FetchMonixBackend private (fetchOptions: FetchOptions, customizeRequest: F Observable.pure(bytes) ++ go() } } - } val cancel = Task(reader.cancel("Response body reader cancelled")).void (go().doOnSubscriptionCancel(cancel), () => cancel) } - } override protected def compileWebSocketPipe( ws: WebSocket[Task], diff --git a/effects/monix/src/main/scalajvm/sttp/client4/httpclient/monix/HttpClientMonixBackend.scala b/effects/monix/src/main/scalajvm/sttp/client4/httpclient/monix/HttpClientMonixBackend.scala index 833b91692b..a76b436302 100644 --- a/effects/monix/src/main/scalajvm/sttp/client4/httpclient/monix/HttpClientMonixBackend.scala +++ b/effects/monix/src/main/scalajvm/sttp/client4/httpclient/monix/HttpClientMonixBackend.scala @@ -16,7 +16,7 @@ import sttp.client4.internal.httpclient.{BodyFromHttpClient, BodyToHttpClient, S import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.WebSocketStreamBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, WebSocketStreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketStreamBackend} import sttp.monad.MonadError import java.io.UnsupportedEncodingException @@ -69,12 +69,11 @@ class HttpClientMonixBackend private ( override protected def createBodyHandler: HttpResponse.BodyHandler[Publisher[ju.List[ByteBuffer]]] = BodyHandlers.ofPublisher() - override protected def bodyHandlerBodyToBody(p: Publisher[ju.List[ByteBuffer]]): Observable[Array[Byte]] = { + override protected def bodyHandlerBodyToBody(p: Publisher[ju.List[ByteBuffer]]): Observable[Array[Byte]] = Observable .fromReactivePublisher(FlowAdapters.toPublisher(p)) .flatMapIterable(_.asScala.toList) .map(_.safeRead()) - } override protected def emptyBody(): Observable[Array[Byte]] = Observable.empty @@ -101,9 +100,9 @@ object HttpClientMonixBackend { ) def apply( - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: MonixEncodingHandler = PartialFunction.empty + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: MonixEncodingHandler = PartialFunction.empty )(implicit s: Scheduler = Scheduler.global ): Task[WebSocketStreamBackend[Task, MonixStreams]] = @@ -117,9 +116,9 @@ object HttpClientMonixBackend { ) def resource( - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: MonixEncodingHandler = PartialFunction.empty + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: MonixEncodingHandler = PartialFunction.empty )(implicit s: Scheduler = Scheduler.global ): Resource[Task, WebSocketStreamBackend[Task, MonixStreams]] = diff --git a/effects/monix/src/main/scalajvm/sttp/client4/httpclient/monix/MonixBodyFromHttpClient.scala b/effects/monix/src/main/scalajvm/sttp/client4/httpclient/monix/MonixBodyFromHttpClient.scala index 7d9bdf373e..0e3ac75cb8 100644 --- a/effects/monix/src/main/scalajvm/sttp/client4/httpclient/monix/MonixBodyFromHttpClient.scala +++ b/effects/monix/src/main/scalajvm/sttp/client4/httpclient/monix/MonixBodyFromHttpClient.scala @@ -26,17 +26,16 @@ private[monix] trait MonixBodyFromHttpClient extends BodyFromHttpClient[Task, Mo MonixWebSockets.compilePipe(ws, pipe) override protected def bodyFromResponseAs - : BodyFromResponseAs[Task, Observable[Array[Byte]], WebSocket[Task], Observable[Array[Byte]]] = { + : BodyFromResponseAs[Task, Observable[Array[Byte]], WebSocket[Task], Observable[Array[Byte]]] = new BodyFromResponseAs[Task, MonixStreams.BinaryStream, WebSocket[Task], MonixStreams.BinaryStream]() { override protected def withReplayableBody( response: Observable[Array[Byte]], replayableBody: Either[Array[Byte], SttpFile] - ): Task[Observable[Array[Byte]]] = { + ): Task[Observable[Array[Byte]]] = replayableBody match { case Left(value) => Task.pure(Observable.now(value)) case Right(file) => Task.pure(readAsync(file.toPath, 32 * 1024)) } - } override protected def regularIgnore(response: Observable[Array[Byte]]): Task[Unit] = response.consumeWith(Consumer.complete) @@ -68,5 +67,4 @@ private[monix] trait MonixBodyFromHttpClient extends BodyFromHttpClient[Task, Mo override protected def cleanupWhenGotWebSocket(response: WebSocket[Task], e: GotAWebSocketException): Task[Unit] = response.close() } - } } diff --git a/effects/zio/src/main/scala/sttp/client4/impl/zio/ZioSimpleQueue.scala b/effects/zio/src/main/scala/sttp/client4/impl/zio/ZioSimpleQueue.scala index 89ff842ec1..5d6cfa07d2 100644 --- a/effects/zio/src/main/scala/sttp/client4/impl/zio/ZioSimpleQueue.scala +++ b/effects/zio/src/main/scala/sttp/client4/impl/zio/ZioSimpleQueue.scala @@ -5,14 +5,12 @@ import sttp.ws.WebSocketBufferFull import zio.{Queue, RIO, Runtime, Unsafe} private[client4] class ZioSimpleQueue[R, A](queue: Queue[A], runtime: Runtime[Any]) extends SimpleQueue[RIO[R, *], A] { - override def offer(t: A): Unit = { + override def offer(t: A): Unit = Unsafe.unsafeCompat { implicit u => if (!runtime.unsafe.run(queue.offer(t)).getOrThrowFiberFailure()) { throw WebSocketBufferFull(queue.capacity) } } - } - override def poll: RIO[R, A] = { + override def poll: RIO[R, A] = queue.take - } } diff --git a/effects/zio/src/main/scalajs/sttp/client4/impl/zio/FetchZioBackend.scala b/effects/zio/src/main/scalajs/sttp/client4/impl/zio/FetchZioBackend.scala index 0b1f59d795..a6c6a6ccde 100644 --- a/effects/zio/src/main/scalajs/sttp/client4/impl/zio/FetchZioBackend.scala +++ b/effects/zio/src/main/scalajs/sttp/client4/impl/zio/FetchZioBackend.scala @@ -46,13 +46,13 @@ class FetchZioBackend private (fetchOptions: FetchOptions, customizeRequest: Fet override protected def handleResponseAsStream( response: FetchResponse - ): Task[(Observable[Byte], () => Task[Unit])] = { + ): Task[(Observable[Byte], () => Task[Unit])] = ZIO.attempt { lazy val reader = response.body.getReader() def read() = convertFromFuture(reader.read().toFuture) - def go(): Observable[Byte] = { + def go(): Observable[Byte] = ZStream.fromZIO(read()).flatMap { chunk => if (chunk.done) ZStream.empty else { @@ -60,11 +60,9 @@ class FetchZioBackend private (fetchOptions: FetchOptions, customizeRequest: Fet ZStream.fromChunk(Chunk.fromArray(bytes)) ++ go() } } - } val cancel = ZIO.fromPromiseJS(reader.cancel("Response body reader cancelled")) (ZStream.fromIterableZIO(go().runCollect.onInterrupt(cancel.catchAll(_ => ZIO.unit))), () => cancel) } - } override protected def compileWebSocketPipe( ws: WebSocket[Task], diff --git a/effects/zio/src/main/scalajvm/sttp/client4/httpclient/zio/HttpClientZioBackend.scala b/effects/zio/src/main/scalajvm/sttp/client4/httpclient/zio/HttpClientZioBackend.scala index 4fad0a56ec..60c65419c2 100644 --- a/effects/zio/src/main/scalajvm/sttp/client4/httpclient/zio/HttpClientZioBackend.scala +++ b/effects/zio/src/main/scalajvm/sttp/client4/httpclient/zio/HttpClientZioBackend.scala @@ -11,7 +11,7 @@ import sttp.client4.internal.httpclient.{BodyFromHttpClient, BodyToHttpClient, S import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.WebSocketStreamBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, WebSocketStreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketStreamBackend} import sttp.monad.MonadError import zio.Chunk.ByteArray import zio._ @@ -111,7 +111,7 @@ object HttpClientZioBackend { options: BackendOptions = BackendOptions.Default, customizeRequest: HttpRequest => HttpRequest = identity, customEncodingHandler: ZioEncodingHandler = PartialFunction.empty - ): Task[WebSocketStreamBackend[Task, ZioStreams]] = { + ): Task[WebSocketStreamBackend[Task, ZioStreams]] = ZIO.executor.flatMap(executor => ZIO.attempt( HttpClientZioBackend( @@ -122,7 +122,6 @@ object HttpClientZioBackend { ) ) ) - } def scoped( options: BackendOptions = BackendOptions.Default, @@ -146,7 +145,7 @@ object HttpClientZioBackend { options: BackendOptions = BackendOptions.Default, customizeRequest: HttpRequest => HttpRequest = identity, customEncodingHandler: ZioEncodingHandler = PartialFunction.empty - ): ZLayer[Any, Throwable, SttpClient] = { + ): ZLayer[Any, Throwable, SttpClient] = ZLayer.scoped( (for { backend <- HttpClientZioBackend( @@ -156,7 +155,6 @@ object HttpClientZioBackend { ) } yield backend).tap(client => ZIO.addFinalizer(client.close().ignore)) ) - } def usingClient( client: HttpClient, @@ -174,7 +172,7 @@ object HttpClientZioBackend { client: HttpClient, customizeRequest: HttpRequest => HttpRequest = identity, customEncodingHandler: ZioEncodingHandler = PartialFunction.empty - ): ZLayer[Any, Throwable, SttpClient] = { + ): ZLayer[Any, Throwable, SttpClient] = ZLayer.scoped( ZIO .acquireRelease( @@ -187,7 +185,6 @@ object HttpClientZioBackend { ) )(_.close().ignore) ) - } /** Create a stub backend for testing, which uses the [[Task]] response wrapper, and supports `Stream[Throwable, * ByteBuffer]` streaming. diff --git a/effects/zio/src/main/scalajvm/sttp/client4/httpclient/zio/ZioBodyFromHttpClient.scala b/effects/zio/src/main/scalajvm/sttp/client4/httpclient/zio/ZioBodyFromHttpClient.scala index b15dc02372..f56ffac366 100644 --- a/effects/zio/src/main/scalajvm/sttp/client4/httpclient/zio/ZioBodyFromHttpClient.scala +++ b/effects/zio/src/main/scalajvm/sttp/client4/httpclient/zio/ZioBodyFromHttpClient.scala @@ -34,12 +34,11 @@ private[zio] class ZioBodyFromHttpClient extends BodyFromHttpClient[Task, ZioStr override protected def withReplayableBody( response: ZStream[Any, Throwable, Byte], replayableBody: Either[Array[Byte], SttpFile] - ): Task[ZStream[Any, Throwable, Byte]] = { + ): Task[ZStream[Any, Throwable, Byte]] = replayableBody match { case Left(byteArray) => ZIO.succeed(ZStream.fromIterable(byteArray)) case Right(file) => ZIO.succeed(ZStream.fromPath(file.toPath)) } - } override protected def regularIgnore(response: ZStream[Any, Throwable, Byte]): Task[Unit] = response.run(ZSink.drain) @@ -52,10 +51,10 @@ private[zio] class ZioBodyFromHttpClient extends BodyFromHttpClient[Task, ZioStr response: ZStream[Any, Throwable, Byte], file: SttpFile ): Task[SttpFile] = response - .run({ + .run { val options = Set[OpenOption](StandardOpenOption.WRITE, StandardOpenOption.CREATE) ZSink.fromPath(file.toPath, options = options) - }) + } .as(file) override protected def regularAsStream( diff --git a/effects/zio1/src/main/scala/sttp/client4/impl/zio/ZioSimpleQueue.scala b/effects/zio1/src/main/scala/sttp/client4/impl/zio/ZioSimpleQueue.scala index 2d0b8435b9..d1824ea452 100644 --- a/effects/zio1/src/main/scala/sttp/client4/impl/zio/ZioSimpleQueue.scala +++ b/effects/zio1/src/main/scala/sttp/client4/impl/zio/ZioSimpleQueue.scala @@ -5,12 +5,10 @@ import sttp.ws.WebSocketBufferFull import zio.{Queue, RIO, Runtime} class ZioSimpleQueue[R, A](queue: Queue[A], runtime: Runtime[Any]) extends SimpleQueue[RIO[R, *], A] { - override def offer(t: A): Unit = { + override def offer(t: A): Unit = if (!runtime.unsafeRun(queue.offer(t))) { throw WebSocketBufferFull(queue.capacity) } - } - override def poll: RIO[R, A] = { + override def poll: RIO[R, A] = queue.take - } } diff --git a/effects/zio1/src/main/scalajs/sttp/client4/impl/zio/FetchZioBackend.scala b/effects/zio1/src/main/scalajs/sttp/client4/impl/zio/FetchZioBackend.scala index 03c8b31a13..36c275bdce 100644 --- a/effects/zio1/src/main/scalajs/sttp/client4/impl/zio/FetchZioBackend.scala +++ b/effects/zio1/src/main/scalajs/sttp/client4/impl/zio/FetchZioBackend.scala @@ -51,13 +51,13 @@ class FetchZioBackend private (fetchOptions: FetchOptions, customizeRequest: Fet override protected def handleResponseAsStream( response: FetchResponse - ): Task[(Observable[Byte], () => Task[Unit])] = { + ): Task[(Observable[Byte], () => Task[Unit])] = ZIO.effect { lazy val reader = response.body.getReader() def read() = convertFromFuture(reader.read().toFuture) - def go(): Observable[Byte] = { + def go(): Observable[Byte] = ZStream.fromEffect(read()).flatMap { chunk => if (chunk.done) ZStream.empty else { @@ -65,11 +65,9 @@ class FetchZioBackend private (fetchOptions: FetchOptions, customizeRequest: Fet ZStream.fromChunk(Chunk.fromArray(bytes)) ++ go() } } - } val cancel = ZIO.fromPromiseJS(reader.cancel("Response body reader cancelled")) (ZStream.fromIterableM(go().runCollect.onInterrupt(cancel.catchAll(_ => ZIO.unit))), () => cancel) } - } override protected def compileWebSocketPipe( ws: WebSocket[Task], diff --git a/effects/zio1/src/main/scalajvm/sttp/client4/httpclient/zio/HttpClientZioBackend.scala b/effects/zio1/src/main/scalajvm/sttp/client4/httpclient/zio/HttpClientZioBackend.scala index 12eb973929..f93f374315 100644 --- a/effects/zio1/src/main/scalajvm/sttp/client4/httpclient/zio/HttpClientZioBackend.scala +++ b/effects/zio1/src/main/scalajvm/sttp/client4/httpclient/zio/HttpClientZioBackend.scala @@ -11,7 +11,7 @@ import sttp.client4.internal.httpclient.{BodyFromHttpClient, BodyToHttpClient, S import sttp.client4.internal.ws.SimpleQueue import sttp.client4.testing.WebSocketStreamBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, WebSocketStreamBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, WebSocketStreamBackend} import sttp.monad.MonadError import zio.Chunk.ByteArray import zio._ @@ -109,10 +109,10 @@ object HttpClientZioBackend { ) def apply( - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: ZioEncodingHandler = PartialFunction.empty - ): Task[WebSocketStreamBackend[Task, ZioStreams]] = { + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: ZioEncodingHandler = PartialFunction.empty + ): Task[WebSocketStreamBackend[Task, ZioStreams]] = UIO.executor.flatMap(executor => Task.effect( HttpClientZioBackend( @@ -123,22 +123,21 @@ object HttpClientZioBackend { ) ) ) - } def managed( - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: ZioEncodingHandler = PartialFunction.empty + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: ZioEncodingHandler = PartialFunction.empty ): ZManaged[Any, Throwable, WebSocketStreamBackend[Task, ZioStreams]] = ZManaged.make(apply(options, customizeRequest, customEncodingHandler))( _.close().ignore ) def layer( - options: BackendOptions = BackendOptions.Default, - customizeRequest: HttpRequest => HttpRequest = identity, - customEncodingHandler: ZioEncodingHandler = PartialFunction.empty - ): ZLayer[Any, Throwable, SttpClient] = { + options: BackendOptions = BackendOptions.Default, + customizeRequest: HttpRequest => HttpRequest = identity, + customEncodingHandler: ZioEncodingHandler = PartialFunction.empty + ): ZLayer[Any, Throwable, SttpClient] = ZLayer.fromManaged( (for { backend <- HttpClientZioBackend( @@ -148,7 +147,6 @@ object HttpClientZioBackend { ) } yield backend).toManaged(_.close().ignore) ) - } def usingClient( client: HttpClient, @@ -166,7 +164,7 @@ object HttpClientZioBackend { client: HttpClient, customizeRequest: HttpRequest => HttpRequest = identity, customEncodingHandler: ZioEncodingHandler = PartialFunction.empty - ): ZLayer[Any, Throwable, SttpClient] = { + ): ZLayer[Any, Throwable, SttpClient] = ZLayer.fromManaged( ZManaged .makeEffect( @@ -177,7 +175,6 @@ object HttpClientZioBackend { ) )(_.close().ignore) ) - } /** Create a stub backend for testing, which uses the [[Task]] response wrapper, and supports `Stream[Throwable, * ByteBuffer]` streaming. diff --git a/effects/zio1/src/main/scalajvm/sttp/client4/httpclient/zio/ZioBodyFromHttpClient.scala b/effects/zio1/src/main/scalajvm/sttp/client4/httpclient/zio/ZioBodyFromHttpClient.scala index 31f669643e..2457ee1fb7 100644 --- a/effects/zio1/src/main/scalajvm/sttp/client4/httpclient/zio/ZioBodyFromHttpClient.scala +++ b/effects/zio1/src/main/scalajvm/sttp/client4/httpclient/zio/ZioBodyFromHttpClient.scala @@ -36,7 +36,7 @@ private[zio] class ZioBodyFromHttpClient extends BodyFromHttpClient[Task, ZioStr override protected def withReplayableBody( response: ZStream[Any, Throwable, Byte], replayableBody: Either[Array[Byte], SttpFile] - ): Task[ZStream[Any, Throwable, Byte]] = { + ): Task[ZStream[Any, Throwable, Byte]] = replayableBody match { case Left(byteArray) => ZIO.succeed(Stream.fromIterable(byteArray)) case Right(file) => @@ -49,7 +49,6 @@ private[zio] class ZioBodyFromHttpClient extends BodyFromHttpClient[Task, ZioStr } yield bytes ) } - } override protected def regularIgnore(response: ZStream[Any, Throwable, Byte]): Task[Unit] = response.run(ZSink.drain) @@ -62,7 +61,7 @@ private[zio] class ZioBodyFromHttpClient extends BodyFromHttpClient[Task, ZioStr response: ZStream[Any, Throwable, Byte], file: SttpFile ): Task[SttpFile] = response - .run({ + .run { ZSink.managed( AsynchronousFileChannel .open(Path.fromJava(file.toPath), StandardOpenOption.WRITE, StandardOpenOption.CREATE): Managed[ @@ -80,7 +79,7 @@ private[zio] class ZioBodyFromHttpClient extends BodyFromHttpClient[Task, ZioStr ) } } - }) + } .as(file) override protected def regularAsStream( diff --git a/examples-ce2/src/main/scala/sttp/client4/examples/PostSerializeJsonMonixHttpClientCirce.scala b/examples-ce2/src/main/scala/sttp/client4/examples/PostSerializeJsonMonixHttpClientCirce.scala index c263ca616d..e747d0f9af 100644 --- a/examples-ce2/src/main/scala/sttp/client4/examples/PostSerializeJsonMonixHttpClientCirce.scala +++ b/examples-ce2/src/main/scala/sttp/client4/examples/PostSerializeJsonMonixHttpClientCirce.scala @@ -15,7 +15,7 @@ object PostSerializeJsonMonixHttpClientCirce extends App { .post(uri"https://httpbin.org/post") r.send(backend) - .flatMap { response => Task(println(s"""Got ${response.code} response, body:\n${response.body}""")) } + .flatMap(response => Task(println(s"""Got ${response.code} response, body:\n${response.body}"""))) .guarantee(backend.close()) } diff --git a/examples-ce2/src/main/scala/sttp/client4/examples/WebSocketTesting.scala b/examples-ce2/src/main/scala/sttp/client4/examples/WebSocketTesting.scala index e655b37295..8a20c0e4d6 100644 --- a/examples-ce2/src/main/scala/sttp/client4/examples/WebSocketTesting.scala +++ b/examples-ce2/src/main/scala/sttp/client4/examples/WebSocketTesting.scala @@ -18,13 +18,12 @@ object WebSocketTesting extends App { } // the request description - def openWebSocket(backend: WebSocketBackend[Task]): Task[Unit] = { + def openWebSocket(backend: WebSocketBackend[Task]): Task[Unit] = basicRequest .get(uri"wss://echo.websocket.org") .response(asWebSocket(useWebSocket)) .send(backend) .void - } // the backend stub which we'll use instead of a "real" backend val stubBackend: WebSocketStreamBackendStub[Task, MonixStreams] = diff --git a/examples/src/main/scala/sttp/client4/examples/GetAndParseJsonZioCirce.scala b/examples/src/main/scala/sttp/client4/examples/GetAndParseJsonZioCirce.scala index 2bb72e526b..00f8f864d1 100644 --- a/examples/src/main/scala/sttp/client4/examples/GetAndParseJsonZioCirce.scala +++ b/examples/src/main/scala/sttp/client4/examples/GetAndParseJsonZioCirce.scala @@ -3,7 +3,7 @@ package sttp.client4.examples import io.circe.generic.auto._ import sttp.client4._ import sttp.client4.circe._ -import sttp.client4.httpclient.zio.{HttpClientZioBackend, send} +import sttp.client4.httpclient.zio.{send, HttpClientZioBackend} import zio._ object GetAndParseJsonZioCirce extends ZIOAppDefault { diff --git a/examples/src/main/scala/sttp/client4/examples/RetryZio.scala b/examples/src/main/scala/sttp/client4/examples/RetryZio.scala index 0c37a5a753..eaa495d18a 100644 --- a/examples/src/main/scala/sttp/client4/examples/RetryZio.scala +++ b/examples/src/main/scala/sttp/client4/examples/RetryZio.scala @@ -2,10 +2,10 @@ package sttp.client4.examples import sttp.client4._ import sttp.client4.httpclient.zio.HttpClientZioBackend -import zio.{Schedule, Task, ZIO, ZIOAppDefault, durationInt} +import zio.{durationInt, Schedule, Task, ZIO, ZIOAppDefault} object RetryZio extends ZIOAppDefault { - override def run: ZIO[Any, Throwable, Response[String]] = { + override def run: ZIO[Any, Throwable, Response[String]] = HttpClientZioBackend() .flatMap { backend => val localhostRequest = basicRequest @@ -24,5 +24,4 @@ object RetryZio extends ZIOAppDefault { sendWithRetries.ensuring(backend.close().ignore) } - } } diff --git a/examples/src/main/scala/sttp/client4/examples/StreamFs2.scala b/examples/src/main/scala/sttp/client4/examples/StreamFs2.scala index 15237e4ab8..2f536c72f9 100644 --- a/examples/src/main/scala/sttp/client4/examples/StreamFs2.scala +++ b/examples/src/main/scala/sttp/client4/examples/StreamFs2.scala @@ -4,7 +4,7 @@ import sttp.client4._ import sttp.client4.httpclient.fs2.HttpClientFs2Backend import cats.effect.IO import cats.instances.string._ -import fs2.{Stream, text} +import fs2.{text, Stream} import sttp.capabilities.fs2.Fs2Streams object StreamFs2 extends App { @@ -16,17 +16,16 @@ object StreamFs2 extends App { .post(uri"https://httpbin.org/post") .streamBody(Fs2Streams[IO])(stream) .send(backend) - .map { response => println(s"RECEIVED:\n${response.body}") } + .map(response => println(s"RECEIVED:\n${response.body}")) } - def streamResponseBody(backend: StreamBackend[IO, Fs2Streams[IO]]): IO[Unit] = { + def streamResponseBody(backend: StreamBackend[IO, Fs2Streams[IO]]): IO[Unit] = basicRequest .body("I want a stream!") .post(uri"https://httpbin.org/post") .response(asStreamAlways(Fs2Streams[IO])(_.chunks.through(text.utf8.decodeC).compile.foldMonoid)) .send(backend) - .map { response => println(s"RECEIVED:\n${response.body}") } - } + .map(response => println(s"RECEIVED:\n${response.body}")) val effect = HttpClientFs2Backend.resource[IO]().use { backend => streamRequestBody(backend).flatMap(_ => streamResponseBody(backend)) diff --git a/examples/src/main/scala/sttp/client4/examples/StreamZio.scala b/examples/src/main/scala/sttp/client4/examples/StreamZio.scala index dd69aacd7f..4645c88af8 100644 --- a/examples/src/main/scala/sttp/client4/examples/StreamZio.scala +++ b/examples/src/main/scala/sttp/client4/examples/StreamZio.scala @@ -5,7 +5,7 @@ import sttp.client4._ import zio.Console._ import zio._ import zio.stream._ -import sttp.client4.httpclient.zio.{HttpClientZioBackend, SttpClient, send} +import sttp.client4.httpclient.zio.{send, HttpClientZioBackend, SttpClient} object StreamZio extends ZIOAppDefault { def streamRequestBody: RIO[SttpClient, Unit] = { @@ -14,7 +14,7 @@ object StreamZio extends ZIOAppDefault { basicRequest .post(uri"https://httpbin.org/post") .streamBody(ZioStreams)(stream) - ).flatMap { response => printLine(s"RECEIVED:\n${response.body}") } + ).flatMap(response => printLine(s"RECEIVED:\n${response.body}")) } def streamResponseBody: RIO[SttpClient, Unit] = @@ -23,7 +23,7 @@ object StreamZio extends ZIOAppDefault { .post(uri"https://httpbin.org/post") .body("I want a stream!") .response(asStreamAlways(ZioStreams)(_.via(ZPipeline.utf8Decode).runFold("")(_ + _))) - ).flatMap { response => printLine(s"RECEIVED:\n${response.body}") } + ).flatMap(response => printLine(s"RECEIVED:\n${response.body}")) override def run = (streamRequestBody *> streamResponseBody).provide(HttpClientZioBackend.layer()) diff --git a/examples/src/main/scala/sttp/client4/examples/WebSocketZio.scala b/examples/src/main/scala/sttp/client4/examples/WebSocketZio.scala index 0fe40fb3b2..322b6a5433 100644 --- a/examples/src/main/scala/sttp/client4/examples/WebSocketZio.scala +++ b/examples/src/main/scala/sttp/client4/examples/WebSocketZio.scala @@ -16,8 +16,7 @@ object WebSocketZio extends ZIOAppDefault { def sendAndPrint(backend: WebSocketBackend[Task]): Task[Response[Unit]] = basicRequest.get(uri"wss://ws.postman-echo.com/raw").response(asWebSocketAlways(useWebSocket)).send(backend) - override def run = { + override def run = // provide an implementation for the SttpClient dependency HttpClientZioBackend.scoped().flatMap(sendAndPrint) - } } diff --git a/finagle-backend/src/main/scala/sttp/client4/finagle/FinagleBackend.scala b/finagle-backend/src/main/scala/sttp/client4/finagle/FinagleBackend.scala index 6292b6cf8f..86325267c2 100644 --- a/finagle-backend/src/main/scala/sttp/client4/finagle/FinagleBackend.scala +++ b/finagle-backend/src/main/scala/sttp/client4/finagle/FinagleBackend.scala @@ -1,8 +1,15 @@ package sttp.client4.finagle import com.twitter.finagle.Http.Client -import com.twitter.finagle.http.{FileElement, FormElement, RequestBuilder, SimpleElement, Method => FMethod, Response => FResponse} -import com.twitter.finagle.{Http, Service, http} +import com.twitter.finagle.http.{ + FileElement, + FormElement, + Method => FMethod, + RequestBuilder, + Response => FResponse, + SimpleElement +} +import com.twitter.finagle.{http, Http, Service} import com.twitter.io.Buf import com.twitter.io.Buf.{ByteArray, ByteBuffer} import com.twitter.util @@ -47,9 +54,8 @@ class FinagleBackend(client: Option[Client] = None) extends Backend[TFuture] { override implicit val monad: MonadError[TFuture] = TFutureMonadError - private def headersToMap(headers: Seq[Header]): Map[String, String] = { + private def headersToMap(headers: Seq[Header]): Map[String, String] = headers.map(header => header.name -> header.value).toMap - } private def methodToFinagle(m: Method): FMethod = m match { @@ -90,7 +96,7 @@ class FinagleBackend(client: Option[Client] = None) extends Backend[TFuture] { ) case m: MultipartBody[_] => val requestBuilder = RequestBuilder.create().url(url).addHeaders(headers) - val elements = m.parts.map { part => getBasicBodyContent(part) } + val elements = m.parts.map(part => getBasicBodyContent(part)) requestBuilder.add(elements).buildFormPost(true) // requestBuilder.addFormElement(elements: _*).buildFormPost(true) case _ => buildRequest(url, headers, finagleMethod, None, r.httpVersion) @@ -144,12 +150,13 @@ class FinagleBackend(client: Option[Client] = None) extends Backend[TFuture] { override protected def withReplayableBody( response: FResponse, replayableBody: Either[Array[Byte], SttpFile] - ): TFuture[FResponse] = { - response.content(replayableBody match { - case Left(byteArray) => Buf.ByteArray(byteArray: _*) - case Right(file) => Buf.ByteArray(FileHelpers.readFile(file.toFile): _*) - }) - }.unit + ): TFuture[FResponse] = + response + .content(replayableBody match { + case Left(byteArray) => Buf.ByteArray(byteArray: _*) + case Right(file) => Buf.ByteArray(FileHelpers.readFile(file.toFile): _*) + }) + .unit override protected def regularIgnore(response: FResponse): TFuture[Unit] = TFuture(response.clearContent()) @@ -161,17 +168,16 @@ class FinagleBackend(client: Option[Client] = None) extends Backend[TFuture] { b }) - override protected def regularAsFile(response: FResponse, file: SttpFile): TFuture[SttpFile] = { + override protected def regularAsFile(response: FResponse, file: SttpFile): TFuture[SttpFile] = TFuture.const(util.Try(FileHelpers.saveFile(file.toFile, response.getInputStream()))).map(_ => file) - } override protected def regularAsStream(response: FResponse): TFuture[Nothing] = TFuture.exception(new IllegalStateException("Streaming isn't supported")) override protected def handleWS[T]( - responseAs: GenericWebSocketResponseAs[T, _], - meta: ResponseMetadata, - ws: Nothing + responseAs: GenericWebSocketResponseAs[T, _], + meta: ResponseMetadata, + ws: Nothing ): TFuture[T] = ws override protected def cleanupWhenNotAWebSocket(response: FResponse, e: NotAWebSocketException): TFuture[Unit] = diff --git a/http4s-backend/src/main/scala/sttp/client4/http4s/Http4sBackend.scala b/http4s-backend/src/main/scala/sttp/client4/http4s/Http4sBackend.scala index 9bb2912388..041057a1ca 100644 --- a/http4s-backend/src/main/scala/sttp/client4/http4s/Http4sBackend.scala +++ b/http4s-backend/src/main/scala/sttp/client4/http4s/Http4sBackend.scala @@ -8,7 +8,7 @@ import cats.implicits._ import cats.effect.implicits._ import fs2.io.file.Files import fs2.{Chunk, Stream} -import org.http4s.{ContentCoding, EntityBody, Status, Request => Http4sRequest} +import org.http4s.{ContentCoding, EntityBody, Request => Http4sRequest, Status} import org.http4s import org.http4s.blaze.client.BlazeClientBuilder import org.http4s.client.Client @@ -18,7 +18,7 @@ import sttp.capabilities.fs2.Fs2Streams import sttp.client4.http4s.Http4sBackend.EncodingHandler import sttp.client4.httpclient.fs2.Fs2Compression import sttp.client4.impl.cats.CatsMonadAsyncError -import sttp.client4.internal.{BodyFromResponseAs, IOBufferSize, SttpFile, throwNestedMultipartNotAllowed} +import sttp.client4.internal.{throwNestedMultipartNotAllowed, BodyFromResponseAs, IOBufferSize, SttpFile} import sttp.model._ import sttp.monad.MonadError import sttp.client4.testing.StreamBackendStub @@ -81,7 +81,7 @@ class Http4sBackend[F[_]: Async]( ) body - .map { b => Response(b, code, statusText, headers, Nil, r.onlyMetadata) } + .map(b => Response(b, code, statusText, headers, Nil, r.onlyMetadata)) .flatMap(r => responseVar.complete(Right(r))) .flatMap(_ => responseBodyCompleteVar.get) } @@ -109,18 +109,17 @@ class Http4sBackend[F[_]: Async]( case _ => http4s.Method.fromString(m.method).right.get } - private def versionToHttp4s(version: HttpVersion): http4s.HttpVersion = { + private def versionToHttp4s(version: HttpVersion): http4s.HttpVersion = version match { case HttpVersion.HTTP_1 => http4s.HttpVersion.`HTTP/1.0` case HttpVersion.HTTP_1_1 => http4s.HttpVersion.`HTTP/1.1` case HttpVersion.HTTP_2 => http4s.HttpVersion.`HTTP/2` case HttpVersion.HTTP_3 => http4s.HttpVersion.`HTTP/3` } - } private def charsetToHttp4s(encoding: String) = http4s.Charset.fromNioCharset(Charset.forName(encoding)) - private def basicBodyToHttp4s(body: BasicBodyPart): http4s.Entity[F] = { + private def basicBodyToHttp4s(body: BasicBodyPart): http4s.Entity[F] = body match { case StringBody(b, encoding, _) => http4s.EntityEncoder.stringEncoder(charsetToHttp4s(encoding)).toEntity(b) @@ -137,9 +136,11 @@ class Http4sBackend[F[_]: Async]( case FileBody(b, _) => http4s.EntityEncoder.fileEncoder.toEntity(b.toFile) } - } - private def bodyToHttp4s[R](r: GenericRequest[_, R], body: GenericRequestBody[R]): (http4s.Entity[F], http4s.Headers) = { + private def bodyToHttp4s[R]( + r: GenericRequest[_, R], + body: GenericRequestBody[R] + ): (http4s.Entity[F], http4s.Headers) = body match { case NoBody => (http4s.Entity(http4s.EmptyBody: http4s.EntityBody[F]), http4s.Headers.empty) @@ -156,7 +157,6 @@ class Http4sBackend[F[_]: Async]( val multipart = http4s.multipart.Multipart(parts) (http4s.EntityEncoder.multipartEncoder.toEntity(multipart), multipart.headers) } - } private def multipartToHttp4s(mp: Part[BodyPart[_]]): http4s.multipart.Part[F] = { val contentDisposition = @@ -172,17 +172,15 @@ class Http4sBackend[F[_]: Async]( http4s.multipart.Part(http4s.Headers(allHeaders), body) } - private def onFinalizeSignal(hr: http4s.Response[F], signal: F[Unit]): http4s.Response[F] = { + private def onFinalizeSignal(hr: http4s.Response[F], signal: F[Unit]): http4s.Response[F] = hr.copy(body = hr.body.onFinalize(signal)) - } private def decompressResponseBodyIfNotHead[T]( m: Method, hr: http4s.Response[F], disableAutoDecompression: Boolean - ): http4s.Response[F] = { + ): http4s.Response[F] = if (m == Method.HEAD || disableAutoDecompression) hr else decompressResponseBody(hr) - } private def decompressResponseBody(hr: http4s.Response[F]): http4s.Response[F] = { val body = hr.headers diff --git a/http4s-ce2-backend/src/main/scala/sttp/client4/http4s/Http4sBackend.scala b/http4s-ce2-backend/src/main/scala/sttp/client4/http4s/Http4sBackend.scala index 1bbf0b27d9..81cfd1109b 100644 --- a/http4s-ce2-backend/src/main/scala/sttp/client4/http4s/Http4sBackend.scala +++ b/http4s-ce2-backend/src/main/scala/sttp/client4/http4s/Http4sBackend.scala @@ -8,7 +8,7 @@ import cats.effect.{Blocker, Concurrent, ConcurrentEffect, ContextShift, Resourc import cats.implicits._ import cats.effect.implicits._ import fs2.{Chunk, Stream} -import org.http4s.{ContentCoding, EntityBody, Status, Request => Http4sRequest} +import org.http4s.{ContentCoding, EntityBody, Request => Http4sRequest, Status} import org.http4s import org.http4s.blaze.client.BlazeClientBuilder import org.http4s.client.Client @@ -18,7 +18,7 @@ import sttp.capabilities.fs2.Fs2Streams import sttp.client4.http4s.Http4sBackend.EncodingHandler import sttp.client4.impl.cats.CatsMonadAsyncError import sttp.client4.impl.fs2.Fs2Compression -import sttp.client4.internal.{BodyFromResponseAs, IOBufferSize, SttpFile, throwNestedMultipartNotAllowed} +import sttp.client4.internal.{throwNestedMultipartNotAllowed, BodyFromResponseAs, IOBufferSize, SttpFile} import sttp.model._ import sttp.monad.MonadError import sttp.client4.testing.StreamBackendStub @@ -83,7 +83,7 @@ class Http4sBackend[F[_]: ConcurrentEffect: ContextShift]( ) body - .map { b => Response(b, code, statusText, headers, Nil, r.onlyMetadata) } + .map(b => Response(b, code, statusText, headers, Nil, r.onlyMetadata)) .flatMap(r => responseVar.put(Right(r))) .flatMap(_ => responseBodyCompleteVar.take) } @@ -111,17 +111,16 @@ class Http4sBackend[F[_]: ConcurrentEffect: ContextShift]( case _ => http4s.Method.fromString(m.method).right.get } - private def versionToHttp4s(version: HttpVersion): http4s.HttpVersion = { + private def versionToHttp4s(version: HttpVersion): http4s.HttpVersion = version match { case HttpVersion.HTTP_1 => http4s.HttpVersion.`HTTP/1.0` case HttpVersion.HTTP_1_1 => http4s.HttpVersion.`HTTP/1.1` case HttpVersion.HTTP_2 => http4s.HttpVersion.`HTTP/2` case HttpVersion.HTTP_3 => http4s.HttpVersion.`HTTP/3` } - } private def charsetToHttp4s(encoding: String) = http4s.Charset.fromNioCharset(Charset.forName(encoding)) - private def basicBodyToHttp4s(body: BasicBodyPart): http4s.Entity[F] = { + private def basicBodyToHttp4s(body: BasicBodyPart): http4s.Entity[F] = body match { case StringBody(b, encoding, _) => http4s.EntityEncoder.stringEncoder(charsetToHttp4s(encoding)).toEntity(b) @@ -138,9 +137,8 @@ class Http4sBackend[F[_]: ConcurrentEffect: ContextShift]( case FileBody(b, _) => http4s.EntityEncoder.fileEncoder(blocker).toEntity(b.toFile) } - } - private def bodyToHttp4s(r: GenericRequest[_, R], body: GenericRequestBody[R]): (http4s.Entity[F], http4s.Headers) = { + private def bodyToHttp4s(r: GenericRequest[_, R], body: GenericRequestBody[R]): (http4s.Entity[F], http4s.Headers) = body match { case NoBody => (http4s.Entity(http4s.EmptyBody: http4s.EntityBody[F]), http4s.Headers.empty) @@ -157,7 +155,6 @@ class Http4sBackend[F[_]: ConcurrentEffect: ContextShift]( val multipart = http4s.multipart.Multipart(parts) (http4s.EntityEncoder.multipartEncoder.toEntity(multipart), multipart.headers) } - } private def multipartToHttp4s(mp: Part[BodyPart[_]]): http4s.multipart.Part[F] = { val contentDisposition = @@ -173,17 +170,15 @@ class Http4sBackend[F[_]: ConcurrentEffect: ContextShift]( http4s.multipart.Part(http4s.Headers(allHeaders), body) } - private def onFinalizeSignal(hr: http4s.Response[F], signal: F[Unit]): http4s.Response[F] = { + private def onFinalizeSignal(hr: http4s.Response[F], signal: F[Unit]): http4s.Response[F] = hr.copy(body = hr.body.onFinalize(signal)) - } private def decompressResponseBodyIfNotHead[T]( m: Method, hr: http4s.Response[F], disableAutoDecompression: Boolean - ): http4s.Response[F] = { + ): http4s.Response[F] = if (m == Method.HEAD || disableAutoDecompression) hr else decompressResponseBody(hr) - } private def decompressResponseBody(hr: http4s.Response[F]): http4s.Response[F] = { val body = hr.headers @@ -294,9 +289,8 @@ object Http4sBackend { blocker: Blocker, customizeRequest: Http4sRequest[F] => Http4sRequest[F] = identity[Http4sRequest[F]] _, customEncodingHandler: EncodingHandler[F] = PartialFunction.empty - ): Resource[F, StreamBackend[F, Fs2Streams[F]]] = { + ): Resource[F, StreamBackend[F, Fs2Streams[F]]] = blazeClientBuilder.resource.map(c => usingClient(c, blocker, customizeRequest, customEncodingHandler)) - } def usingDefaultBlazeClientBuilder[F[_]: ConcurrentEffect: ContextShift]( blocker: Blocker, diff --git a/json/circe/src/main/scala/sttp/client4/circe/SttpCirceApi.scala b/json/circe/src/main/scala/sttp/client4/circe/SttpCirceApi.scala index 05b1234b86..ca2cdae638 100644 --- a/json/circe/src/main/scala/sttp/client4/circe/SttpCirceApi.scala +++ b/json/circe/src/main/scala/sttp/client4/circe/SttpCirceApi.scala @@ -37,12 +37,11 @@ trait SttpCirceApi { * - `Left(DeserializationException)` if there's an error during deserialization */ def asJsonEither[E: Decoder: IsOption, B: Decoder: IsOption] - : ResponseAs[Either[ResponseException[E, io.circe.Error], B]] = { + : ResponseAs[Either[ResponseException[E, io.circe.Error], B]] = asJson[B].mapLeft { case HttpError(e, code) => deserializeJson[E].apply(e).fold(DeserializationException(e, _), HttpError(_, code)) case de @ DeserializationException(_, _) => de }.showAsJsonEither - } def deserializeJson[B: Decoder: IsOption]: String => Either[io.circe.Error, B] = JsonInput.sanitize[B].andThen(decode[B]) diff --git a/json/json4s/src/main/scala/sttp/client4/json4s/SttpJson4sApi.scala b/json/json4s/src/main/scala/sttp/client4/json4s/SttpJson4sApi.scala index e88b625cbd..4c289cd792 100644 --- a/json/json4s/src/main/scala/sttp/client4/json4s/SttpJson4sApi.scala +++ b/json/json4s/src/main/scala/sttp/client4/json4s/SttpJson4sApi.scala @@ -43,13 +43,12 @@ trait SttpJson4sApi { def asJsonEither[E: Manifest, B: Manifest](implicit formats: Formats, serialization: Serialization - ): ResponseAs[Either[ResponseException[E, Exception], B]] = { + ): ResponseAs[Either[ResponseException[E, Exception], B]] = asJson[B].mapLeft { case HttpError(e, code) => ResponseAs.deserializeCatchingExceptions(deserializeJson[E])(e).fold(identity, HttpError(_, code)) case de @ DeserializationException(_, _) => de }.showAsJsonEither - } def deserializeJson[B: Manifest](implicit formats: Formats, diff --git a/json/jsoniter/src/main/scala/sttp/client4/jsoniter/SttpJsoniterJsonApi.scala b/json/jsoniter/src/main/scala/sttp/client4/jsoniter/SttpJsoniterJsonApi.scala index b41583998a..6d3db0206b 100644 --- a/json/jsoniter/src/main/scala/sttp/client4/jsoniter/SttpJsoniterJsonApi.scala +++ b/json/jsoniter/src/main/scala/sttp/client4/jsoniter/SttpJsoniterJsonApi.scala @@ -3,6 +3,8 @@ package sttp.client4.jsoniter import sttp.client4.internal.Utf8 import sttp.client4.json.RichResponseAs import sttp.client4.{ + asString, + asStringAlways, BodySerializer, DeserializationException, HttpError, @@ -11,9 +13,7 @@ import sttp.client4.{ ResponseAs, ResponseException, ShowError, - StringBody, - asString, - asStringAlways + StringBody } import sttp.model.MediaType @@ -47,12 +47,11 @@ trait SttpJsoniterJsonApi { def asJsonEither[ E: JsonValueCodec: IsOption, B: JsonValueCodec: IsOption - ]: ResponseAs[Either[ResponseException[E, Exception], B]] = { + ]: ResponseAs[Either[ResponseException[E, Exception], B]] = asJson[B].mapLeft { case de @ DeserializationException(_, _) => de case HttpError(e, code) => deserializeJson[E].apply(e).fold(DeserializationException(e, _), HttpError(_, code)) }.showAsJsonEither - } def deserializeJson[B: JsonValueCodec: IsOption]: String => Either[Exception, B] = { (s: String) => try Right(readFromString[B](JsonInput.sanitize[B].apply(s))) @@ -63,7 +62,7 @@ trait SttpJsoniterJsonApi { implicit def optionDecoder[T: JsonValueCodec]: JsonValueCodec[Option[T]] = new JsonValueCodec[Option[T]] { private val codec = implicitly[JsonValueCodec[T]] - override def decodeValue(in: JsonReader, default: Option[T]): Option[T] = { + override def decodeValue(in: JsonReader, default: Option[T]): Option[T] = if ( in.isNextToken('n'.toByte) && in.isNextToken('u'.toByte) @@ -75,7 +74,6 @@ trait SttpJsoniterJsonApi { in.rollbackToken() Some(codec.decodeValue(in, codec.nullValue)) } - } override def encodeValue(x: Option[T], out: JsonWriter): Unit = x.foreach(codec.encodeValue(_, out)) diff --git a/json/play-json/src/main/scala/sttp/client4/playJson/SttpPlayJsonApi.scala b/json/play-json/src/main/scala/sttp/client4/playJson/SttpPlayJsonApi.scala index 44da2723c9..daf10f665e 100644 --- a/json/play-json/src/main/scala/sttp/client4/playJson/SttpPlayJsonApi.scala +++ b/json/play-json/src/main/scala/sttp/client4/playJson/SttpPlayJsonApi.scala @@ -37,13 +37,12 @@ trait SttpPlayJsonApi { * - `Left(HttpError(E))` if the response was other than 2xx and parsing was successful * - `Left(DeserializationException)` if there's an error during deserialization */ - def asJsonEither[E: Reads: IsOption, B: Reads: IsOption]: ResponseAs[Either[ResponseException[E, JsError], B]] = { + def asJsonEither[E: Reads: IsOption, B: Reads: IsOption]: ResponseAs[Either[ResponseException[E, JsError], B]] = asJson[B].mapLeft { case HttpError(e, code) => deserializeJson[E].apply(e).fold(DeserializationException(e, _), HttpError(_, code)) case de @ DeserializationException(_, _) => de }.showAsJsonEither - } // Note: None of the play-json utilities attempt to catch invalid // json, so Json.parse needs to be wrapped in Try diff --git a/json/spray-json/src/main/scala/sttp/client4/sprayJson/SttpSprayJsonApi.scala b/json/spray-json/src/main/scala/sttp/client4/sprayJson/SttpSprayJsonApi.scala index d8ce955960..249fad6742 100644 --- a/json/spray-json/src/main/scala/sttp/client4/sprayJson/SttpSprayJsonApi.scala +++ b/json/spray-json/src/main/scala/sttp/client4/sprayJson/SttpSprayJsonApi.scala @@ -32,13 +32,12 @@ trait SttpSprayJsonApi { * - `Left(DeserializationException)` if there's an error during deserialization */ def asJsonEither[E: JsonReader: IsOption, B: JsonReader: IsOption] - : ResponseAs[Either[ResponseException[E, Exception], B]] = { + : ResponseAs[Either[ResponseException[E, Exception], B]] = asJson[B].mapLeft { case HttpError(e, code) => ResponseAs.deserializeCatchingExceptions(deserializeJson[E])(e).fold(identity, HttpError(_, code)) case de @ DeserializationException(_, _) => de }.showAsJsonEither - } def deserializeJson[B: JsonReader: IsOption]: String => B = JsonInput.sanitize[B].andThen((_: String).parseJson.convertTo[B]) diff --git a/json/upickle/src/main/scala/sttp/client4/upicklejson/SttpUpickleApi.scala b/json/upickle/src/main/scala/sttp/client4/upicklejson/SttpUpickleApi.scala index 3979e3c6eb..a7b1a8ce00 100644 --- a/json/upickle/src/main/scala/sttp/client4/upicklejson/SttpUpickleApi.scala +++ b/json/upickle/src/main/scala/sttp/client4/upicklejson/SttpUpickleApi.scala @@ -1,6 +1,6 @@ package sttp.client4.upicklejson -import upickle.default.{Reader, Writer, read, write} +import upickle.default.{read, write, Reader, Writer} import sttp.client4._ import sttp.client4.internal.Utf8 import sttp.model.MediaType @@ -32,17 +32,16 @@ trait SttpUpickleApi { * - `Left(HttpError(E))` if the response was other than 2xx and parsing was successful * - `Left(DeserializationException)` if there's an error during deserialization */ - def asJsonEither[E: Reader: IsOption, B: Reader: IsOption]: ResponseAs[Either[ResponseException[E, Exception], B]] = { + def asJsonEither[E: Reader: IsOption, B: Reader: IsOption]: ResponseAs[Either[ResponseException[E, Exception], B]] = asJson[B].mapLeft { case HttpError(e, code) => deserializeJson[E].apply(e).fold(DeserializationException(e, _), HttpError(_, code)) case de @ DeserializationException(_, _) => de }.showAsJsonEither - } def deserializeJson[B: Reader: IsOption]: String => Either[Exception, B] = { (s: String) => - try { + try Right(read[B](JsonInput.sanitize[B].apply(s))) - } catch { + catch { case e: Exception => Left(e) case t: Throwable => // in ScalaJS, ArrayIndexOutOfBoundsException exceptions are wrapped in org.scalajs.linker.runtime.UndefinedBehaviorError diff --git a/json/zio-json/src/main/scala/sttp/client4/ziojson/SttpZioJsonApi.scala b/json/zio-json/src/main/scala/sttp/client4/ziojson/SttpZioJsonApi.scala index ec4ee383bb..c27c023c61 100644 --- a/json/zio-json/src/main/scala/sttp/client4/ziojson/SttpZioJsonApi.scala +++ b/json/zio-json/src/main/scala/sttp/client4/ziojson/SttpZioJsonApi.scala @@ -3,6 +3,8 @@ package sttp.client4.ziojson import sttp.client4.internal.Utf8 import sttp.client4.json.RichResponseAs import sttp.client4.{ + asString, + asStringAlways, BodySerializer, DeserializationException, HttpError, @@ -11,9 +13,7 @@ import sttp.client4.{ ResponseAs, ResponseException, ShowError, - StringBody, - asString, - asStringAlways + StringBody } import sttp.model.MediaType diff --git a/json/zio-json/src/main/scalajvm/sttp/client4/ziojson/SttpZioJsonApiExtensions.scala b/json/zio-json/src/main/scalajvm/sttp/client4/ziojson/SttpZioJsonApiExtensions.scala index 71788e6198..d4ac15ba72 100644 --- a/json/zio-json/src/main/scalajvm/sttp/client4/ziojson/SttpZioJsonApiExtensions.scala +++ b/json/zio-json/src/main/scalajvm/sttp/client4/ziojson/SttpZioJsonApiExtensions.scala @@ -2,7 +2,7 @@ package sttp.client4.ziojson import sttp.capabilities.Effect import sttp.capabilities.zio.ZioStreams -import sttp.client4.{DeserializationException, HttpError, IsOption, ResponseException, StreamResponseAs, asStream} +import sttp.client4.{asStream, DeserializationException, HttpError, IsOption, ResponseException, StreamResponseAs} import zio.json.JsonDecoder import zio.stream.ZPipeline import zio.{Task, ZIO} diff --git a/json/zio1-json/src/main/scala/sttp/client4/ziojson/SttpZioJsonApi.scala b/json/zio1-json/src/main/scala/sttp/client4/ziojson/SttpZioJsonApi.scala index ec4ee383bb..c27c023c61 100644 --- a/json/zio1-json/src/main/scala/sttp/client4/ziojson/SttpZioJsonApi.scala +++ b/json/zio1-json/src/main/scala/sttp/client4/ziojson/SttpZioJsonApi.scala @@ -3,6 +3,8 @@ package sttp.client4.ziojson import sttp.client4.internal.Utf8 import sttp.client4.json.RichResponseAs import sttp.client4.{ + asString, + asStringAlways, BodySerializer, DeserializationException, HttpError, @@ -11,9 +13,7 @@ import sttp.client4.{ ResponseAs, ResponseException, ShowError, - StringBody, - asString, - asStringAlways + StringBody } import sttp.model.MediaType diff --git a/json/zio1-json/src/main/scalajvm/sttp/client4/ziojson/SttpZioJsonApiExtensions.scala b/json/zio1-json/src/main/scalajvm/sttp/client4/ziojson/SttpZioJsonApiExtensions.scala index 182898b1fd..b21f8e2050 100644 --- a/json/zio1-json/src/main/scalajvm/sttp/client4/ziojson/SttpZioJsonApiExtensions.scala +++ b/json/zio1-json/src/main/scalajvm/sttp/client4/ziojson/SttpZioJsonApiExtensions.scala @@ -2,7 +2,7 @@ package sttp.client4.ziojson import sttp.capabilities.Effect import sttp.capabilities.zio.ZioStreams -import sttp.client4.{DeserializationException, HttpError, IsOption, ResponseException, StreamResponseAs, asStream} +import sttp.client4.{asStream, DeserializationException, HttpError, IsOption, ResponseException, StreamResponseAs} import zio.blocking.Blocking import zio.json.JsonDecoder import zio.stream.ZTransducer diff --git a/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackend.scala b/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackend.scala index 41753c350f..8cb1b9814f 100644 --- a/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackend.scala +++ b/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsBackend.scala @@ -86,15 +86,15 @@ private object OpenTelemetryMetricsListener { } private class OpenTelemetryMetricsListener( - meter: Meter, - clock: Clock, - requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig], - requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig], - responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig], - requestToErrorCounterMapper: Response[_] => Option[CollectorConfig], - requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig], - requestToSizeHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig], - responseToSizeHistogramMapper: Response[_] => Option[CollectorConfig] + meter: Meter, + clock: Clock, + requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig], + requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig], + responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig], + requestToErrorCounterMapper: Response[_] => Option[CollectorConfig], + requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig], + requestToSizeHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig], + responseToSizeHistogramMapper: Response[_] => Option[CollectorConfig] ) extends RequestListener[Identity, Option[Long]] { private val counters = new ConcurrentHashMap[String, LongCounter] @@ -118,7 +118,7 @@ private class OpenTelemetryMetricsListener( updateInProgressCounter(request, -1) } - override def requestException(request: GenericRequest[_, _], tag: Option[Long], e: Exception): Unit = { + override def requestException(request: GenericRequest[_, _], tag: Option[Long], e: Exception): Unit = HttpError.find(e) match { case Some(HttpError(body, statusCode)) => requestSuccessful(request, Response(body, statusCode).copy(request = request.onlyMetadata), tag) @@ -127,23 +127,20 @@ private class OpenTelemetryMetricsListener( recordHistogram(requestToLatencyHistogramMapper(request), tag.map(clock.millis() - _)) updateInProgressCounter(request, -1) } - } - private def updateInProgressCounter[R, T](request: GenericRequest[T, R], delta: Long): Unit = { + private def updateInProgressCounter[R, T](request: GenericRequest[T, R], delta: Long): Unit = requestToInProgressCounterMapper(request) .foreach(config => getOrCreateMetric(upAndDownCounter, config, createNewUpDownCounter).add(delta, config.attributes) ) - } private def recordHistogram(config: Option[CollectorConfig], size: Option[Long]): Unit = config.foreach { cfg => getOrCreateMetric(histograms, cfg, createNewHistogram).record(size.getOrElse(0L).toDouble, cfg.attributes) } - private def incrementCounter(collectorConfig: Option[CollectorConfig]): Unit = { + private def incrementCounter(collectorConfig: Option[CollectorConfig]): Unit = collectorConfig .foreach(config => getOrCreateMetric(counters, config, createNewCounter).add(1, config.attributes)) - } private def getOrCreateMetric[T]( cache: ConcurrentHashMap[String, T], diff --git a/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsConfig.scala b/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsConfig.scala index da0d79e42d..8887ede27f 100644 --- a/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsConfig.scala +++ b/observability/opentelemetry-metrics-backend/src/main/scala/sttp/client4/opentelemetry/OpenTelemetryMetricsConfig.scala @@ -8,35 +8,35 @@ import sttp.client4.opentelemetry.OpenTelemetryMetricsBackend._ import java.time.Clock final case class OpenTelemetryMetricsConfig( - meter: Meter, - clock: Clock, - requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig], - requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig], - responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig], - requestToErrorCounterMapper: Response[_] => Option[CollectorConfig], - requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig], - requestToSizeHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig], - responseToSizeHistogramMapper: Response[_] => Option[CollectorConfig] + meter: Meter, + clock: Clock, + requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig], + requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig], + responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig], + requestToErrorCounterMapper: Response[_] => Option[CollectorConfig], + requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig], + requestToSizeHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig], + responseToSizeHistogramMapper: Response[_] => Option[CollectorConfig] ) object OpenTelemetryMetricsConfig { def apply( - openTelemetry: OpenTelemetry, - meterConfig: MeterConfig = MeterConfig.Default, - clock: Clock = Clock.systemUTC(), - requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => + openTelemetry: OpenTelemetry, + meterConfig: MeterConfig = MeterConfig.Default, + clock: Clock = Clock.systemUTC(), + requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => Some(CollectorConfig(DefaultLatencyHistogramName, unit = Some(CollectorConfig.Milliseconds))), - requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => + requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => Some(CollectorConfig(DefaultRequestsInProgressCounterName)), - responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => + responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => Some(CollectorConfig(DefaultSuccessCounterName)), - responseToErrorCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => + responseToErrorCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => Some(CollectorConfig(DefaultErrorCounterName)), - requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig] = + requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig] = (_: GenericRequest[_, _], _: Throwable) => Some(CollectorConfig(DefaultFailureCounterName)), - requestToSizeHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => + requestToSizeHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => Some(CollectorConfig(DefaultRequestSizeHistogramName, unit = Some(CollectorConfig.Bytes))), - responseToSizeHistogramMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => + responseToSizeHistogramMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => Some(CollectorConfig(DefaultResponseSizeHistogramName, unit = Some(CollectorConfig.Bytes))) ): OpenTelemetryMetricsConfig = usingMeter( openTelemetry.meterBuilder(meterConfig.name).setInstrumentationVersion(meterConfig.version).build(), @@ -51,21 +51,21 @@ object OpenTelemetryMetricsConfig { ) def usingMeter( - meter: Meter, - clock: Clock = Clock.systemUTC(), - requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => + meter: Meter, + clock: Clock = Clock.systemUTC(), + requestToLatencyHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => Some(CollectorConfig(DefaultLatencyHistogramName, unit = Some(CollectorConfig.Milliseconds))), - requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => + requestToInProgressCounterMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => Some(CollectorConfig(DefaultRequestsInProgressCounterName)), - responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => + responseToSuccessCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => Some(CollectorConfig(DefaultSuccessCounterName)), - responseToErrorCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => + responseToErrorCounterMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => Some(CollectorConfig(DefaultErrorCounterName)), - requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig] = + requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig] = (_: GenericRequest[_, _], _: Throwable) => Some(CollectorConfig(DefaultFailureCounterName)), - requestToSizeHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => + requestToSizeHistogramMapper: GenericRequest[_, _] => Option[CollectorConfig] = (_: GenericRequest[_, _]) => Some(CollectorConfig(DefaultRequestSizeHistogramName, unit = Some(CollectorConfig.Bytes))), - responseToSizeHistogramMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => + responseToSizeHistogramMapper: Response[_] => Option[CollectorConfig] = (_: Response[_]) => Some(CollectorConfig(DefaultResponseSizeHistogramName, unit = Some(CollectorConfig.Bytes))) ): OpenTelemetryMetricsConfig = OpenTelemetryMetricsConfig( diff --git a/observability/opentelemetry-tracing-zio-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala b/observability/opentelemetry-tracing-zio-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala index 451340fb5a..57389f982a 100644 --- a/observability/opentelemetry-tracing-zio-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala +++ b/observability/opentelemetry-tracing-zio-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala @@ -13,9 +13,9 @@ import zio.telemetry.opentelemetry._ import scala.collection.mutable private class OpenTelemetryTracingZioBackend[+P]( - delegate: GenericBackend[Task, P], - tracer: OpenTelemetryZioTracer, - tracing: Tracing + delegate: GenericBackend[Task, P], + tracer: OpenTelemetryZioTracer, + tracing: Tracing ) extends DelegateBackend[Task, P](delegate) with Backend[Task] { def send[T](request: GenericRequest[T, P with Effect[Task]]): Task[Response[T]] = { diff --git a/observability/opentelemetry-tracing-zio1-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala b/observability/opentelemetry-tracing-zio1-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala index e43ce461c3..ced2b53d86 100644 --- a/observability/opentelemetry-tracing-zio1-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala +++ b/observability/opentelemetry-tracing-zio1-backend/src/main/scala/sttp/client4/opentelemetry/zio/OpenTelemetryTracingZioBackend.scala @@ -13,9 +13,9 @@ import zio.telemetry.opentelemetry._ import scala.collection.mutable private class OpenTelemetryTracingZioBackend[+P]( - delegate: GenericBackend[Task, P], - tracer: OpenTelemetryZioTracer, - tracing: Tracing + delegate: GenericBackend[Task, P], + tracer: OpenTelemetryZioTracer, + tracing: Tracing ) extends DelegateBackend[Task, P](delegate) with Backend[Task] { def send[T](request: GenericRequest[T, P with Effect[Task]]): Task[Response[T]] = { diff --git a/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusBackend.scala b/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusBackend.scala index 8984ce6439..77c5b61450 100644 --- a/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusBackend.scala +++ b/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusBackend.scala @@ -42,7 +42,9 @@ object PrometheusBackend { FollowRedirectsBackend(ListenerBackend(delegate, RequestListener.lift(listener(config), delegate.monad))) def apply[F[_]](delegate: Backend[F], config: PrometheusConfig): Backend[F] = - wrappers.FollowRedirectsBackend[F](ListenerBackend(delegate, RequestListener.lift(listener(config), delegate.monad))) + wrappers.FollowRedirectsBackend[F]( + ListenerBackend(delegate, RequestListener.lift(listener(config), delegate.monad)) + ) def apply[F[_]](delegate: WebSocketBackend[F], config: PrometheusConfig): WebSocketBackend[F] = wrappers.FollowRedirectsBackend(ListenerBackend(delegate, RequestListener.lift(listener(config), delegate.monad))) @@ -77,13 +79,12 @@ object PrometheusBackend { * The modified collector config. The config can be used when configuring the backend using [[apply]]. */ def addMethodLabel[T <: BaseCollectorConfig](config: T, req: GenericRequest[_, _]): config.T = { - val methodLabel: Option[(String, String)] = { + val methodLabel: Option[(String, String)] = if (config.labels.map(_._1.toLowerCase).contains(DefaultMethodLabel)) { None } else { Some((DefaultMethodLabel, req.method.method.toUpperCase)) } - } config.addLabels(methodLabel.toList) } @@ -96,13 +97,12 @@ object PrometheusBackend { * The modified collector config. The config can be used when configuring the backend using [[apply]]. */ def addStatusLabel[T <: BaseCollectorConfig](config: T, resp: Response[_]): config.T = { - val statusLabel: Option[(String, String)] = { + val statusLabel: Option[(String, String)] = if (config.labels.map(_._1.toLowerCase).contains(DefaultStatusLabel)) { None } else { Some((DefaultStatusLabel, mapStatusToLabelValue(resp.code))) } - } config.addLabels(statusLabel.toList) } @@ -148,18 +148,18 @@ object PrometheusBackend { } class PrometheusListener( - requestToHistogramNameMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig], - requestToInProgressGaugeNameMapper: GenericRequest[_, _] => Option[CollectorConfig], - requestToSuccessCounterMapper: ((GenericRequest[_, _], Response[_])) => Option[CollectorConfig], - requestToErrorCounterMapper: ((GenericRequest[_, _], Response[_])) => Option[CollectorConfig], - requestToFailureCounterMapper: ((GenericRequest[_, _], Exception)) => Option[CollectorConfig], - requestToSizeSummaryMapper: GenericRequest[_, _] => Option[CollectorConfig], - responseToSizeSummaryMapper: ((GenericRequest[_, _], Response[_])) => Option[CollectorConfig], - collectorRegistry: CollectorRegistry, - histogramsCache: ConcurrentHashMap[String, Histogram], - gaugesCache: ConcurrentHashMap[String, Gauge], - countersCache: ConcurrentHashMap[String, Counter], - summariesCache: ConcurrentHashMap[String, Summary] + requestToHistogramNameMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig], + requestToInProgressGaugeNameMapper: GenericRequest[_, _] => Option[CollectorConfig], + requestToSuccessCounterMapper: ((GenericRequest[_, _], Response[_])) => Option[CollectorConfig], + requestToErrorCounterMapper: ((GenericRequest[_, _], Response[_])) => Option[CollectorConfig], + requestToFailureCounterMapper: ((GenericRequest[_, _], Exception)) => Option[CollectorConfig], + requestToSizeSummaryMapper: GenericRequest[_, _] => Option[CollectorConfig], + responseToSizeSummaryMapper: ((GenericRequest[_, _], Response[_])) => Option[CollectorConfig], + collectorRegistry: CollectorRegistry, + histogramsCache: ConcurrentHashMap[String, Histogram], + gaugesCache: ConcurrentHashMap[String, Gauge], + countersCache: ConcurrentHashMap[String, Counter], + summariesCache: ConcurrentHashMap[String, Summary] ) extends RequestListener[Identity, RequestCollectors] { override def beforeRequest(request: GenericRequest[_, _]): RequestCollectors = { @@ -180,10 +180,10 @@ class PrometheusListener( } override def requestException( - request: GenericRequest[_, _], - requestCollectors: RequestCollectors, - e: Exception - ): Unit = { + request: GenericRequest[_, _], + requestCollectors: RequestCollectors, + e: Exception + ): Unit = HttpError.find(e) match { case Some(HttpError(body, statusCode)) => requestSuccessful(request, Response(body, statusCode).copy(request = request.onlyMetadata), requestCollectors) @@ -192,12 +192,11 @@ class PrometheusListener( requestCollectors.maybeGauge.foreach(_.dec()) incCounterIfMapped((request, e), requestToFailureCounterMapper) } - } override def requestSuccessful( - request: GenericRequest[_, _], - response: Response[_], - requestCollectors: RequestCollectors + request: GenericRequest[_, _], + response: Response[_], + requestCollectors: RequestCollectors ): Unit = { requestCollectors.maybeTimer.foreach(_.observeDuration()) requestCollectors.maybeGauge.foreach(_.dec()) @@ -219,9 +218,9 @@ class PrometheusListener( } private def observeResponseContentLengthSummaryIfMapped( - request: GenericRequest[_, _], - response: Response[_], - mapper: ((GenericRequest[_, _], Response[_])) => Option[BaseCollectorConfig] + request: GenericRequest[_, _], + response: Response[_], + mapper: ((GenericRequest[_, _], Response[_])) => Option[BaseCollectorConfig] ): Unit = mapper((request, response)).foreach { data => response.contentLength.map(_.toDouble).foreach { size => @@ -230,8 +229,8 @@ class PrometheusListener( } private def observeRequestContentLengthSummaryIfMapped( - request: GenericRequest[_, _], - mapper: GenericRequest[_, _] => Option[BaseCollectorConfig] + request: GenericRequest[_, _], + mapper: GenericRequest[_, _] => Option[BaseCollectorConfig] ): Unit = mapper(request).foreach { data => (request.contentLength: Option[Long]).map(_.toDouble).foreach { size => diff --git a/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusConfig.scala b/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusConfig.scala index b6bffaa9cd..61dc52a34f 100644 --- a/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusConfig.scala +++ b/observability/prometheus-backend/src/main/scala/sttp/client4/prometheus/PrometheusConfig.scala @@ -6,25 +6,26 @@ import sttp.client4.Response import sttp.client4.prometheus.PrometheusBackend._ final case class PrometheusConfig( - requestToHistogramNameMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = + requestToHistogramNameMapper: GenericRequest[_, _] => Option[HistogramCollectorConfig] = (req: GenericRequest[_, _]) => Some(addMethodLabel(HistogramCollectorConfig(DefaultHistogramName), req)), - requestToInProgressGaugeNameMapper: GenericRequest[_, _] => Option[CollectorConfig] = - (req: GenericRequest[_, _]) => Some(addMethodLabel(CollectorConfig(DefaultRequestsInProgressGaugeName), req)), - responseToSuccessCounterMapper: (GenericRequest[_, _], Response[_]) => Option[CollectorConfig] = + requestToInProgressGaugeNameMapper: GenericRequest[_, _] => Option[CollectorConfig] = (req: GenericRequest[_, _]) => + Some(addMethodLabel(CollectorConfig(DefaultRequestsInProgressGaugeName), req)), + responseToSuccessCounterMapper: (GenericRequest[_, _], Response[_]) => Option[CollectorConfig] = (req: GenericRequest[_, _], resp: Response[_]) => Some(addStatusLabel(addMethodLabel(CollectorConfig(DefaultSuccessCounterName), req), resp)), - responseToErrorCounterMapper: (GenericRequest[_, _], Response[_]) => Option[CollectorConfig] = + responseToErrorCounterMapper: (GenericRequest[_, _], Response[_]) => Option[CollectorConfig] = (req: GenericRequest[_, _], resp: Response[_]) => Some(addStatusLabel(addMethodLabel(CollectorConfig(DefaultErrorCounterName), req), resp)), - requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig] = - (req: GenericRequest[_, _], _: Throwable) => - Some(addMethodLabel(CollectorConfig(DefaultFailureCounterName), req)), - requestToSizeSummaryMapper: GenericRequest[_, _] => Option[CollectorConfig] = (req: GenericRequest[_, _]) => + requestToFailureCounterMapper: (GenericRequest[_, _], Throwable) => Option[CollectorConfig] = ( + req: GenericRequest[_, _], + _: Throwable + ) => Some(addMethodLabel(CollectorConfig(DefaultFailureCounterName), req)), + requestToSizeSummaryMapper: GenericRequest[_, _] => Option[CollectorConfig] = (req: GenericRequest[_, _]) => Some(addMethodLabel(CollectorConfig(DefaultRequestSizeName), req)), - responseToSizeSummaryMapper: (GenericRequest[_, _], Response[_]) => Option[CollectorConfig] = + responseToSizeSummaryMapper: (GenericRequest[_, _], Response[_]) => Option[CollectorConfig] = (req: GenericRequest[_, _], resp: Response[_]) => Some(addStatusLabel(addMethodLabel(CollectorConfig(DefaultResponseSizeName), req), resp)), - collectorRegistry: CollectorRegistry = CollectorRegistry.defaultRegistry + collectorRegistry: CollectorRegistry = CollectorRegistry.defaultRegistry ) object PrometheusConfig { diff --git a/okhttp-backend/monix/src/main/scala/sttp/client4/okhttp/monix/OkHttpMonixBackend.scala b/okhttp-backend/monix/src/main/scala/sttp/client4/okhttp/monix/OkHttpMonixBackend.scala index 6e7c57c6a4..3e54958067 100644 --- a/okhttp-backend/monix/src/main/scala/sttp/client4/okhttp/monix/OkHttpMonixBackend.scala +++ b/okhttp-backend/monix/src/main/scala/sttp/client4/okhttp/monix/OkHttpMonixBackend.scala @@ -47,13 +47,12 @@ class OkHttpMonixBackend private ( stream: streams.BinaryStream, mt: MediaType, cl: Option[Long] - ): OkHttpRequestBody = { + ): OkHttpRequestBody = new OkHttpRequestBody() { override def writeTo(sink: BufferedSink): Unit = toIterable(stream).foreach(sink.write) override def contentType(): MediaType = mt override def contentLength(): Long = cl.getOrElse(super.contentLength()) } - } } override protected val bodyFromOkHttp: BodyFromOkHttp[Task, MonixStreams] = new BodyFromOkHttp[Task, MonixStreams] { @@ -82,13 +81,11 @@ class OkHttpMonixBackend private ( observable.executeAsync.subscribe(new Subscriber[T] { override implicit def scheduler: Scheduler = s - override def onError(ex: Throwable): Unit = { + override def onError(ex: Throwable): Unit = blockingQueue.put(Left(ex)) - } - override def onComplete(): Unit = { + override def onComplete(): Unit = blockingQueue.put(Left(Completed)) - } override def onNext(elem: T): Future[Ack] = { blockingQueue.put(Right(elem)) @@ -129,9 +126,9 @@ object OkHttpMonixBackend { ) def apply( - options: BackendOptions = BackendOptions.Default, - customEncodingHandler: EncodingHandler = PartialFunction.empty, - webSocketBufferCapacity: Option[Int] = OkHttpBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customEncodingHandler: EncodingHandler = PartialFunction.empty, + webSocketBufferCapacity: Option[Int] = OkHttpBackend.DefaultWebSocketBufferCapacity )(implicit s: Scheduler = Scheduler.global ): Task[WebSocketStreamBackend[Task, MonixStreams]] = @@ -145,9 +142,9 @@ object OkHttpMonixBackend { ) def resource( - options: BackendOptions = BackendOptions.Default, - customEncodingHandler: EncodingHandler = PartialFunction.empty, - webSocketBufferCapacity: Option[Int] = OkHttpBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customEncodingHandler: EncodingHandler = PartialFunction.empty, + webSocketBufferCapacity: Option[Int] = OkHttpBackend.DefaultWebSocketBufferCapacity )(implicit s: Scheduler = Scheduler.global ): Resource[Task, WebSocketStreamBackend[Task, MonixStreams]] = diff --git a/okhttp-backend/src/main/scala/sttp/client4/okhttp/BodyFromOkHttp.scala b/okhttp-backend/src/main/scala/sttp/client4/okhttp/BodyFromOkHttp.scala index 6a0ea56e70..6ce96ca08b 100644 --- a/okhttp-backend/src/main/scala/sttp/client4/okhttp/BodyFromOkHttp.scala +++ b/okhttp-backend/src/main/scala/sttp/client4/okhttp/BodyFromOkHttp.scala @@ -30,10 +30,10 @@ private[okhttp] trait BodyFromOkHttp[F[_], S] { def compileWebSocketPipe(ws: WebSocket[F], pipe: streams.Pipe[WebSocketFrame.Data[_], WebSocketFrame]): F[Unit] def apply[T]( - responseBody: InputStream, - responseAs: ResponseAsDelegate[T, _], - responseMetadata: ResponseMetadata, - ws: Option[WebSocket[F]] + responseBody: InputStream, + responseAs: ResponseAsDelegate[T, _], + responseMetadata: ResponseMetadata, + ws: Option[WebSocket[F]] ): F[T] = bodyFromResponseAs(responseAs, responseMetadata, ws.toRight(responseBody)) private lazy val bodyFromResponseAs = @@ -41,12 +41,11 @@ private[okhttp] trait BodyFromOkHttp[F[_], S] { override protected def withReplayableBody( response: InputStream, replayableBody: Either[Array[Byte], SttpFile] - ): F[InputStream] = { + ): F[InputStream] = (replayableBody match { case Left(byteArray) => new ByteArrayInputStream(byteArray) case Right(file) => new BufferedInputStream(new FileInputStream(file.toFile)) }).unit - } override protected def regularIgnore(response: InputStream): F[Unit] = monad.eval(response.close()) diff --git a/okhttp-backend/src/main/scala/sttp/client4/okhttp/BodyToOkHttp.scala b/okhttp-backend/src/main/scala/sttp/client4/okhttp/BodyToOkHttp.scala index 7f285f7c5b..5320086246 100644 --- a/okhttp-backend/src/main/scala/sttp/client4/okhttp/BodyToOkHttp.scala +++ b/okhttp-backend/src/main/scala/sttp/client4/okhttp/BodyToOkHttp.scala @@ -1,8 +1,8 @@ package sttp.client4.okhttp import okhttp3.{ - MediaType, Headers => OkHttpHeaders, + MediaType, MultipartBody => OkHttpMultipartBody, RequestBody => OkHttpRequestBody } diff --git a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpAsyncBackend.scala b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpAsyncBackend.scala index 7abf72aad6..fd2c672deb 100644 --- a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpAsyncBackend.scala +++ b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpAsyncBackend.scala @@ -8,7 +8,7 @@ import sttp.capabilities.Streams import sttp.client4.internal.ws.{SimpleQueue, WebSocketEvent} import sttp.monad.syntax._ import sttp.client4.okhttp.OkHttpBackend.EncodingHandler -import sttp.client4.{GenericRequest, Response, ignore} +import sttp.client4.{ignore, GenericRequest, Response} import sttp.monad.{Canceler, MonadAsyncError} abstract class OkHttpAsyncBackend[F[_], S <: Streams[S], P]( diff --git a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpBackend.scala b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpBackend.scala index 04a85978f4..d979001929 100644 --- a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpBackend.scala +++ b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpBackend.scala @@ -8,10 +8,10 @@ import okhttp3.{ Authenticator, Credentials, OkHttpClient, - Route, Request => OkHttpRequest, RequestBody => OkHttpRequestBody, - Response => OkHttpResponse + Response => OkHttpResponse, + Route } import sttp.capabilities.{Effect, Streams} import sttp.client4.BackendOptions.Proxy @@ -33,7 +33,7 @@ abstract class OkHttpBackend[F[_], S <: Streams[S], P]( val streams: Streams[S] type R = P with Effect[F] - override def send[T](request: GenericRequest[T, R]): F[Response[T]] = { + override def send[T](request: GenericRequest[T, R]): F[Response[T]] = adjustExceptions(request.isWebSocket, request) { if (request.isWebSocket) { sendWebSocket(request) @@ -41,7 +41,6 @@ abstract class OkHttpBackend[F[_], S <: Streams[S], P]( sendRegular(request) } } - } protected def sendRegular[T](request: GenericRequest[T, R]): F[Response[T]] protected def sendWebSocket[T](request: GenericRequest[T, R]): F[Response[T]] @@ -65,7 +64,7 @@ abstract class OkHttpBackend[F[_], S <: Streams[S], P]( } ) - request.headers.foreach { header => builder.addHeader(header.name, header.value) } + request.headers.foreach(header => builder.addHeader(header.name, header.value)) builder.build() } @@ -74,9 +73,9 @@ abstract class OkHttpBackend[F[_], S <: Streams[S], P]( protected val bodyFromOkHttp: BodyFromOkHttp[F, S] private[okhttp] def readResponse[T]( - res: OkHttpResponse, - request: GenericRequest[_, R], - responseAs: ResponseAsDelegate[T, R] + res: OkHttpResponse, + request: GenericRequest[_, R], + responseAs: ResponseAsDelegate[T, R] ): F[Response[T]] = { val headers = readHeaders(res) val responseMetadata = ResponseMetadata(StatusCode(res.code()), res.message(), headers) @@ -102,14 +101,13 @@ abstract class OkHttpBackend[F[_], S <: Streams[S], P]( monad.map(body)(Response(_, StatusCode(res.code()), res.message(), headers, Nil, request.onlyMetadata)) } - private def readHeaders(res: OkHttpResponse): List[Header] = { + private def readHeaders(res: OkHttpResponse): List[Header] = res .headers() .names() .asScala .flatMap(name => res.headers().values(name).asScala.map(Header(name, _))) .toList - } private def standardEncoding: (InputStream, String) => InputStream = { case (body, "gzip") => new GZIPInputStream(body) @@ -159,8 +157,8 @@ object OkHttpBackend { } private[okhttp] def updateClientIfCustomReadTimeout[T, S]( - r: GenericRequest[T, S], - client: OkHttpClient + r: GenericRequest[T, S], + client: OkHttpClient ): OkHttpClient = { val readTimeoutMillis = if (r.options.readTimeout.isFinite) r.options.readTimeout.toMillis else 0 val reuseClient = readTimeoutMillis == client.readTimeoutMillis() @@ -173,9 +171,9 @@ object OkHttpBackend { } private[okhttp] def exceptionToSttpClientException( - isWebsocket: Boolean, - request: GenericRequest[_, _], - e: Exception + isWebsocket: Boolean, + request: GenericRequest[_, _], + e: Exception ): Option[Exception] = e match { // if the websocket protocol upgrade fails, OkHttp throws a ProtocolException - however the whole request has diff --git a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpFutureBackend.scala b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpFutureBackend.scala index 1b64a6e22f..784ccf062b 100644 --- a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpFutureBackend.scala +++ b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpFutureBackend.scala @@ -8,7 +8,7 @@ import sttp.client4.internal.ws.{FutureSimpleQueue, SimpleQueue} import sttp.client4.okhttp.OkHttpBackend.EncodingHandler import sttp.client4.testing.WebSocketBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, DefaultReadTimeout, WebSocketBackend, wrappers} +import sttp.client4.{wrappers, BackendOptions, DefaultReadTimeout, WebSocketBackend} import sttp.monad.{FutureMonad, MonadError} import sttp.ws.WebSocket @@ -51,12 +51,14 @@ object OkHttpFutureBackend { )(implicit ec: ExecutionContext ): WebSocketBackend[Future] = - wrappers.FollowRedirectsBackend(new OkHttpFutureBackend(client, closeClient, customEncodingHandler, webSocketBufferCapacity)) + wrappers.FollowRedirectsBackend( + new OkHttpFutureBackend(client, closeClient, customEncodingHandler, webSocketBufferCapacity) + ) def apply( - options: BackendOptions = BackendOptions.Default, - customEncodingHandler: EncodingHandler = PartialFunction.empty, - webSocketBufferCapacity: Option[Int] = OkHttpBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customEncodingHandler: EncodingHandler = PartialFunction.empty, + webSocketBufferCapacity: Option[Int] = OkHttpBackend.DefaultWebSocketBufferCapacity )(implicit ec: ExecutionContext = ExecutionContext.global): WebSocketBackend[Future] = OkHttpFutureBackend( OkHttpBackend.defaultClient(DefaultReadTimeout.toMillis, options), diff --git a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpSyncBackend.scala b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpSyncBackend.scala index 33181e394d..0e749d6cf0 100644 --- a/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpSyncBackend.scala +++ b/okhttp-backend/src/main/scala/sttp/client4/okhttp/OkHttpSyncBackend.scala @@ -11,12 +11,21 @@ import sttp.client4.monad.IdMonad import sttp.client4.okhttp.OkHttpBackend.EncodingHandler import sttp.client4.testing.WebSocketBackendStub import sttp.client4.wrappers.FollowRedirectsBackend -import sttp.client4.{BackendOptions, DefaultReadTimeout, GenericRequest, Identity, Response, WebSocketBackend, ignore, wrappers} +import sttp.client4.{ + ignore, + wrappers, + BackendOptions, + DefaultReadTimeout, + GenericRequest, + Identity, + Response, + WebSocketBackend +} import sttp.monad.MonadError import sttp.ws.WebSocket import scala.concurrent.duration.Duration -import scala.concurrent.{Await, ExecutionContext, Future, blocking} +import scala.concurrent.{blocking, Await, ExecutionContext, Future} class OkHttpSyncBackend private ( client: OkHttpClient, @@ -97,12 +106,14 @@ object OkHttpSyncBackend { customEncodingHandler: EncodingHandler, webSocketBufferCapacity: Option[Int] ): WebSocketBackend[Identity] = - wrappers.FollowRedirectsBackend(new OkHttpSyncBackend(client, closeClient, customEncodingHandler, webSocketBufferCapacity)) + wrappers.FollowRedirectsBackend( + new OkHttpSyncBackend(client, closeClient, customEncodingHandler, webSocketBufferCapacity) + ) def apply( - options: BackendOptions = BackendOptions.Default, - customEncodingHandler: EncodingHandler = PartialFunction.empty, - webSocketBufferCapacity: Option[Int] = OkHttpBackend.DefaultWebSocketBufferCapacity + options: BackendOptions = BackendOptions.Default, + customEncodingHandler: EncodingHandler = PartialFunction.empty, + webSocketBufferCapacity: Option[Int] = OkHttpBackend.DefaultWebSocketBufferCapacity ): WebSocketBackend[Identity] = OkHttpSyncBackend( OkHttpBackend.defaultClient(DefaultReadTimeout.toMillis, options), diff --git a/okhttp-backend/src/main/scala/sttp/client4/okhttp/WebSocketImpl.scala b/okhttp-backend/src/main/scala/sttp/client4/okhttp/WebSocketImpl.scala index 3b4b50b987..ffaf0671d5 100644 --- a/okhttp-backend/src/main/scala/sttp/client4/okhttp/WebSocketImpl.scala +++ b/okhttp-backend/src/main/scala/sttp/client4/okhttp/WebSocketImpl.scala @@ -2,7 +2,7 @@ package sttp.client4.okhttp import java.util.concurrent.atomic.AtomicBoolean -import okhttp3.{WebSocketListener, Headers => OkHttpHeaders, Response => OkHttpResponse, WebSocket => OkHttpWebSocket} +import okhttp3.{Headers => OkHttpHeaders, Response => OkHttpResponse, WebSocket => OkHttpWebSocket, WebSocketListener} import okio.ByteString import sttp.client4.internal.ws.{SimpleQueue, WebSocketEvent} import sttp.model.{Header, Headers} @@ -24,7 +24,7 @@ private[okhttp] class WebSocketImpl[F[_]]( /** After receiving a close frame, no further interactions with the web socket should happen. Subsequent invocations * of `receive`, as well as `send`, will fail with the [[sttp.ws.WebSocketClosed]] exception. */ - override def receive(): F[WebSocketFrame] = { + override def receive(): F[WebSocketFrame] = queue.poll.flatMap { case WebSocketEvent.Open() => receive() @@ -38,7 +38,6 @@ private[okhttp] class WebSocketImpl[F[_]]( case WebSocketEvent.Frame(f: WebSocketFrame) => monad.unit(f) } - } override def send(f: WebSocketFrame, isContinuation: Boolean = false): F[Unit] = monad.flatten(monad.eval(f match { @@ -59,13 +58,12 @@ private[okhttp] class WebSocketImpl[F[_]]( monad.error(new UnsupportedOperationException("Pong is handled by okhttp under the hood")) })) - private def fromBoolean(result: Boolean): F[Unit] = { + private def fromBoolean(result: Boolean): F[Unit] = if (!result) { monad.error(new SendMessageException) } else { monad.unit(()) } - } override lazy val upgradeHeaders: Headers = Headers( _headers.iterator().asScala.map(p => Header(p.getFirst, p.getSecond)).toList @@ -132,9 +130,8 @@ private[okhttp] class AddToQueueListener[F[_]](queue: SimpleQueue[F, WebSocketEv override def onMessage(webSocket: OkHttpWebSocket, bytes: ByteString): Unit = onFrame(WebSocketFrame.Binary(bytes.toByteArray, finalFragment = true, None)) - override def onMessage(webSocket: OkHttpWebSocket, text: String): Unit = { + override def onMessage(webSocket: OkHttpWebSocket, text: String): Unit = onFrame(WebSocketFrame.Text(text, finalFragment = true, None)) - } private def onFrame(f: WebSocketFrame): Unit = queue.offer(WebSocketEvent.Frame(f)) } diff --git a/testing/server/src/main/scala/akka/http/scaladsl/coding/DeflateNoWrap.scala b/testing/server/src/main/scala/akka/http/scaladsl/coding/DeflateNoWrap.scala index 9b9ad47047..b0c72b10c3 100644 --- a/testing/server/src/main/scala/akka/http/scaladsl/coding/DeflateNoWrap.scala +++ b/testing/server/src/main/scala/akka/http/scaladsl/coding/DeflateNoWrap.scala @@ -9,9 +9,8 @@ class DeflateNoWrap private[http] ( compressionLevel: Int, override val messageFilter: HttpMessage => Boolean ) extends Deflate(compressionLevel, messageFilter) { - def this(messageFilter: HttpMessage => Boolean) = { + def this(messageFilter: HttpMessage => Boolean) = this(DeflateCompressor.DefaultCompressionLevel, messageFilter) - } override def newCompressor = new DeflateCompressor(compressionLevel) { override protected lazy val deflater = new Deflater(compressionLevel, true) diff --git a/testing/server/src/main/scala/sttp/client4/testing/server/HttpServer.scala b/testing/server/src/main/scala/sttp/client4/testing/server/HttpServer.scala index 9035701209..ebc9578783 100644 --- a/testing/server/src/main/scala/sttp/client4/testing/server/HttpServer.scala +++ b/testing/server/src/main/scala/sttp/client4/testing/server/HttpServer.scala @@ -94,7 +94,7 @@ private class HttpServer(port: Int, info: String => Unit) extends AutoCloseable } } ~ path("exact") { post { - entity(as[Array[Byte]]) { (body: Array[Byte]) => complete(body) } + entity(as[Array[Byte]])((body: Array[Byte]) => complete(body)) } } ~ post { parameterMap { params => @@ -120,7 +120,7 @@ private class HttpServer(port: Int, info: String => Unit) extends AutoCloseable } ~ pathPrefix("streaming") { path("echo") { post { - parameterMap { _ => entity(as[String]) { (body: String) => complete(body) } } + parameterMap(_ => entity(as[String])((body: String) => complete(body))) } } ~ path("is_chunked") { @@ -214,7 +214,7 @@ private class HttpServer(port: Int, info: String => Unit) extends AutoCloseable Some(un) case _ => None } - ) { userName => complete(s"Hello, $userName!") } + )(userName => complete(s"Hello, $userName!")) } ~ path("secure_digest") { get { import akka.http.scaladsl.model._ @@ -387,13 +387,13 @@ private class HttpServer(port: Int, info: String => Unit) extends AutoCloseable discardEntity(redirect("/redirect/get_after_post/result", StatusCodes.PermanentRedirect)) } ~ path("result") { get(complete(s"GET")) ~ - entity(as[String]) { (body: String) => post(complete(s"POST$body")) } + entity(as[String])((body: String) => post(complete(s"POST$body"))) } } ~ pathPrefix("strip_sensitive_headers") { path("r1") { discardEntity(redirect("/redirect/strip_sensitive_headers/result", StatusCodes.PermanentRedirect)) } ~ path("result") { - extractRequest { (req: HttpRequest) => complete(s"${req.headers.mkString(",")}") } + extractRequest((req: HttpRequest) => complete(s"${req.headers.mkString(",")}")) } } ~ pathPrefix("relative") { discardEntity(redirect("r4", StatusCodes.PermanentRedirect)) @@ -490,15 +490,14 @@ private class HttpServer(port: Int, info: String => Unit) extends AutoCloseable } } - def discardEntity(inner: Route): Route = { + def discardEntity(inner: Route): Route = extractRequest { request => onComplete(request.entity.discardBytes().future()) { _ => inner } } - } - val corsServerRoutes: Route = { + val corsServerRoutes: Route = handleRejections(CorsDirectives.corsRejectionHandler) { cors(corsSettings) { handleRejections(RejectionHandler.default) { @@ -506,15 +505,13 @@ private class HttpServer(port: Int, info: String => Unit) extends AutoCloseable } } } - } - def start(): Future[Http.ServerBinding] = { + def start(): Future[Http.ServerBinding] = unbindServer().flatMap { _ => val server = Http().bindAndHandle(corsServerRoutes, "localhost", port) this.server = Some(server) server } - } def close(): Unit = { val unbind = unbindServer() @@ -525,9 +522,8 @@ private class HttpServer(port: Int, info: String => Unit) extends AutoCloseable ) } - private def unbindServer(): Future[Done] = { + private def unbindServer(): Future[Done] = server.map(_.flatMap(_.unbind())).getOrElse(Future.successful(Done)) - } private def transfer(is: InputStream, os: OutputStream): Unit = { var read = 0