HttpContext.kt
- package com.hexagonkt.http.handlers
- import com.hexagonkt.handlers.Context
- import com.hexagonkt.core.assertEnabled
- import com.hexagonkt.core.media.TEXT_EVENT_STREAM
- import com.hexagonkt.core.media.TEXT_PLAIN
- import com.hexagonkt.core.toText
- import com.hexagonkt.handlers.Handler
- import com.hexagonkt.http.model.*
- import com.hexagonkt.http.model.INTERNAL_SERVER_ERROR_500
- import com.hexagonkt.http.model.ServerEvent
- import com.hexagonkt.http.model.ws.WsSession
- import java.net.URL
- import java.security.cert.X509Certificate
- import java.util.concurrent.Flow.Publisher
- // TODO Add exception parameter to 'send*' methods
- data class HttpContext(
- override val event: HttpCall,
- override val predicate: (Context<HttpCall>) -> Boolean,
- override val nextHandlers: List<Handler<HttpCall>> = emptyList(),
- override val nextHandler: Int = 0,
- override val exception: Exception? = null,
- override val attributes: Map<*, *> = emptyMap<Any, Any>(),
- override val handled: Boolean = false,
- ): Context<HttpCall> {
- val request: HttpRequestPort = event.request
- val response: HttpResponsePort = event.response
- val method: HttpMethod by lazy { request.method }
- val protocol: HttpProtocol by lazy { request.protocol }
- val host: String by lazy { request.host }
- val port: Int by lazy { request.port }
- val path: String by lazy { request.path }
- val queryParameters: QueryParameters by lazy { request.queryParameters }
- val parts: List<HttpPart> by lazy { request.parts }
- val formParameters: FormParameters by lazy { request.formParameters }
- val accept: List<ContentType> by lazy { request.accept }
- val authorization: Authorization? by lazy { request.authorization }
- val certificateChain: List<X509Certificate> by lazy { request.certificateChain }
- val partsMap: Map<String, HttpPart> by lazy { request.partsMap() }
- val url: URL by lazy { request.url() }
- val userAgent: String? by lazy { request.userAgent() }
- val referer: String? by lazy { request.referer() }
- val origin: String? by lazy { request.origin() }
- val certificate: X509Certificate? by lazy { request.certificate() }
- val status: HttpStatus = response.status
- val pathParameters: Map<String, String> by lazy {
- val httpHandler = predicate as HttpPredicate
- val pattern = httpHandler.pathPattern
- if (assertEnabled)
- check(!pattern.prefix) { "Loading path parameters not allowed for paths" }
- pattern.extractParameters(request.path)
- }
- constructor(context: Context<HttpCall>) : this(
- event = context.event,
- predicate = context.predicate,
- nextHandlers = context.nextHandlers,
- nextHandler = context.nextHandler,
- exception = context.exception,
- attributes = context.attributes,
- )
- constructor(
- request: HttpRequestPort = HttpRequest(),
- response: HttpResponsePort = HttpResponse(),
- predicate: HttpPredicate = HttpPredicate(),
- attributes: Map<*, *> = emptyMap<Any, Any>(),
- ) : this(HttpCall(request, response), predicate, attributes = attributes)
- override fun next(): HttpContext {
- for (index in nextHandler until nextHandlers.size) {
- val handler = nextHandlers[index]
- val p = handler.predicate
- if (handler is OnHandler) {
- if ((!handled) && p(this))
- return handler.process(with(predicate = p, nextHandler = index + 1)) as HttpContext
- }
- else {
- if (p(this))
- return handler.process(with(predicate = p, nextHandler = index + 1)) as HttpContext
- }
- }
- return this
- }
- override fun with(
- event: HttpCall,
- predicate: (Context<HttpCall>) -> Boolean,
- nextHandlers: List<Handler<HttpCall>>,
- nextHandler: Int,
- exception: Exception?,
- attributes: Map<*, *>,
- handled: Boolean,
- ): Context<HttpCall> =
- copy(
- event = event,
- predicate = predicate,
- nextHandlers = nextHandlers,
- nextHandler = nextHandler,
- exception = exception,
- attributes = attributes,
- handled = handled,
- )
- fun unauthorized(
- body: Any = response.body,
- headers: Headers = response.headers,
- contentType: ContentType? = response.contentType,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(UNAUTHORIZED_401, body, headers, contentType, cookies, attributes)
- fun forbidden(
- body: Any = response.body,
- headers: Headers = response.headers,
- contentType: ContentType? = response.contentType,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(FORBIDDEN_403, body, headers, contentType, cookies, attributes)
- fun internalServerError(
- body: Any = response.body,
- headers: Headers = response.headers,
- contentType: ContentType? = response.contentType,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(INTERNAL_SERVER_ERROR_500, body, headers, contentType, cookies, attributes)
- fun serverError(
- status: HttpStatus,
- exception: Exception,
- headers: Headers = response.headers,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(
- status = status,
- body = exception.toText(),
- headers = headers,
- contentType = ContentType(TEXT_PLAIN),
- attributes = attributes,
- )
- fun internalServerError(
- exception: Exception,
- headers: Headers = response.headers,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- serverError(INTERNAL_SERVER_ERROR_500, exception, headers, attributes)
- fun ok(
- body: Any = response.body,
- headers: Headers = response.headers,
- contentType: ContentType? = response.contentType,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(OK_200, body, headers, contentType, cookies, attributes)
- fun sse(body: Publisher<ServerEvent>): HttpContext =
- ok(
- body = body,
- headers = response.headers + Header("cache-control", "no-cache"),
- contentType = ContentType(TEXT_EVENT_STREAM)
- )
- fun badRequest(
- body: Any = response.body,
- headers: Headers = response.headers,
- contentType: ContentType? = response.contentType,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(BAD_REQUEST_400, body, headers, contentType, cookies, attributes)
- fun notFound(
- body: Any = response.body,
- headers: Headers = response.headers,
- contentType: ContentType? = response.contentType,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(NOT_FOUND_404, body, headers, contentType, cookies, attributes)
- fun created(
- body: Any = response.body,
- headers: Headers = response.headers,
- contentType: ContentType? = response.contentType,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(CREATED_201, body, headers, contentType, cookies, attributes)
- fun redirect(
- status: HttpStatus,
- location: String,
- headers: Headers = response.headers,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(
- status,
- headers = headers + Header("location", location),
- cookies = cookies,
- attributes = attributes
- )
- fun found(
- location: String,
- headers: Headers = response.headers,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- redirect(FOUND_302, location, headers, cookies, attributes)
- fun accepted(
- onConnect: WsSession.() -> Unit = {},
- onBinary: WsSession.(data: ByteArray) -> Unit = {},
- onText: WsSession.(text: String) -> Unit = {},
- onPing: WsSession.(data: ByteArray) -> Unit = {},
- onPong: WsSession.(data: ByteArray) -> Unit = {},
- onClose: WsSession.(status: Int, reason: String) -> Unit = { _, _ -> },
- ): HttpContext =
- send(
- event.response.with(
- status = ACCEPTED_202,
- onConnect = onConnect,
- onBinary = onBinary,
- onText = onText,
- onPing = onPing,
- onPong = onPong,
- onClose = onClose,
- )
- )
- fun send(
- status: HttpStatus = response.status,
- body: Any = response.body,
- headers: Headers = response.headers,
- contentType: ContentType? = response.contentType,
- cookies: List<Cookie> = response.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(
- response.with(
- body = body,
- headers = headers,
- contentType = contentType,
- cookies = cookies,
- status = status,
- ),
- attributes
- )
- fun send(response: HttpResponsePort, attributes: Map<*, *> = this.attributes): HttpContext =
- copy(
- event = event.copy(response = response),
- attributes = attributes
- )
- fun send(request: HttpRequestPort, attributes: Map<*, *> = this.attributes): HttpContext =
- copy(
- event = event.copy(request = request),
- attributes = attributes
- )
- fun receive(
- body: Any = request.body,
- headers: Headers = request.headers,
- contentType: ContentType? = request.contentType,
- accept: List<ContentType> = request.accept,
- cookies: List<Cookie> = request.cookies,
- attributes: Map<*, *> = this.attributes,
- ): HttpContext =
- send(
- request.with(
- body = body,
- headers = headers,
- contentType = contentType,
- accept = accept,
- cookies = cookies,
- ),
- attributes
- )
- }