Handlers.kt

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

  2. package com.hexagontk.http.handlers

  3. import com.hexagontk.handlers.Context
  4. import com.hexagontk.http.model.*
  5. import com.hexagontk.http.model.HttpMethod.*
  6. import com.hexagontk.http.model.HttpProtocol.HTTP
  7. import java.math.BigInteger
  8. import java.security.cert.X509Certificate

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

  11. private val BODY_TYPES_NAMES: String by lazy {
  12.     val bodyTypes = setOf(String::class, ByteArray::class, Int::class, Long::class)
  13.     bodyTypes.joinToString(", ") { it.simpleName.toString() }
  14. }

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

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

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

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

  66. // TODO rename to 'buildPath' to follow same pattern as Kotlin's 'buildList' or 'buildMap'???
  67. fun path(pattern: String = "", block: HandlerBuilder.() -> Unit): PathHandler {
  68.     val builder = HandlerBuilder()
  69.     builder.block()
  70.     return path(pattern, builder.handlers)
  71. }

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

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

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

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

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

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

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

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

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

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

  98. /**
  99.  * Convert an object to a byte array. If the body class is not supported, it throws a runtime
  100.  * exception. IMPORTANT: Must be enclosed on a try/catch block to check runtime errors.
  101.  *
  102.  * @param body Object to convert to bytes.
  103.  * @return The bytes of the supplied object.
  104.  */
  105. fun bodyToBytes(body: Any): ByteArray =
  106.     when (body) {
  107.         is String -> body.toByteArray()
  108.         is ByteArray -> body
  109.         is Int -> BigInteger.valueOf(body.toLong()).toByteArray()
  110.         is Long -> BigInteger.valueOf(body).toByteArray()
  111.         else -> {
  112.             val className = body.javaClass.simpleName
  113.             val message = "Unsupported body type: $className. Must be: $BODY_TYPES_NAMES"

  114.             throw IllegalStateException(message)
  115.         }
  116.     }