FileCallback.kt

  1. package com.hexagonkt.http.server.callbacks

  2. import com.hexagonkt.core.logging.Logger
  3. import com.hexagonkt.core.media.mediaTypeOfOrNull
  4. import com.hexagonkt.core.require
  5. import com.hexagonkt.http.model.ContentType
  6. import com.hexagonkt.http.handlers.HttpContext
  7. import java.io.File

  8. /**
  9.  * Callback that resolves requests' path parameters to files based on a base file. Requests path
  10.  * parameters are not allowed to contain `..` (references to [file] parent directories are not
  11.  * permitted).
  12.  *
  13.  * If request does not have path parameters [file] will be returned (or not found if [file] is a
  14.  * directory).
  15.  *
  16.  * @param file Base file used to resolve paths passed on the request.
  17.  */
  18. class FileCallback(private val file: File) : (HttpContext) -> HttpContext {

  19.     constructor(file: String) : this(File(file))

  20.     private companion object {
  21.         val logger: Logger = Logger(FileCallback::class)
  22.     }

  23.     override fun invoke(context: HttpContext): HttpContext {
  24.         val file = when (context.pathParameters.size) {
  25.             0 -> file.absoluteFile
  26.             1 -> {
  27.                 val relativePath = context.pathParameters.require("0")
  28.                 check(!relativePath.contains("..")) {
  29.                     "Requested path cannot contain '..': $relativePath"
  30.                 }
  31.                 file.resolve(relativePath).absoluteFile
  32.             }
  33.             else -> error("File loading require a single path parameter or none")
  34.         }
  35.         val fileName = file.name

  36.         logger.debug { "Resolving file: $fileName" }

  37.         return if (file.exists() && file.isFile) {
  38.             val bytes = file.readBytes()
  39.             val mediaType = mediaTypeOfOrNull(file)
  40.             context.ok(bytes, contentType = mediaType?.let { ContentType(it) })
  41.         }
  42.         else {
  43.             context.notFound("File '$fileName' not found")
  44.         }
  45.     }
  46. }