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

Commit

Permalink
Add support for the HttpClient (#148)
Browse files Browse the repository at this point in the history
`Streamer` takes a new optional `HttpClient` dependency to handle HTTP requests.
  • Loading branch information
mickael-menu authored Apr 20, 2021
1 parent ecdb8c7 commit 2309e8a
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 32 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ All notable changes to this project will be documented in this file.

**Warning:** Features marked as *experimental* may change or be removed in a future release without notice. Use with caution.

<!-- ## [Unreleased] -->
## [Unreleased]

### Added

* `Streamer` takes a new optional `HttpClient` dependency to handle HTTP requests.


## [2.0.0-beta.2]

Expand Down
9 changes: 6 additions & 3 deletions r2-streamer/src/main/java/org/readium/r2/streamer/Streamer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.readium.r2.shared.publication.asset.PublicationAsset
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.archive.ArchiveFactory
import org.readium.r2.shared.util.archive.DefaultArchiveFactory
import org.readium.r2.shared.util.http.DefaultHttpClient
import org.readium.r2.shared.util.logging.WarningLogger
import org.readium.r2.shared.util.mediatype.MediaType
import org.readium.r2.shared.util.pdf.PdfDocumentFactory
Expand All @@ -28,7 +29,7 @@ import org.readium.r2.streamer.parser.image.ImageParser
import org.readium.r2.streamer.parser.pdf.PdfParser
import org.readium.r2.streamer.parser.pdf.PdfiumPdfDocumentFactory
import org.readium.r2.streamer.parser.readium.ReadiumWebPubParser
import java.lang.Exception
import kotlin.Exception

internal typealias PublicationTry<SuccessT> = Try<SuccessT, Publication.OpeningException>

Expand All @@ -45,6 +46,7 @@ internal typealias PublicationTry<SuccessT> = Try<SuccessT, Publication.OpeningE
* @param ignoreDefaultParsers When true, only parsers provided in parsers will be used.
* @param archiveFactory Opens an archive (e.g. ZIP, RAR), optionally protected by credentials.
* @param pdfFactory Parses a PDF document, optionally protected by password.
* @param httpClient Service performing HTTP requests.
* @param onCreatePublication Called on every parsed [Publication.Builder]. It can be used to modify
* the [Manifest], the root [Fetcher] or the list of service factories of a [Publication].
*/
Expand All @@ -56,6 +58,7 @@ class Streamer constructor(
private val contentProtections: List<ContentProtection> = emptyList(),
private val archiveFactory: ArchiveFactory = DefaultArchiveFactory(),
private val pdfFactory: PdfDocumentFactory = DefaultPdfDocumentFactory(context),
private val httpClient: DefaultHttpClient = DefaultHttpClient(),
private val onCreatePublication: Publication.Builder.() -> Unit = {}
) {

Expand Down Expand Up @@ -121,7 +124,7 @@ class Streamer constructor(
} catch (e: Exception) {
throw Publication.OpeningException.ParsingFailed(e)
}
} ?: throw Publication.OpeningException.UnsupportedFormat
} ?: throw Publication.OpeningException.UnsupportedFormat(Exception("Cannot find a parser for this asset"))

