FileCallback.kt
package com.hexagonkt.http.server.callbacks
import com.hexagonkt.core.logging.Logger
import com.hexagonkt.core.media.mediaTypeOfOrNull
import com.hexagonkt.core.require
import com.hexagonkt.http.model.ContentType
import com.hexagonkt.http.handlers.HttpContext
import java.io.File
/**
* Callback that resolves requests' path parameters to files based on a base file. Requests path
* parameters are not allowed to contain `..` (references to [file] parent directories are not
* permitted).
*
* If request does not have path parameters [file] will be returned (or not found if [file] is a
* directory).
*
* @param file Base file used to resolve paths passed on the request.
*/
class FileCallback(private val file: File) : (HttpContext) -> HttpContext {
constructor(file: String) : this(File(file))
private companion object {
val logger: Logger = Logger(FileCallback::class)
}
override fun invoke(context: HttpContext): HttpContext {
val file = when (context.pathParameters.size) {
0 -> file.absoluteFile
1 -> {
val relativePath = context.pathParameters.require("0")
check(!relativePath.contains("..")) {
"Requested path cannot contain '..': $relativePath"
}
file.resolve(relativePath).absoluteFile
}
else -> error("File loading require a single path parameter or none")
}
val fileName = file.name
logger.debug { "Resolving file: $fileName" }
return if (file.exists() && file.isFile) {
val bytes = file.readBytes()
val mediaType = mediaTypeOfOrNull(file)
context.ok(bytes, contentType = mediaType?.let { ContentType(it) })
}
else {
context.notFound("File '$fileName' not found")
}
}
}