GoogleHttpFunction.kt

package com.hexagontk.serverless.http.google

import com.hexagontk.http.handlers.bodyToBytes
import com.google.cloud.functions.HttpFunction
import com.google.cloud.functions.HttpRequest
import com.google.cloud.functions.HttpResponse
import com.hexagontk.core.media.TEXT_PLAIN
import com.hexagontk.core.toText
import com.hexagontk.http.handlers.HttpContext
import com.hexagontk.http.handlers.HttpHandler
import com.hexagontk.http.model.*
import com.hexagontk.http.parseContentType
import java.net.URI

class GoogleHttpFunction(private val handler: HttpHandler): HttpFunction {

    override fun service(request: HttpRequest, response: HttpResponse) {
        val handlerRequest = createRequest(request)
        val handlerContext = handler.process(handlerRequest)
        writeResponse(response, handlerContext)
    }

    private fun createRequest(request: HttpRequest): com.hexagontk.http.model.HttpRequest {
        val uri = URI(request.uri)
        val qp =
            request.queryParameters?.flatMap { (k, v) -> v.map { Parameter(k, it) } } ?: emptyList()
        val h = request.headers?.flatMap { (k, v) -> v.map { Header(k, it) } } ?: emptyList()
        val contentType = request.contentType.map { parseContentType(it) }.orElse(null)
        val accept = request.headers["accept"]?.map { parseContentType(it) } ?: emptyList()
        val authorization = request.headers["authorization"]
            ?.first()
            ?.split(" ", limit = 2)
            ?.let { Authorization(it.first(), it.last()) }

        // TODO Fix parts upload
        val parts = emptyList<HttpPart>()
//            if (request.contentType.orElse("").contains("multipart")) {
//                request.parts.map { (k, v) ->
//                    HttpPart(
//                        name = k,
//                        body = v.inputStream.readAllBytes(),
//                        headers = Headers(),
//                        contentType = v.contentType?.map { parseContentType(it) }?.orElse(null),
//                        size = v.contentLength,
//                        submittedFileName = v.fileName.orElse(null),
//                    )
//                }
//            }
//            else emptyList()

        return HttpRequest(
            method = HttpMethod.valueOf(request.method),
            protocol = HttpProtocol.valueOf(uri.scheme.uppercase()),
            host = uri.host,
            port = uri.port,
            path = request.path,
            queryParameters = Parameters(qp),
            headers = Headers(h),
            body = request.inputStream.readAllBytes() ?: ByteArray(0),
            parts = parts,
            contentType = contentType,
            accept = accept,
            contentLength = request.contentLength,
            authorization = authorization,
        )
    }

    private fun writeResponse(response: HttpResponse, context: HttpContext) {
        val handlerResponse = context.response

        handlerResponse.contentType?.text?.let(response::setContentType)
        handlerResponse.headers.forEach { response.appendHeader(it.name, it.text) }
        response.setStatusCode(handlerResponse.status)

        try {
            response.outputStream.write(bodyToBytes(handlerResponse.body))
        }
        catch (e: Exception) {
            response.setContentType(TEXT_PLAIN.fullType)
            response.setStatusCode(500)
            response.outputStream.write(bodyToBytes(e.toText()))
        }
    }
}