diff --git a/akka-docs-dev/rst/java/http/client-side/connection-level.rst b/akka-docs-dev/rst/java/http/client-side/connection-level.rst index 775ecbdbc3..b4d6d4e4e3 100644 --- a/akka-docs-dev/rst/java/http/client-side/connection-level.rst +++ b/akka-docs-dev/rst/java/http/client-side/connection-level.rst @@ -62,7 +62,19 @@ as a more general purpose streaming infrastructure feature. However, akka-stream should soon provide such a feature. +.. _http-client-layer-java: + Stand-Alone HTTP Layer Usage ---------------------------- -// TODO \ No newline at end of file +Due to its Reactive-Streams-based nature the Akka HTTP layer is fully detachable from the underlying TCP +interface. While in most applications this "feature" will not be crucial it can be useful in certain cases to be able +to "run" the HTTP layer (and, potentially, higher-layers) against data that do not come from the network but rather +some other source. Potential scenarios where this might be useful include tests, debugging or low-level event-sourcing +(e.g by replaying network traffic). + +On the client-side the stand-alone HTTP layer forms a ``BidiFlow``, +that is a stage that "upgrades" a potentially encrypted raw connection to the HTTP level. + +You create an instance of the layer by calling one of the two overloads of the ``Http.get(system).clientLayer`` method, +which also allows for varying degrees of configuration. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/server-side/low-level-server-side-api.rst b/akka-docs-dev/rst/java/http/server-side/low-level-server-side-api.rst index e6e85561c3..1f60302120 100644 --- a/akka-docs-dev/rst/java/http/server-side/low-level-server-side-api.rst +++ b/akka-docs-dev/rst/java/http/server-side/low-level-server-side-api.rst @@ -154,7 +154,15 @@ If defined encryption is enabled on all accepted connections. Otherwise it is di Stand-Alone HTTP Layer Usage ---------------------------- -It is currently only possible to use the HTTP server layer with Scala in a stand-alone fashion. -See :ref:`http-server-layer-scala` and `#18027`_ for the plan to add Java support. +Due to its Reactive-Streams-based nature the Akka HTTP layer is fully detachable from the underlying TCP +interface. While in most applications this "feature" will not be crucial it can be useful in certain cases to be able +to "run" the HTTP layer (and, potentially, higher-layers) against data that do not come from the network but rather +some other source. Potential scenarios where this might be useful include tests, debugging or low-level event-sourcing +(e.g by replaying network traffic). -.. _`#18027`: https://github.com/akka/akka/issues/18027 \ No newline at end of file +On the server-side the stand-alone HTTP layer forms a ``BidiFlow``, +that is a stage that "upgrades" a potentially encrypted raw connection to the HTTP level. + +You create an instance of the layer by calling one of the two overloads of the ``Http.get(system).serverLayer`` method, +which also allows for varying degrees of configuration. Note, that the returned instance is not reusable and can only +be materialized once. \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/client-side/connection-level.rst b/akka-docs-dev/rst/scala/http/client-side/connection-level.rst index 162937b06d..1f403e88b6 100644 --- a/akka-docs-dev/rst/scala/http/client-side/connection-level.rst +++ b/akka-docs-dev/rst/scala/http/client-side/connection-level.rst @@ -67,7 +67,7 @@ However, akka-stream should soon provide such a feature. Stand-Alone HTTP Layer Usage ---------------------------- -Due to its Reactive-Stream-based nature the Akka HTTP layer is fully detachable from the underlying TCP +Due to its Reactive-Streams-based nature the Akka HTTP layer is fully detachable from the underlying TCP interface. While in most applications this "feature" will not be crucial it can be useful in certain cases to be able to "run" the HTTP layer (and, potentially, higher-layers) against data that do not come from the network but rather some other source. Potential scenarios where this might be useful include tests, debugging or low-level event-sourcing diff --git a/akka-docs-dev/rst/scala/http/low-level-server-side-api.rst b/akka-docs-dev/rst/scala/http/low-level-server-side-api.rst index 8726eb5951..23e67ec865 100644 --- a/akka-docs-dev/rst/scala/http/low-level-server-side-api.rst +++ b/akka-docs-dev/rst/scala/http/low-level-server-side-api.rst @@ -155,13 +155,13 @@ If defined encryption is enabled on all accepted connections. Otherwise it is di Stand-Alone HTTP Layer Usage ---------------------------- -Due to its Reactive-Stream-based nature the Akka HTTP layer is fully detachable from the underlying TCP +Due to its Reactive-Streams-based nature the Akka HTTP layer is fully detachable from the underlying TCP interface. While in most applications this "feature" will not be crucial it can be useful in certain cases to be able to "run" the HTTP layer (and, potentially, higher-layers) against data that do not come from the network but rather some other source. Potential scenarios where this might be useful include tests, debugging or low-level event-sourcing (e.g by replaying network traffic). -On the server-side the stand-alone HTTP layer forms a ``BidiStage`` that is defined like this: +On the server-side the stand-alone HTTP layer forms a ``BidiFlow`` that is defined like this: .. includecode2:: /../../akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala :snippet: server-layer diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala b/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala index ca4f6a5c74..325de26860 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala @@ -81,7 +81,7 @@ private[http] object JavaMapping { } } - /** This trivial mapping isn't enabled by default to prevent it from conflicting with the `Inherited ones `*/ + /** This trivial mapping isn't enabled by default to prevent it from conflicting with the `Inherited` ones `*/ def identity[T]: JavaMapping[T, T] = new JavaMapping[T, T] { def toJava(scalaObject: T): J = scalaObject @@ -117,6 +117,14 @@ private[http] object JavaMapping { scaladsl.Flow[JIn].map(inMapping.toScala(_)).viaMat(scalaObject)(scaladsl.Keep.right).map(outMapping.toJava(_)) } } + + def scalaToJavaAdapterFlow[J, S](implicit mapping: JavaMapping[J, S]): scaladsl.Flow[S, J, Unit] = + scaladsl.Flow[S].map(mapping.toJava(_)) + def javaToScalaAdapterFlow[J, S](implicit mapping: JavaMapping[J, S]): scaladsl.Flow[J, S, Unit] = + scaladsl.Flow[J].map(mapping.toScala(_)) + def adapterBidiFlow[JIn, SIn, SOut, JOut](implicit inMapping: JavaMapping[JIn, SIn], outMapping: JavaMapping[JOut, SOut]): scaladsl.BidiFlow[JIn, SIn, SOut, JOut, Unit] = + scaladsl.BidiFlow.wrap(javaToScalaAdapterFlow(inMapping), scalaToJavaAdapterFlow(outMapping))(scaladsl.Keep.none) + implicit def pairMapping[J1, J2, S1, S2](implicit _1Mapping: JavaMapping[J1, S1], _2Mapping: JavaMapping[J2, S2]): JavaMapping[Pair[J1, J2], (S1, S2)] = new JavaMapping[Pair[J1, J2], (S1, S2)] { def toJava(scalaObject: (S1, S2)): J = Pair(_1Mapping.toJava(scalaObject._1), _2Mapping.toJava(scalaObject._2)) @@ -165,6 +173,8 @@ private[http] object JavaMapping { implicit object RemoteAddress extends Inherited[jm.RemoteAddress, sm.RemoteAddress] implicit object TransferEncoding extends Inherited[jm.TransferEncoding, sm.TransferEncoding] + implicit object HostHeader extends Inherited[jm.headers.Host, sm.headers.Host] + implicit object ByteRange extends Inherited[jm.headers.ByteRange, sm.headers.ByteRange] implicit object CacheDirective extends Inherited[jm.headers.CacheDirective, sm.headers.CacheDirective] implicit object ContentDispositionType extends Inherited[jm.headers.ContentDispositionType, sm.headers.ContentDispositionType] diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala b/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala index af763fbb9f..6c7deabcc0 100644 --- a/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala +++ b/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala @@ -7,6 +7,7 @@ package akka.http.javadsl import java.lang.{ Iterable ⇒ JIterable } import java.net.InetSocketAddress import akka.http.impl.util.JavaMapping +import akka.stream.io.{ SslTlsInbound, SslTlsOutbound } import scala.language.implicitConversions import scala.concurrent.Future @@ -17,7 +18,8 @@ import akka.actor.{ ExtendedActorSystem, ActorSystem, ExtensionIdProvider, Exten import akka.event.LoggingAdapter import akka.io.Inet import akka.stream.Materializer -import akka.stream.javadsl.{ Flow, Source } +import akka.stream.javadsl.{ BidiFlow, Flow, Source } + import akka.http.impl.util.JavaMapping.Implicits._ import akka.http.scaladsl.{ model ⇒ sm } import akka.http.javadsl.model._ @@ -37,6 +39,42 @@ class Http(system: ExtendedActorSystem) extends akka.actor.Extension { private implicit def convertHttpsContext(hctx: Option[HttpsContext]) = hctx.map(_.asInstanceOf[akka.http.scaladsl.HttpsContext]) + /** + * Constructs a server layer stage using the configured default [[ServerSettings]]. The returned [[BidiFlow]] isn't + * reusable and can only be materialized once. + */ + def serverLayer(materializer: Materializer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, Unit] = + adaptServerLayer(delegate.serverLayer()(materializer)) + + /** + * Constructs a server layer stage using the given [[ServerSettings]]. The returned [[BidiFlow]] isn't reusable and + * can only be materialized once. + */ + def serverLayer(settings: ServerSettings, + materializer: Materializer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, Unit] = + adaptServerLayer(delegate.serverLayer(settings)(materializer)) + + /** + * Constructs a server layer stage using the given [[ServerSettings]]. The returned [[BidiFlow]] isn't reusable and + * can only be materialized once. The `remoteAddress`, if provided, will be added as a header to each [[HttpRequest]] + * this layer produces if the `akka.http.server.remote-address-header` configuration option is enabled. + */ + def serverLayer(settings: ServerSettings, + remoteAddress: Option[InetSocketAddress], + materializer: Materializer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, Unit] = + adaptServerLayer(delegate.serverLayer(settings, remoteAddress.asScala)(materializer)) + + /** + * Constructs a server layer stage using the given [[ServerSettings]]. The returned [[BidiFlow]] isn't reusable and + * can only be materialized once. The remoteAddress, if provided, will be added as a header to each [[HttpRequest]] + * this layer produces if the `akka.http.server.remote-address-header` configuration option is enabled. + */ + def serverLayer(settings: ServerSettings, + remoteAddress: Option[InetSocketAddress], + log: LoggingAdapter, + materializer: Materializer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, Unit] = + adaptServerLayer(delegate.serverLayer(settings, remoteAddress.asScala, log)(materializer)) + /** * Creates a [[Source]] of [[IncomingConnection]] instances which represents a prospective HTTP server binding * on the given `endpoint`. @@ -167,6 +205,27 @@ class Http(system: ExtendedActorSystem) extends akka.actor.Extension { interface, port, settings, httpsContext, parallelism, log)(materializer) .map(new ServerBinding(_))(ec) + /** + * Constructs a client layer stage using the configured default [[ClientConnectionSettings]]. + */ + def clientLayer(hostHeader: headers.Host): BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, Unit] = + adaptClientLayer(delegate.clientLayer(JavaMapping.toScala(hostHeader))) + + /** + * Constructs a client layer stage using the given [[ClientConnectionSettings]]. + */ + def clientLayer(hostHeader: headers.Host, + settings: ClientConnectionSettings): BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, Unit] = + adaptClientLayer(delegate.clientLayer(JavaMapping.toScala(hostHeader), settings)) + + /** + * Constructs a client layer stage using the given [[ClientConnectionSettings]]. + */ + def clientLayer(hostHeader: headers.Host, + settings: ClientConnectionSettings, + log: LoggingAdapter): BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, Unit] = + adaptClientLayer(delegate.clientLayer(JavaMapping.toScala(hostHeader), settings, log)) + /** * Creates a [[Flow]] representing a prospective HTTP client connection to the given endpoint. * Every materialization of the produced flow will attempt to establish a new outgoing connection. @@ -468,4 +527,14 @@ class Http(system: ExtendedActorSystem) extends akka.actor.Extension { implicit val _ = JavaMapping.identity[T] JavaMapping.toJava(scalaFlow)(JavaMapping.flowMapping[Pair[HttpRequest, T], (scaladsl.model.HttpRequest, T), Pair[Try[HttpResponse], T], (Try[scaladsl.model.HttpResponse], T), Mat]) } + + private def adaptServerLayer(serverLayer: scaladsl.Http.ServerLayer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, Unit] = + new BidiFlow( + JavaMapping.adapterBidiFlow[HttpResponse, sm.HttpResponse, sm.HttpRequest, HttpRequest] + .atop(serverLayer)) + + private def adaptClientLayer(clientLayer: scaladsl.Http.ClientLayer): BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, Unit] = + new BidiFlow( + JavaMapping.adapterBidiFlow[HttpRequest, sm.HttpRequest, sm.HttpResponse, HttpResponse] + .atop(clientLayer)) } diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala index 2c310f9930..ef6b246beb 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala @@ -150,7 +150,8 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E /** * Constructs a [[ServerLayer]] stage using the given [[ServerSettings]]. The returned [[BidiFlow]] isn't reusable and - * can only be materialized once. + * can only be materialized once. The `remoteAddress`, if provided, will be added as a header to each [[HttpRequest]] + * this layer produces if the `akka.http.server.remote-address-header` configuration option is enabled. */ def serverLayer(settings: ServerSettings, remoteAddress: Option[InetSocketAddress] = None,