Skip to content
This repository has been archived by the owner on Jul 29, 2022. It is now read-only.

Commit

Permalink
Refactor HTTP handlers to make them more generic
Browse files Browse the repository at this point in the history
Simplify serving files from the application's assets
  • Loading branch information
mickael-menu committed May 25, 2020
1 parent 8871956 commit 06756c0
Show file tree
Hide file tree
Showing 15 changed files with 486 additions and 470 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import org.json.JSONArray
import org.json.JSONObject
import org.readium.r2.shared.Injectable
import org.readium.r2.shared.ReadiumCSSName
import org.readium.r2.shared.publication.ContentLayout
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.publication.epub.EpubLayout
import org.readium.r2.shared.publication.epub.layout
Expand Down Expand Up @@ -85,16 +86,16 @@ internal class ContentFiltersEpub(private val userPropertiesPath: String?, priva
if (endHeadIndex == -1)
return stream

val cssStyle = publication.cssStyle
val contentLayout = publication.contentLayout

val endIncludes = mutableListOf<String>()
val beginIncludes = mutableListOf<String>()
beginIncludes.add("<meta name=\"viewport\" content=\"width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\" />")

beginIncludes.add(getHtmlLink("/"+ Injectable.Style.rawValue +"/$cssStyle-before.css"))
endIncludes.add(getHtmlLink("/"+ Injectable.Style.rawValue +"/$cssStyle-after.css"))
endIncludes.add(getHtmlScript("/"+ Injectable.Script.rawValue +"/touchHandling.js"))
endIncludes.add(getHtmlScript("/"+ Injectable.Script.rawValue +"/utils.js"))
beginIncludes.add(getHtmlLink("/assets/readium-css/${contentLayout.readiumCSSPath}ReadiumCSS-before.css"))
endIncludes.add(getHtmlLink("/assets/readium-css/${contentLayout.readiumCSSPath}ReadiumCSS-after.css"))
endIncludes.add(getHtmlScript("/assets/scripts/touchHandling.js"))
endIncludes.add(getHtmlScript("/assets/scripts/utils.js"))

customResources?.let {
// Inject all custom resourses
Expand All @@ -119,7 +120,7 @@ internal class ContentFiltersEpub(private val userPropertiesPath: String?, priva
resourceHtml = StringBuilder(resourceHtml).insert(endHeadIndex, element).toString()
endHeadIndex += element.length
}
resourceHtml = StringBuilder(resourceHtml).insert(endHeadIndex, getHtmlFont("/fonts/OpenDyslexic-Regular.otf")).toString()
resourceHtml = StringBuilder(resourceHtml).insert(endHeadIndex, getHtmlFont(fontFamily = "OpenDyslexic", href = "/assets/fonts/OpenDyslexic-Regular.otf")).toString()
resourceHtml = StringBuilder(resourceHtml).insert(endHeadIndex, "<style>@import url('https://fonts.googleapis.com/css?family=PT+Serif|Roboto|Source+Sans+Pro|Vollkorn');</style>\n").toString()

// Inject userProperties
Expand Down Expand Up @@ -185,10 +186,10 @@ internal class ContentFiltersEpub(private val userPropertiesPath: String?, priva
return resourceHtml.toByteArray().inputStream()
}

private fun getHtmlFont(resourceName: String): String {
val prefix = "<style type=\"text/css\"> @font-face{font-family: \"OpenDyslexic\"; src:url(\""
private fun getHtmlFont(fontFamily: String, href: String): String {
val prefix = "<style type=\"text/css\"> @font-face{font-family: \"$fontFamily\"; src:url(\""
val suffix = "\") format('truetype');}</style>\n"
return prefix + resourceName + suffix
return prefix + href + suffix
}

private fun getHtmlLink(resourceName: String): String {
Expand Down Expand Up @@ -318,9 +319,13 @@ internal class ContentFiltersEpub(private val userPropertiesPath: String?, priva
return string
}

private val ContentLayout.readiumCSSPath: String get() = when(this) {
ContentLayout.LTR -> ""
ContentLayout.RTL -> "rtl/"
ContentLayout.CJK_VERTICAL -> "cjk-vertical/"
ContentLayout.CJK_HORIZONTAL -> "cjk-horizontal/"
}

}


internal class ContentFiltersCbz : ContentFilters

62 changes: 62 additions & 0 deletions r2-streamer/src/main/java/org/readium/r2/streamer/server/Assets.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Module: r2-streamer-kotlin
* Developers: Mickaël Menu
*
* Copyright (c) 2020. Readium Foundation. All rights reserved.
* Use of this source code is governed by a BSD-style license which is detailed in the
* LICENSE file present in the project repository where this source code is maintained.
*/

package org.readium.r2.streamer.server

import android.content.res.AssetManager
import android.net.Uri
import org.readium.r2.shared.extensions.isParentOf
import org.readium.r2.shared.format.Format
import org.readium.r2.shared.format.MediaType
import java.io.File
import java.io.InputStream


/**
* Files to be served from the application's assets.
*
* @param basePath Base path (ignoring host) from where the files are served.
* @param fallbackMediaType Media type which will be used for responses when it can't be determined
* from the served file.
*/
internal class Assets(
private val assetManager: AssetManager,
private val basePath: String,
private val fallbackMediaType: MediaType = MediaType.BINARY
) {
private val assets: MutableList<Pair<String, File>> = mutableListOf()

fun add(href: String, path: String) {
// Inserts at the beginning to take precedence over already registered assets.
assets.add(0, Pair(href, File("/$path").canonicalFile))
}

fun find(uri: Uri): ServedAsset? {
val path = uri.path?.removePrefix(basePath) ?: return null

for ((href, file) in assets) {
if (path.startsWith(href)) {
val requestedFile = File(file, path.removePrefix(href)).canonicalFile
// Makes sure that the requested file is `file` or one of its descendant.
if (file == requestedFile || file.isParentOf(requestedFile)) {
val mediaType = Format.of(fileExtension = requestedFile.extension)?.mediaType ?: fallbackMediaType
return ServedAsset(assetManager.open(requestedFile.path.removePrefix("/")), mediaType)
}
}
}

return null
}

data class ServedAsset(
val stream: InputStream,
val mediaType: MediaType
)

}
60 changes: 60 additions & 0 deletions r2-streamer/src/main/java/org/readium/r2/streamer/server/Files.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Module: r2-streamer-kotlin
* Developers: Aferdita Muriqi, Clément Baumann
*
* Copyright (c) 2018. Readium Foundation. All rights reserved.
* Use of this source code is governed by a BSD-style license which is detailed in the
* LICENSE file present in the project repository where this source code is maintained.
*/

package org.readium.r2.streamer.server

import android.net.Uri
import org.readium.r2.shared.extensions.isParentOf
import org.readium.r2.shared.format.Format
import org.readium.r2.shared.format.MediaType
import java.io.File

/**
* Files to be served from the file system.
*
* @param basePath Base path (ignoring host) from where the files are served.
* @param fallbackMediaType Media type which will be used for responses when it can't be determined
* from the served file.
*/
internal class Files(
private val basePath: String,
private val fallbackMediaType: MediaType = MediaType.BINARY
) {
private val files: MutableMap<String, File> = mutableMapOf()

operator fun set(href: String, file: File) {
files[href] = file.canonicalFile
}

operator fun get(key: String): File? = files[key]

fun find(uri: Uri): ServedFile? {
val path = uri.path?.removePrefix(basePath) ?: return null

for ((href, file) in files) {
if (path.startsWith(href)) {
val requestedFile = File(file, path.removePrefix(href)).canonicalFile
// Makes sure that the requested file is `file` or one of its descendant.
if (file.isParentOf(requestedFile)) {
return ServedFile(requestedFile, fallbackMediaType)
}
}
}

return null
}

data class ServedFile(
val file: File,
private val fallbackMediaType: MediaType
) {
val mediaType: MediaType get() = Format.of(file)?.mediaType ?: fallbackMediaType
}

}
24 changes: 0 additions & 24 deletions r2-streamer/src/main/java/org/readium/r2/streamer/server/Fonts.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,10 @@ class Resources {
}
}

fun getPair(key: String): Any {
return resources[key] ?: ""
}

fun get(key: String): String =
fun get(key: String): String? =
when (val resource = resources[key]) {
is Pair<*, *> -> resource.first as? String
else -> resource as? String
} ?: ""
}

}
99 changes: 33 additions & 66 deletions r2-streamer/src/main/java/org/readium/r2/streamer/server/Server.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import android.content.Context
import android.content.res.AssetManager
import org.nanohttpd.router.RouterNanoHTTPD
import org.readium.r2.shared.Injectable
import org.readium.r2.shared.publication.ContentLayout
import org.readium.r2.shared.publication.Publication
import org.readium.r2.streamer.BuildConfig.DEBUG
import org.readium.r2.streamer.container.Container
Expand All @@ -27,24 +26,30 @@ import java.net.URL
import java.net.URLDecoder
import java.util.*

