Handlers.kt

  1. @file:Suppress("FunctionName") // Uppercase functions are used for providing named constructors

  2. package com.hexagonkt.http.handlers

  3. import com.hexagonkt.core.logging.Logger
  4. import com.hexagonkt.handlers.Context
  5. import com.hexagonkt.http.model.*
  6. import com.hexagonkt.http.model.HttpMethod.*
  7. import com.hexagonkt.http.model.HttpProtocol.HTTP
  8. import java.math.BigInteger
  9. import java.security.cert.X509Certificate

  10. typealias HttpCallbackType = HttpContext.() -> HttpContext
  11. typealias HttpExceptionCallbackType<T> = HttpContext.(T) -> HttpContext

  12. private val logger: Logger by lazy { Logger(HttpHandler::class.java.packageName) }
  13. private val BODY_TYPES_NAMES: String by lazy {
  14.     val bodyTypes = setOf(String::class, ByteArray::class, Int::class, Long::class)
  15.     bodyTypes.joinToString(", ") { it.simpleName.toString() }
  16. }

  17. internal fun toCallback(block: HttpCallbackType): (Context<HttpCall>) -> Context<HttpCall> =
  18.     { context -> HttpContext(context).block() }

  19. internal fun <E : Exception> toCallback(
  20.     block: HttpExceptionCallbackType<E>
  21. ): (Context<HttpCall>, E) -> Context<HttpCall> =
  22.     { context, e -> HttpContext(context).block(e) }

  23. fun HttpCallbackType.process(
  24.     request: HttpRequest,
  25.     attributes: Map<*, *> = emptyMap<Any, Any>()
  26. ): HttpContext =
  27.     this(HttpContext(request = request, attributes = attributes))

  28. fun HttpCallbackType.process(
  29.     method: HttpMethod = GET,
  30.     protocol: HttpProtocol = HTTP,
  31.     host: String = "localhost",
  32.     port: Int = 80,
  33.     path: String = "",
  34.     queryParameters: QueryParameters = QueryParameters(),
  35.     headers: Headers = Headers(),
  36.     body: Any = "",
  37.     parts: List<HttpPart> = emptyList(),
  38.     formParameters: FormParameters = FormParameters(),
  39.     cookies: List<Cookie> = emptyList(),
  40.     contentType: ContentType? = null,
  41.     certificateChain: List<X509Certificate> = emptyList(),
  42.     accept: List<ContentType> = emptyList(),
  43.     contentLength: Long = -1L,
  44.     attributes: Map<*, *> = emptyMap<Any, Any>(),
  45. ): HttpContext =
  46.     this.process(
  47.         HttpRequest(
  48.             method,
  49.             protocol,
  50.             host,
  51.             port,
  52.             path,
  53.             queryParameters,
  54.             headers,
  55.             body,
  56.             parts,
  57.             formParameters,
  58.             cookies,
  59.             contentType,
  60.             certificateChain,
  61.             accept,
  62.             contentLength,
  63.         ),
  64.         attributes,
  65.     )

  66. fun path(pattern: String = "", block: HandlerBuilder.() -> Unit): PathHandler {
  67.     val builder = HandlerBuilder()
  68.     builder.block()
  69.     return path(pattern, builder.handlers)
  70. }

  71. fun path(contextPath: String = "", handlers: List<HttpHandler>): PathHandler =
  72.     handlers
  73.         .let {
  74.             if (it.size == 1 && it[0] is PathHandler)
  75.                 (it[0] as PathHandler).addPrefix(contextPath) as PathHandler
  76.             else
  77.                 PathHandler(contextPath, it)
  78.         }

  79. fun Get(pattern: String = "", callback: HttpCallbackType): OnHandler =
  80.     OnHandler(GET, pattern, callback)

  81. fun Ws(pattern: String = "", callback: HttpCallbackType): OnHandler =
  82.     Get(pattern, callback)

  83. fun Head(pattern: String = "", callback: HttpCallbackType): OnHandler =
  84.     OnHandler(HEAD, pattern, callback)

  85. fun Post(pattern: String = "", callback: HttpCallbackType): OnHandler =
  86.     OnHandler(POST, pattern, callback)

  87. fun Put(pattern: String = "", callback: HttpCallbackType): OnHandler =
  88.     OnHandler(PUT, pattern, callback)

  89. fun Delete(pattern: String = "", callback: HttpCallbackType): OnHandler =
  90.     OnHandler(DELETE, pattern, callback)

  91. fun Trace(pattern: String = "", callback: HttpCallbackType): OnHandler =
  92.     OnHandler(TRACE, pattern, callback)

  93. fun Options(pattern: String = "", callback: HttpCallbackType): OnHandler =
  94.     OnHandler(OPTIONS, pattern, callback)

  95. fun Patch(pattern: String = "", callback: HttpCallbackType): OnHandler =
  96.     OnHandler(PATCH, pattern, callback)

  97. fun bodyToBytes(body: Any): ByteArray =
  98.     when (body) {
  99.         is String -> body.toByteArray()
  100.         is ByteArray -> body
  101.         is Int -> BigInteger.valueOf(body.toLong()).toByteArray()
  102.         is Long -> BigInteger.valueOf(body).toByteArray()
  103.         else -> {
  104.             val className = body.javaClass.simpleName
  105.             val message = "Unsupported body type: $className. Must be: $BODY_TYPES_NAMES"
  106.             val exception = IllegalStateException(message)

  107.             logger.error(exception)
  108.             throw exception
  109.         }
  110.     }