Skip to content

Commit c81bfc0

Browse files
Added remote address in ConnectionInfo for pekko (#4847)
Relatively simple implementation of ConnectionInfo.remote and ConnectionInfo.secure for pekko-http. Maybe better than nothing. I am open to suggestions, but do not see how I can do it better with pekko now. One small step would be to use HostDirectives.extractHost in PekkoHttpServerInterpreter - for a better a local connection info.
1 parent 139c7f8 commit c81bfc0

File tree

3 files changed

+37
-4
lines changed

3 files changed

+37
-4
lines changed

server/pekko-http-server/src/main/scala/sttp/tapir/server/pekkohttp/PekkoServerRequest.scala

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,35 @@
11
package sttp.tapir.server.pekkohttp
22

3-
import org.apache.pekko.http.scaladsl.model.{Uri => PekkoUri}
3+
import org.apache.pekko.http.scaladsl.model.{AttributeKeys, Uri => PekkoUri}
44
import org.apache.pekko.http.scaladsl.server.RequestContext
55
import sttp.model.Uri.{Authority, FragmentSegment, HostSegment, PathSegments, QuerySegment}
66
import sttp.model.{Header, HeaderNames, Method, QueryParams, Uri}
77
import sttp.tapir.model.{ConnectionInfo, ServerRequest}
88
import sttp.tapir.{AttributeKey, AttributeMap}
99

10+
import java.net.InetSocketAddress
1011
import scala.annotation.tailrec
1112
import scala.collection.immutable.Seq
1213

1314
private[pekkohttp] case class PekkoServerRequest(ctx: RequestContext, attributes: AttributeMap = AttributeMap.Empty) extends ServerRequest {
1415
override def protocol: String = ctx.request.protocol.value
15-
override lazy val connectionInfo: ConnectionInfo = ConnectionInfo(None, None, None)
16+
private lazy val remote = ctx
17+
.request
18+
.attribute(AttributeKeys.remoteAddress)
19+
.flatMap(_.toIP)
20+
21+
override def connectionInfo: ConnectionInfo = {
22+
// simple / naive deduction of secure property, as pekko uses only this 4 schemes
23+
// @see org.apache.pekko.http.scaladsl.model.HttpRequest#verifyUri
24+
val secure = ctx.request.uri.scheme match {
25+
case "https" | "wss" => Some(true)
26+
case "http" | "ws" => Some(false)
27+
case _ => None
28+
}
29+
ConnectionInfo(None, remote.map( addr =>
30+
new InetSocketAddress(addr.ip, addr.port.getOrElse(0))
31+
) , secure)
32+
}
1633
override def underlying: Any = ctx
1734

1835
override lazy val pathSegments: List[String] = {

server/pekko-http-server/src/test/scala/sttp/tapir/server/pekkohttp/PekkoHttpServerTest.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,21 @@ class PekkoHttpServerTest extends TestSuite with EitherValues {
163163
basicRequest.get(uri"http://localhost:$port/api/custom_code").send(backend).map(_.code shouldBe StatusCode(800))
164164
}
165165
.unsafeToFuture()
166-
}
166+
},
167+
Test("Server reads remote address and secure from connectionInfo") {
168+
val e = endpoint.get.in("test").in(extractFromRequest(req => req.connectionInfo) ).out(stringBody)
169+
.serverLogic{connectionInfo =>
170+
val remote = connectionInfo.remote
171+
val secure = connectionInfo.secure
172+
s"$remote $secure".asRight[Unit].unit}
173+
val route = Directives.pathPrefix("api")(PekkoHttpServerInterpreter().toRoute(e))
174+
interpreter
175+
.server(NonEmptyList.of(route))
176+
.use { port =>
177+
basicRequest.get(uri"http://localhost:$port/api/test").send(backend).map(_.body.value should fullyMatch regex """Some\(/127\.0\.0\.1:\d+\) Some\(false\)""")
178+
}
179+
.unsafeToFuture()
180+
},
167181
)
168182
def drainPekko(stream: PekkoStreams.BinaryStream): Future[Unit] =
169183
stream.runWith(Sink.ignore).map(_ => ())

server/pekko-http-server/src/test/scala/sttp/tapir/server/pekkohttp/PekkoHttpTestServerInterpreter.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ class PekkoHttpTestServerInterpreter(implicit actorSystem: ActorSystem)
2727
routes: NonEmptyList[Route],
2828
gracefulShutdownTimeout: Option[FiniteDuration]
2929
): Resource[IO, Port] = {
30-
val bind = IO.fromFuture(IO(Http().newServerAt("localhost", 0).bind(concat(routes.toList: _*))))
30+
val bind = IO.fromFuture(IO(Http().newServerAt("localhost", 0)
31+
.adaptSettings( setts => setts.withRemoteAddressAttribute(true))
32+
.bind(concat(routes.toList: _*))))
3133

3234
Resource
3335
.make(bind)(server => IO.fromFuture(IO(server.terminate(gracefulShutdownTimeout.getOrElse(50.millis)))).void)

0 commit comments

Comments
 (0)