// Transform from the Content Protection.
protectedAsset?.let { builder.apply(it.onCreatePublication) }
Expand All @@ -145,7 +148,7 @@ class Streamer constructor(
listOf(
EpubParser(),
PdfParser(context, pdfFactory),
ReadiumWebPubParser(pdfFactory),
ReadiumWebPubParser(pdfFactory, httpClient),
ImageParser(),
AudioParser()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,25 @@

package org.readium.r2.streamer.parser.readium

import android.content.Context
import kotlinx.coroutines.runBlocking
import org.json.JSONObject
import org.readium.r2.shared.PdfSupport
import org.readium.r2.shared.drm.DRM
import org.readium.r2.shared.fetcher.ArchiveFetcher
import org.readium.r2.shared.fetcher.Fetcher
import org.readium.r2.shared.fetcher.FileFetcher
import org.readium.r2.shared.fetcher.TransformingFetcher
import org.readium.r2.shared.fetcher.*
import org.readium.r2.shared.publication.Manifest
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.publication.asset.FileAsset
import org.readium.r2.shared.publication.asset.PublicationAsset
import org.readium.r2.shared.publication.services.PerResourcePositionsService
import org.readium.r2.shared.publication.services.locatorServiceFactory
import org.readium.r2.shared.publication.services.positionsServiceFactory
import org.readium.r2.shared.util.http.HttpClient
import org.readium.r2.shared.util.logging.WarningLogger
import org.readium.r2.shared.util.mediatype.MediaType
import org.readium.r2.shared.util.pdf.PdfDocumentFactory
import org.readium.r2.streamer.DefaultPdfDocumentFactory
import org.readium.r2.streamer.PublicationParser
import org.readium.r2.streamer.container.ContainerError
import org.readium.r2.streamer.container.PublicationContainer
import org.readium.r2.streamer.extensions.readAsJsonOrNull
import org.readium.r2.streamer.fetcher.LcpDecryptor
import org.readium.r2.streamer.parser.PubBox
import org.readium.r2.streamer.parser.audio.AudioLocatorService
Expand All @@ -43,38 +39,46 @@ import java.io.FileNotFoundException
* Parses any Readium Web Publication package or manifest, e.g. WebPub, Audiobook, DiViNa, LCPDF...
*/
@OptIn(PdfSupport::class)
class ReadiumWebPubParser(private val pdfFactory: PdfDocumentFactory? = null) : PublicationParser, org.readium.r2.streamer.parser.PublicationParser {

constructor(context: Context) : this(pdfFactory = DefaultPdfDocumentFactory(context))
class ReadiumWebPubParser(
private val pdfFactory: PdfDocumentFactory?,
private val httpClient: HttpClient,
) : PublicationParser, org.readium.r2.streamer.parser.PublicationParser {

override suspend fun parse(
asset: PublicationAsset,
fetcher: Fetcher,
warnings: WarningLogger?
): Publication.Builder? {
val mediaType = asset.mediaType()

if (!asset.mediaType().isReadiumWebPubProfile)
if (!mediaType.isReadiumWebPubProfile)
return null

val manifest =
if (asset.mediaType().isRwpm) {
val manifestLink = fetcher.links().firstOrNull()
?: error("Empty fetcher.")
val manifestJson = fetcher.get(manifestLink).use {
it.readAsString().getOrThrow()
}
Manifest.fromJSON(JSONObject(manifestJson))
val isPackage = !mediaType.isRwpm

val manifestJson =
if (isPackage) {
fetcher.readAsJsonOrNull("/manifest.json")
} else {
val manifestLink = fetcher.links()
.firstOrNull { it.href == "/manifest.json" }
?: error("Unable to find a manifest link.")
val manifestJson = fetcher.get(manifestLink).use {
it.readAsString().getOrThrow()
}
Manifest.fromJSON(JSONObject(manifestJson), packaged = true)
// For a single manifest file, reads the first (and only) file in the fetcher.
fetcher.links().firstOrNull()
?.let { fetcher.readAsJsonOrNull(it.href) }
}
?: throw Exception("Failed to parse RWPM.")
?: throw Exception("Manifest not found")

val manifest = Manifest.fromJSON(manifestJson, packaged = isPackage)
?: throw Exception("Failed to parse the RWPM Manifest")

@Suppress("NAME_SHADOWING")
var fetcher = fetcher

// For a manifest, we discard the [fetcher] provided by the Streamer, because it was only
// used to read the manifest file. We use an [HttpFetcher] instead to serve the remote
// resources.
if (!isPackage) {
val baseUrl = manifest.linkWithRel("self")?.let { File(it.href).parent }
fetcher = HttpFetcher(httpClient, baseUrl)
}

// Checks the requirements from the LCPDF specification.
// https://readium.org/lcp-specs/notes/lcp-for-pdf.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ class EpubPositionsServiceTest {

override suspend fun length() = findResource(link.href)
?.let { Try.success(it.first) }
?: Try.failure(Resource.Exception.NotFound)
?: Try.failure(Resource.Exception.NotFound())

override suspend fun read(range: LongRange?): ResourceTry<ByteArray> = Try.success(ByteArray(0))

Expand Down

0 comments on commit 2309e8a

Please sign in to comment.