Skip to content

Commit

Permalink
Optimize server request .uri for pekko-http and akka-http (#3566)
Browse files Browse the repository at this point in the history
  • Loading branch information
kciesielski authored Mar 5, 2024
1 parent db25199 commit 06575d9
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sttp.tapir.server.akkahttp
import akka.http.scaladsl.server.RequestContext
import akka.http.scaladsl.model.headers.{`Content-Length`, `Content-Type`}
import akka.http.scaladsl.model.{Uri => AkkaUri}
import sttp.model.Uri.{Authority, FragmentSegment, HostSegment, PathSegments, QuerySegment}
import sttp.model.{Header, Method, QueryParams, Uri}
import sttp.tapir.{AttributeKey, AttributeMap}
import sttp.tapir.model.{ConnectionInfo, ServerRequest}
Expand All @@ -27,7 +28,35 @@ private[akkahttp] case class AkkaServerRequest(ctx: RequestContext, attributes:
}
override lazy val queryParameters: QueryParams = QueryParams.fromMultiMap(ctx.request.uri.query().toMultiMap)
override lazy val method: Method = Method(ctx.request.method.value.toUpperCase)
override lazy val uri: Uri = Uri.unsafeParse(ctx.request.uri.toString())

private def queryToSegments(query: AkkaUri.Query): List[QuerySegment] = {
@tailrec
def run(q: AkkaUri.Query, acc: List[QuerySegment]): List[QuerySegment] = q match {
case AkkaUri.Query.Cons(k, v, tail) => {
if (k.isEmpty)
run(tail, QuerySegment.Value(v) :: acc)
else if (v.isEmpty)
run(tail, QuerySegment.Value(k) :: acc)
else
run(tail, QuerySegment.KeyValue(k, v) :: acc)
}
case AkkaUri.Query.Empty => acc.reverse
}
run(query, Nil)
}

override lazy val uri: Uri = {
val pekkoUri = ctx.request.uri
Uri(
Some(pekkoUri.scheme),
// UserInfo is available only as a raw string, but we can skip it as it's not needed
Some(Authority(userInfo = None, HostSegment(pekkoUri.authority.host.address), Some(pekkoUri.effectivePort))),
PathSegments.absoluteOrEmptyS(pathSegments ++ (if (pekkoUri.path.endsWithSlash) Seq("") else Nil)),
queryToSegments(ctx.request.uri.query()),
ctx.request.uri.fragment.map(f => FragmentSegment(f))
)
}


private val EmptyContentType = "none/none"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package sttp.tapir.server.pekkohttp

import org.apache.pekko.http.scaladsl.server.RequestContext
import org.apache.pekko.http.scaladsl.model.{Uri => PekkoUri}
import org.apache.pekko.http.scaladsl.server.RequestContext
import sttp.model.Uri.{Authority, FragmentSegment, HostSegment, PathSegments, QuerySegment}
import sttp.model.{Header, HeaderNames, Method, QueryParams, Uri}
import sttp.tapir.{AttributeKey, AttributeMap}
import sttp.tapir.model.{ConnectionInfo, ServerRequest}
import sttp.tapir.{AttributeKey, AttributeMap}

import scala.annotation.tailrec
import scala.collection.immutable.Seq
Expand All @@ -26,7 +27,34 @@ private[pekkohttp] case class PekkoServerRequest(ctx: RequestContext, attributes
}
override lazy val queryParameters: QueryParams = QueryParams.fromMultiMap(ctx.request.uri.query().toMultiMap)
override lazy val method: Method = Method(ctx.request.method.value.toUpperCase)
override lazy val uri: Uri = Uri.unsafeParse(ctx.request.uri.toString())

private def queryToSegments(query: PekkoUri.Query): List[QuerySegment] = {
@tailrec
def run(q: PekkoUri.Query, acc: List[QuerySegment]): List[QuerySegment] = q match {
case PekkoUri.Query.Cons(k, v, tail) => {
if (k.isEmpty)
run(tail, QuerySegment.Value(v) :: acc)
else if (v.isEmpty)
run(tail, QuerySegment.Value(k) :: acc)
else
run(tail, QuerySegment.KeyValue(k, v) :: acc)
}
case PekkoUri.Query.Empty => acc.reverse
}
run(query, Nil)
}

override lazy val uri: Uri = {
val pekkoUri = ctx.request.uri
Uri(
Some(pekkoUri.scheme),
// UserInfo is available only as a raw string, but we can skip it as it's not needed
Some(Authority(userInfo = None, HostSegment(pekkoUri.authority.host.address), Some(pekkoUri.effectivePort))),
PathSegments.absoluteOrEmptyS(pathSegments ++ (if (pekkoUri.path.endsWithSlash) Seq("") else Nil)),
queryToSegments(ctx.request.uri.query()),
ctx.request.uri.fragment.map(f => FragmentSegment(f))
)
}

private val EmptyContentType = "none/none"

Expand Down

0 comments on commit 06575d9

Please sign in to comment.