Handlers.kt
@file:Suppress("FunctionName") // Uppercase functions are used for providing named constructors
package com.hexagonkt.http.handlers
import com.hexagonkt.core.logging.Logger
import com.hexagonkt.handlers.Context
import com.hexagonkt.http.model.*
import com.hexagonkt.http.model.HttpMethod.*
import com.hexagonkt.http.model.HttpProtocol.HTTP
import java.math.BigInteger
import java.security.cert.X509Certificate
typealias HttpCallbackType = HttpContext.() -> HttpContext
typealias HttpExceptionCallbackType<T> = HttpContext.(T) -> HttpContext
private val logger: Logger by lazy { Logger(HttpHandler::class.java.packageName) }
private val BODY_TYPES_NAMES: String by lazy {
val bodyTypes = setOf(String::class, ByteArray::class, Int::class, Long::class)
bodyTypes.joinToString(", ") { it.simpleName.toString() }
}
internal fun toCallback(block: HttpCallbackType): (Context<HttpCall>) -> Context<HttpCall> =
{ context -> HttpContext(context).block() }
internal fun <E : Exception> toCallback(
block: HttpExceptionCallbackType<E>
): (Context<HttpCall>, E) -> Context<HttpCall> =
{ context, e -> HttpContext(context).block(e) }
fun HttpCallbackType.process(
request: HttpRequest,
attributes: Map<*, *> = emptyMap<Any, Any>()
): HttpContext =
this(HttpContext(request = request, attributes = attributes))
fun HttpCallbackType.process(
method: HttpMethod = GET,
protocol: HttpProtocol = HTTP,
host: String = "localhost",
port: Int = 80,
path: String = "",
queryParameters: QueryParameters = QueryParameters(),
headers: Headers = Headers(),
body: Any = "",
parts: List<HttpPart> = emptyList(),
formParameters: FormParameters = FormParameters(),
cookies: List<Cookie> = emptyList(),
contentType: ContentType? = null,
certificateChain: List<X509Certificate> = emptyList(),
accept: List<ContentType> = emptyList(),
contentLength: Long = -1L,
attributes: Map<*, *> = emptyMap<Any, Any>(),
): HttpContext =
this.process(
HttpRequest(
method,
protocol,
host,
port,
path,
queryParameters,
headers,
body,
parts,
formParameters,
cookies,
contentType,
certificateChain,
accept,
contentLength,
),
attributes,
)
fun path(pattern: String = "", block: HandlerBuilder.() -> Unit): PathHandler {
val builder = HandlerBuilder()
builder.block()
return path(pattern, builder.handlers)
}
fun path(contextPath: String = "", handlers: List<HttpHandler>): PathHandler =
handlers
.let {
if (it.size == 1 && it[0] is PathHandler)
(it[0] as PathHandler).addPrefix(contextPath) as PathHandler
else
PathHandler(contextPath, it)
}
fun Get(pattern: String = "", callback: HttpCallbackType): OnHandler =
OnHandler(GET, pattern, callback)
fun Ws(pattern: String = "", callback: HttpCallbackType): OnHandler =
Get(pattern, callback)
fun Head(pattern: String = "", callback: HttpCallbackType): OnHandler =
OnHandler(HEAD, pattern, callback)
fun Post(pattern: String = "", callback: HttpCallbackType): OnHandler =
OnHandler(POST, pattern, callback)
fun Put(pattern: String = "", callback: HttpCallbackType): OnHandler =
OnHandler(PUT, pattern, callback)
fun Delete(pattern: String = "", callback: HttpCallbackType): OnHandler =
OnHandler(DELETE, pattern, callback)
fun Trace(pattern: String = "", callback: HttpCallbackType): OnHandler =
OnHandler(TRACE, pattern, callback)
fun Options(pattern: String = "", callback: HttpCallbackType): OnHandler =
OnHandler(OPTIONS, pattern, callback)
fun Patch(pattern: String = "", callback: HttpCallbackType): OnHandler =
OnHandler(PATCH, pattern, callback)
fun bodyToBytes(body: Any): ByteArray =
when (body) {
is String -> body.toByteArray()
is ByteArray -> body
is Int -> BigInteger.valueOf(body.toLong()).toByteArray()
is Long -> BigInteger.valueOf(body).toByteArray()
else -> {
val className = body.javaClass.simpleName
val message = "Unsupported body type: $className. Must be: $BODY_TYPES_NAMES"
val exception = IllegalStateException(message)
logger.error(exception)
throw exception
}
}