diff --git a/http4s-client-blaze-pureconfig/src/main/scala-2/com/avast/sst/http4s/client/pureconfig/ConfigReaders.scala b/http4s-client-blaze-pureconfig/src/main/scala-2/com/avast/sst/http4s/client/pureconfig/ConfigReaders.scala index 290fbe2e4..af3ee87ab 100644 --- a/http4s-client-blaze-pureconfig/src/main/scala-2/com/avast/sst/http4s/client/pureconfig/ConfigReaders.scala +++ b/http4s-client-blaze-pureconfig/src/main/scala-2/com/avast/sst/http4s/client/pureconfig/ConfigReaders.scala @@ -1,13 +1,14 @@ package com.avast.sst.http4s.client.pureconfig -import cats.syntax.either._ +import cats.syntax.either.* import com.avast.sst.http4s.client.Http4sBlazeClientConfig +import com.avast.sst.http4s.client.Http4sBlazeClientConfig.SocketOptions import org.http4s.blaze.client.ParserMode import org.http4s.headers.`User-Agent` import pureconfig.ConfigReader import pureconfig.error.CannotConvert import pureconfig.generic.ProductHint -import pureconfig.generic.semiauto._ +import pureconfig.generic.semiauto.* trait ConfigReaders { @@ -17,6 +18,8 @@ trait ConfigReaders { `User-Agent`.parse(value).leftMap { parseFailure => CannotConvert(value, "User-Agent HTTP header", parseFailure.message) } } + implicit val http4sClientSocketOptionsReader: ConfigReader[SocketOptions] = deriveReader[SocketOptions] + implicit val http4sClientParserModeReader: ConfigReader[ParserMode] = deriveEnumerationReader implicit val http4sClientHttp4sBlazeClientConfigReader: ConfigReader[Http4sBlazeClientConfig] = deriveReader[Http4sBlazeClientConfig] diff --git a/http4s-client-blaze-pureconfig/src/main/scala-3/com/avast/sst/http4s/client/pureconfig/ConfigReaders.scala b/http4s-client-blaze-pureconfig/src/main/scala-3/com/avast/sst/http4s/client/pureconfig/ConfigReaders.scala index 037417ae4..99492c153 100644 --- a/http4s-client-blaze-pureconfig/src/main/scala-3/com/avast/sst/http4s/client/pureconfig/ConfigReaders.scala +++ b/http4s-client-blaze-pureconfig/src/main/scala-3/com/avast/sst/http4s/client/pureconfig/ConfigReaders.scala @@ -2,6 +2,7 @@ package com.avast.sst.http4s.client.pureconfig import cats.syntax.either.* import com.avast.sst.http4s.client.Http4sBlazeClientConfig +import com.avast.sst.http4s.client.Http4sBlazeClientConfig.SocketOptions import org.http4s.blaze.client.ParserMode import org.http4s.headers.`User-Agent` import pureconfig.ConfigReader @@ -14,6 +15,8 @@ trait ConfigReaders { `User-Agent`.parse(value).leftMap { parseFailure => CannotConvert(value, "User-Agent HTTP header", parseFailure.message) } } + implicit val http4sClientSocketOptionsReader: ConfigReader[SocketOptions] = ConfigReader.derived + implicit val http4sClientParserModeReader: ConfigReader[ParserMode] = ConfigReader.derived implicit val http4sClientHttp4sBlazeClientConfigReader: ConfigReader[Http4sBlazeClientConfig] = diff --git a/http4s-client-blaze/src/main/scala/com/avast/sst/http4s/client/Http4sBlazeClientConfig.scala b/http4s-client-blaze/src/main/scala/com/avast/sst/http4s/client/Http4sBlazeClientConfig.scala index c7b3702a9..75597522b 100644 --- a/http4s-client-blaze/src/main/scala/com/avast/sst/http4s/client/Http4sBlazeClientConfig.scala +++ b/http4s-client-blaze/src/main/scala/com/avast/sst/http4s/client/Http4sBlazeClientConfig.scala @@ -1,5 +1,6 @@ package com.avast.sst.http4s.client +import com.avast.sst.http4s.client.Http4sBlazeClientConfig.SocketOptions import org.http4s.blaze.client.ParserMode import org.http4s.client.defaults import org.http4s.headers.`User-Agent` @@ -23,5 +24,16 @@ final case class Http4sBlazeClientConfig( maxChunkSize: Int = Int.MaxValue, chunkBufferMaxSize: Int = 1024 * 1024, parserMode: ParserMode = ParserMode.Strict, - bufferSize: Int = 8192 + bufferSize: Int = 8192, + socketOptions: Option[SocketOptions] = None ) + +object Http4sBlazeClientConfig { + final case class SocketOptions( + reuseAddress: Boolean = true, + sendBufferSize: Int = 256 * 1024, + receiveBufferSize: Int = 256 * 1024, + keepAlive: Boolean = false, + noDelay: Boolean = false + ) +} diff --git a/http4s-client-blaze/src/main/scala/com/avast/sst/http4s/client/Http4sBlazeClientModule.scala b/http4s-client-blaze/src/main/scala/com/avast/sst/http4s/client/Http4sBlazeClientModule.scala index ee97de78c..b1b037ae6 100644 --- a/http4s-client-blaze/src/main/scala/com/avast/sst/http4s/client/Http4sBlazeClientModule.scala +++ b/http4s-client-blaze/src/main/scala/com/avast/sst/http4s/client/Http4sBlazeClientModule.scala @@ -1,9 +1,12 @@ package com.avast.sst.http4s.client import cats.effect.{ConcurrentEffect, Resource} +import com.avast.sst.http4s.client.Http4sBlazeClientConfig.SocketOptions +import org.http4s.blaze.channel.{ChannelOptions, OptionValue} import org.http4s.blaze.client.BlazeClientBuilder import org.http4s.client.Client +import java.net.StandardSocketOptions import javax.net.ssl.SSLContext import scala.concurrent.ExecutionContext object Http4sBlazeClientModule { @@ -35,7 +38,20 @@ object Http4sBlazeClientModule { .withParserMode(config.parserMode) .withBufferSize(config.bufferSize) - sslContext.map(builder.withSslContext).getOrElse(builder).resource + val builderWithMaybeSocketOptions = config.socketOptions.fold(builder)(s => builder.withChannelOptions(channelOptions(s))) + val builderWithMaybeTLS = sslContext.fold(builderWithMaybeSocketOptions)(builderWithMaybeSocketOptions.withSslContext) + + builderWithMaybeTLS.resource } + def channelOptions(socketOptions: SocketOptions): ChannelOptions = + ChannelOptions( + Vector( + OptionValue[java.lang.Boolean](StandardSocketOptions.SO_REUSEADDR, socketOptions.reuseAddress), + OptionValue[java.lang.Integer](StandardSocketOptions.SO_SNDBUF, socketOptions.sendBufferSize), + OptionValue[java.lang.Integer](StandardSocketOptions.SO_RCVBUF, socketOptions.receiveBufferSize), + OptionValue[java.lang.Boolean](StandardSocketOptions.SO_KEEPALIVE, socketOptions.keepAlive), + OptionValue[java.lang.Boolean](StandardSocketOptions.TCP_NODELAY, socketOptions.noDelay) + ) + ) }