NettyRequestAdapter.kt

  1. package com.hexagonkt.http.server.netty

  2. import com.hexagonkt.http.model.*
  3. import com.hexagonkt.http.model.Headers
  4. import com.hexagonkt.http.model.Headers as HxHttpHeaders
  5. import com.hexagonkt.http.parseContentType
  6. import io.netty.buffer.ByteBufHolder
  7. import io.netty.buffer.ByteBufUtil
  8. import io.netty.buffer.Unpooled
  9. import io.netty.channel.Channel
  10. import io.netty.handler.codec.http.HttpHeaderNames.*
  11. import io.netty.handler.codec.http.HttpHeaders
  12. import io.netty.handler.codec.http.QueryStringDecoder
  13. import io.netty.handler.codec.http.cookie.Cookie
  14. import io.netty.handler.codec.http.cookie.ServerCookieDecoder
  15. import io.netty.handler.codec.http.multipart.*
  16. import io.netty.handler.codec.http.HttpRequest
  17. import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite.*
  18. import io.netty.handler.codec.http.cookie.DefaultCookie
  19. import java.net.InetSocketAddress
  20. import java.net.URI
  21. import java.security.cert.X509Certificate
  22. import kotlin.Long.Companion.MIN_VALUE
  23. import io.netty.handler.codec.http.HttpMethod as NettyHttpMethod

  24. class NettyRequestAdapter(
  25.     methodName: NettyHttpMethod,
  26.     req: HttpRequest,
  27.     override val certificateChain: List<X509Certificate>,
  28.     channel: Channel,
  29.     nettyHeaders: HttpHeaders,
  30. ) : HttpRequestPort {

  31.     private val address: InetSocketAddress by lazy { channel.remoteAddress() as InetSocketAddress }

  32.     override val accept: List<ContentType> by lazy {
  33.         nettyHeaders.getAll(ACCEPT).flatMap { it.split(",") }.map { parseContentType(it) }
  34.     }

  35.     override val contentLength: Long by lazy {
  36.         nettyHeaders[CONTENT_LENGTH]?.toLong() ?: 0L
  37.     }

  38.     override val queryParameters: QueryParameters by lazy {
  39.         val queryStringDecoder = QueryStringDecoder(req.uri())
  40.         QueryParameters(queryStringDecoder.parameters().map { (k, v) -> QueryParameter(k, v) })
  41.     }

  42.     override val parts: List<HttpPart> by lazy {
  43.         HttpPostRequestDecoder(req).bodyHttpDatas.map {
  44.             when (it) {
  45.                 is FileUpload -> HttpPart(
  46.                     name = it.name,
  47.                     body = ByteBufUtil.getBytes(it.content()),
  48.                     submittedFileName = it.filename,
  49.                     contentType = it.contentType?.let { ct -> parseContentType(ct) },
  50.                 )
  51.                 is Attribute -> HttpPart(it.name, it.value)
  52.                 else -> error("Unknown part type: ${it.javaClass}")
  53.             }
  54.         }
  55.     }

  56.     override val formParameters: FormParameters by lazy {
  57.         val fields = parts
  58.             .filter { it.submittedFileName == null }
  59.             .groupBy { it.name }
  60.             .mapValues { it.value.map { v -> v.bodyString() } }
  61.             .map { (k, v) -> FormParameter(k, v) }

  62.         FormParameters(fields)
  63.     }

  64.     override val method: HttpMethod by lazy {
  65.         HttpMethod.valueOf(methodName.name())
  66.     }

  67.     override val protocol: HttpProtocol by lazy {
  68.         HttpProtocol.valueOf(req.protocolVersion().protocolName())
  69.     }

  70.     override val host: String by lazy {
  71.         address.hostName
  72.     }

  73.     override val port: Int by lazy {
  74.         address.port
  75.     }

  76.     override val path: String by lazy { URI(req.uri()).path }

  77.     override val cookies: List<com.hexagonkt.http.model.Cookie> by lazy {
  78.         val cookieHeader: String = nettyHeaders.get(COOKIE)
  79.             ?: return@lazy emptyList<com.hexagonkt.http.model.Cookie>()

  80.         val cookies: Set<Cookie> = ServerCookieDecoder.STRICT.decode(cookieHeader)

  81.         cookies.map {
  82.             Cookie(
  83.                 name = it.name(),
  84.                 value = it.value(),
  85.                 maxAge = if (it.maxAge() == MIN_VALUE) -1 else it.maxAge(),
  86.                 secure = it.isSecure,
  87.                 path = it.path() ?: "/",
  88.                 httpOnly = it.isHttpOnly,
  89.                 sameSite = (it as? DefaultCookie)?.sameSite()?.let { ss ->
  90.                     when (ss) {
  91.                         Strict -> CookieSameSite.STRICT
  92.                         Lax -> CookieSameSite.LAX
  93.                         None -> CookieSameSite.NONE
  94.                     }
  95.                 },
  96.                 domain = it.domain(),
  97.             )
  98.         }
  99.     }

  100.     override val body: Any by lazy {
  101.         val content =
  102.             if (req is ByteBufHolder) req.content()
  103.             else Unpooled.buffer(0)

  104.         if (content.isReadable) ByteBufUtil.getBytes(content)
  105.         else byteArrayOf()
  106.     }

  107.     override val headers: HxHttpHeaders by lazy {
  108.         HxHttpHeaders(
  109.             nettyHeaders.names()
  110.                 .toList()
  111.                 .map { it.lowercase() }
  112.                 .map { Header(it, nettyHeaders.getAll(it)) }
  113.         )
  114.     }

  115.     override val contentType: ContentType? by lazy {
  116.         nettyHeaders[CONTENT_TYPE]?.let { parseContentType(it) }
  117.     }

  118.     override val authorization: Authorization? by lazy { authorization() }

  119.     override fun with(
  120.         body: Any,
  121.         headers: Headers,
  122.         contentType: ContentType?,
  123.         method: HttpMethod,
  124.         protocol: HttpProtocol,
  125.         host: String,
  126.         port: Int,
  127.         path: String,
  128.         queryParameters: QueryParameters,
  129.         parts: List<HttpPart>,
  130.         formParameters: FormParameters,
  131.         cookies: List<com.hexagonkt.http.model.Cookie>,
  132.         accept: List<ContentType>,
  133.         authorization: Authorization?,
  134.         certificateChain: List<X509Certificate>
  135.     ): HttpRequestPort =
  136.         throw UnsupportedOperationException()
  137. }