diff options
3 files changed, 78 insertions, 24 deletions
@@ -498,6 +498,22 @@ sttp .send() ``` +## SSL + +SSL handling can be customized (or disabled) when creating a backend and is +backend-specific. + +Depending on the underlying backend's client, you can customize SSL settings +as follows: + +* `HttpUrlConnectionBackend`: when creating the backend, specify the `customizeConnection: HttpURLConnection => Unit` +parameter, and set the hostname verifier & SSL socket factory as required +* akka-http: when creating the backend, specify the `customHttpsContext: Option[HttpsConnectionContext]` +parameter. See [akka-http docs](http://doc.akka.io/docs/akka-http/current/scala/http/server-side/server-https-support.html) +* async-http-client: create a custom client and use the `setSSLContext` method +* OkHttp: create a custom client modifying the SSL settings as described +[on the wiki](https://github.com/square/okhttp/wiki/HTTPS) + ## Notes * the encoding for `String`s defaults to `utf-8`. diff --git a/akka-http-backend/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpBackend.scala b/akka-http-backend/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpBackend.scala index 57b5441..897692c 100644 --- a/akka-http-backend/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpBackend.scala +++ b/akka-http-backend/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpBackend.scala @@ -3,7 +3,8 @@ package com.softwaremill.sttp.akkahttp import java.io.{File, IOException, UnsupportedEncodingException} import akka.actor.ActorSystem -import akka.http.scaladsl.Http +import akka.event.LoggingAdapter +import akka.http.scaladsl.{Http, HttpsConnectionContext} import akka.http.scaladsl.coding.{Deflate, Gzip, NoCoding} import akka.http.scaladsl.model.ContentTypes.`application/octet-stream` import akka.http.scaladsl.model.HttpHeader.ParsingResult @@ -25,10 +26,14 @@ import scala.concurrent.duration.FiniteDuration import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success, Try} -class AkkaHttpBackend private (actorSystem: ActorSystem, - ec: ExecutionContext, - terminateActorSystemOnClose: Boolean, - connectionTimeout: FiniteDuration) +class AkkaHttpBackend private ( + actorSystem: ActorSystem, + ec: ExecutionContext, + terminateActorSystemOnClose: Boolean, + connectionTimeout: FiniteDuration, + customHttpsContext: Option[HttpsConnectionContext], + customConnectionPoolSettings: Option[ConnectionPoolSettings], + customLog: Option[LoggingAdapter]) extends SttpBackend[Future, Source[ByteString, Any]] { // the supported stream type @@ -37,10 +42,13 @@ class AkkaHttpBackend private (actorSystem: ActorSystem, private implicit val as: ActorSystem = actorSystem private implicit val materializer: ActorMaterializer = ActorMaterializer() + private val http = Http() + private val connectionSettings = ClientConnectionSettings(actorSystem) .withConnectingTimeout(connectionTimeout) - private val connectionPoolSettings = ConnectionPoolSettings(actorSystem) + private val connectionPoolSettings = + customConnectionPoolSettings.getOrElse(ConnectionPoolSettings(actorSystem)) override def send[T](r: Request[T, S]): Future[Response[T]] = { implicit val ec: ExecutionContext = this.ec @@ -52,7 +60,13 @@ class AkkaHttpBackend private (actorSystem: ActorSystem, requestToAkka(r) .flatMap(setBodyOnAkka(r, r.body, _)) .toFuture - .flatMap(req => Http().singleRequest(req, settings = settings)) + .flatMap( + req => + http.singleRequest(req, + settings = settings, + connectionContext = customHttpsContext.getOrElse( + http.defaultClientHttpsContext), + log = customLog.getOrElse(actorSystem.log))) .flatMap { hr => val code = hr.status.intValue() @@ -282,17 +296,23 @@ class AkkaHttpBackend private (actorSystem: ActorSystem, } object AkkaHttpBackend { - - private def apply(actorSystem: ActorSystem, - ec: ExecutionContext, - terminateActorSystemOnClose: Boolean, - connectionTimeout: FiniteDuration) + private def apply( + actorSystem: ActorSystem, + ec: ExecutionContext, + terminateActorSystemOnClose: Boolean, + connectionTimeout: FiniteDuration, + customHttpsContext: Option[HttpsConnectionContext], + customConnectionPoolSettings: Option[ConnectionPoolSettings], + customLog: Option[LoggingAdapter]) : SttpBackend[Future, Source[ByteString, Any]] = new FollowRedirectsBackend( new AkkaHttpBackend(actorSystem, ec, terminateActorSystemOnClose, - connectionTimeout)) + connectionTimeout, + customHttpsContext, + customConnectionPoolSettings, + customLog)) /** * @param ec The execution context for running non-network related operations, @@ -300,13 +320,19 @@ object AkkaHttpBackend { * context. */ def apply(connectionTimeout: FiniteDuration = - SttpBackend.DefaultConnectionTimeout)( + SttpBackend.DefaultConnectionTimeout, + customHttpsContext: Option[HttpsConnectionContext] = None, + customConnectionPoolSettings: Option[ConnectionPoolSettings] = None, + customLog: Option[LoggingAdapter] = None)( implicit ec: ExecutionContext = ExecutionContext.Implicits.global) : SttpBackend[Future, Source[ByteString, Any]] = AkkaHttpBackend(ActorSystem("sttp"), ec, terminateActorSystemOnClose = true, - connectionTimeout) + connectionTimeout, + customHttpsContext, + customConnectionPoolSettings, + customLog) /** * @param actorSystem The actor system which will be used for the http-client @@ -315,13 +341,19 @@ object AkkaHttpBackend { * e.g. mapping responses. Defaults to the global execution * context. */ - def usingActorSystem(actorSystem: ActorSystem, - connectionTimeout: FiniteDuration = - SttpBackend.DefaultConnectionTimeout)( + def usingActorSystem( + actorSystem: ActorSystem, + connectionTimeout: FiniteDuration = SttpBackend.DefaultConnectionTimeout, + customHttpsContext: Option[HttpsConnectionContext] = None, + customConnectionPoolSettings: Option[ConnectionPoolSettings] = None, + customLog: Option[LoggingAdapter] = None)( implicit ec: ExecutionContext = ExecutionContext.Implicits.global) : SttpBackend[Future, Source[ByteString, Any]] = AkkaHttpBackend(actorSystem, ec, terminateActorSystemOnClose = false, - connectionTimeout) + connectionTimeout, + customHttpsContext, + customConnectionPoolSettings, + customLog) } diff --git a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionBackend.scala b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionBackend.scala index 2dbb13d..62ef2bc 100644 --- a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionBackend.scala +++ b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionBackend.scala @@ -13,7 +13,9 @@ import scala.io.Source import scala.collection.JavaConverters._ import scala.concurrent.duration.{Duration, FiniteDuration} -class HttpURLConnectionBackend private (connectionTimeout: FiniteDuration) +class HttpURLConnectionBackend private ( + connectionTimeout: FiniteDuration, + customizeConnection: HttpURLConnection => Unit) extends SttpBackend[Id, Nothing] { override def send[T](r: Request[T, Nothing]): Response[T] = { val c = @@ -27,6 +29,8 @@ class HttpURLConnectionBackend private (connectionTimeout: FiniteDuration) // redirects are handled by FollowRedirectsBackend c.setInstanceFollowRedirects(false) + customizeConnection(c) + if (r.body != NoBody) { c.setDoOutput(true) // we need to take care to: @@ -258,9 +262,11 @@ class HttpURLConnectionBackend private (connectionTimeout: FiniteDuration) object HttpURLConnectionBackend { - def apply( - connectionTimeout: FiniteDuration = SttpBackend.DefaultConnectionTimeout) - : SttpBackend[Id, Nothing] = + def apply(connectionTimeout: FiniteDuration = + SttpBackend.DefaultConnectionTimeout, + customizeConnection: HttpURLConnection => Unit = { _ => + () + }): SttpBackend[Id, Nothing] = new FollowRedirectsBackend[Id, Nothing]( - new HttpURLConnectionBackend(connectionTimeout)) + new HttpURLConnectionBackend(connectionTimeout, customizeConnection)) } |