class Server(port: Int) : AbstractServer(port)
class Server(port: Int, context: Context) : AbstractServer(port, context.applicationContext)

abstract class AbstractServer(private var port: Int) : RouterNanoHTTPD("127.0.0.1", port) {
abstract class AbstractServer(private var port: Int, private val context: Context) : RouterNanoHTTPD("127.0.0.1", port) {

// private val SEARCH_QUERY_HANDLE = "/search"
private val MANIFEST_HANDLE = "/manifest"
private val JSON_MANIFEST_HANDLE = "/manifest.json"
private val MANIFEST_ITEM_HANDLE = "/(.*)"
private val MEDIA_OVERLAY_HANDLE = "/media-overlay"
private val CSS_HANDLE = "/"+ Injectable.Style.rawValue +"/(.*)"
private val JS_HANDLE = "/"+ Injectable.Script.rawValue +"/(.*)"
private val FONT_HANDLE = "/"+ Injectable.Font.rawValue +"/(.*)"
private val ASSETS_HANDLE = "/assets/(.*)"
private var containsMediaOverlay = false

private val resources = Resources()
private val customResources = Resources()
private val assets = Assets(context.assets, basePath = "/assets/")
private val fonts = Files(basePath = "/${Injectable.Style}/")

private val fonts = Fonts()
init {
assets.add(href = "readium-css", path = "readium/readium-css")
assets.add(href = "scripts", path = "readium/scripts")
assets.add(href = "fonts", path = "readium/fonts")
}

private fun addResource(name: String, body: String, custom: Boolean = false, injectable: Injectable? = null) {
if (custom) {
Expand All @@ -58,53 +63,7 @@ abstract class AbstractServer(private var port: Int) : RouterNanoHTTPD("127.0.0.
dir.mkdirs()
inputStream.toFile(context.filesDir.path + "/" + Injectable.Font.rawValue + "/" + name)
val file = File(context.filesDir.path + "/" + Injectable.Font.rawValue + "/" + name)
fonts.add(name, file)
}

fun loadReadiumCSSResources(assets: AssetManager) {
fun load(name: String, layout: ContentLayout) {
try {
addResource("${layout.cssId}-${name}.css", Scanner(assets.open("static/readium-css/${layout.readiumCSSPath}ReadiumCSS-$name.css"), "utf-8")
.useDelimiter("\\A").next())
} catch (e: IOException) {
if (DEBUG) Timber.d(e)
}
}

for (layout in ContentLayout.values()) {
load("before", layout)
load("default", layout)
load("after", layout)
}
}

private val ContentLayout.readiumCSSPath: String get() = when(this) {
ContentLayout.LTR -> ""
ContentLayout.RTL -> "rtl/"
ContentLayout.CJK_VERTICAL -> "cjk-vertical/"
ContentLayout.CJK_HORIZONTAL -> "cjk-horizontal/"
}

fun loadR2ScriptResources(assets: AssetManager) {
try {
addResource("touchHandling.js", Scanner(assets.open(Injectable.Script.rawValue + "/touchHandling.js"), "utf-8")
.useDelimiter("\\A").next())
} catch (e: IOException) {
if (DEBUG) Timber.d(e)
}
try {
addResource("utils.js", Scanner(assets.open(Injectable.Script.rawValue + "/utils.js"), "utf-8")
.useDelimiter("\\A").next())
} catch (e: IOException) {
if (DEBUG) Timber.d(e)
}
}
fun loadR2FontResources(assets: AssetManager, context: Context) {
try {
addFont("OpenDyslexic-Regular.otf", assets.open("static/"+ Injectable.Font.rawValue +"/OpenDyslexic-Regular.otf"), context)
} catch (e: IOException) {
if (DEBUG) Timber.d(e)
}
fonts[name] = file
}

fun loadCustomResource(inputStream: InputStream, fileName: String, injectable: Injectable) {
Expand Down Expand Up @@ -142,28 +101,36 @@ abstract class AbstractServer(private var port: Int) : RouterNanoHTTPD("127.0.0.
}
addRoute(basePath + JSON_MANIFEST_HANDLE, ManifestHandler::class.java, fetcher)
addRoute(basePath + MANIFEST_HANDLE, ManifestHandler::class.java, fetcher)
addRoute(basePath + MANIFEST_ITEM_HANDLE, ResourceHandler::class.java, fetcher)
addRoute(JS_HANDLE, JSHandler::class.java, resources)
addRoute(CSS_HANDLE, CSSHandler::class.java, resources)
addRoute(FONT_HANDLE, FontHandler::class.java, fonts)
addRoute(basePath + MANIFEST_ITEM_HANDLE, PublicationResourceHandler::class.java, fetcher)
addRoute(ASSETS_HANDLE, AssetHandler::class.java, assets)
addRoute(JS_HANDLE, ResourceHandler::class.java, resources)
addRoute(CSS_HANDLE, ResourceHandler::class.java, resources)
addRoute(FONT_HANDLE, FileHandler::class.java, fonts)
}

/* FIXME: To review once the media-overlays will be supported in the Publication model
private fun addLinks(publication: Publication, filePath: String) {
containsMediaOverlay = false
for (link in publication.otherLinks) {
if (link.rel.contains("media-overlay")) {
containsMediaOverlay = true
link.href = link.href?.replace("port", "127.0.0.1:$listeningPort$filePath")
}
}
} */
// FIXME: To review once the media-overlays will be supported in the Publication model
// private fun addLinks(publication: Publication, filePath: String) {
// containsMediaOverlay = false
// for (link in publication.otherLinks) {
// if (link.rel.contains("media-overlay")) {
// containsMediaOverlay = true
// link.href = link.href?.replace("port", "127.0.0.1:$listeningPort$filePath")
// }
// }
// }

private fun InputStream.toFile(path: String) {
use { input ->
File(path).outputStream().use { input.copyTo(it) }
}
}

@Deprecated("This is not needed anymore")
fun loadReadiumCSSResources(assets: AssetManager) {}
@Deprecated("This is not needed anymore")
fun loadR2ScriptResources(assets: AssetManager) {}
@Deprecated("This is not needed anymore")
fun loadR2FontResources(assets: AssetManager, context: Context) {}

}

Loading

0 comments on commit 06756c0

Please sign in to comment.