X-Git-Url: https://git.mdrn.pl/wl-app.git/blobdiff_plain/48b2fe9f7c2dc3d9aeaaa6dbfb27c7da4f3235ff..269195b3729c1bdc22e9053ee4ebca667ea8549d:/Android/r2-streamer/r2-server/src/main/java/org/readium/r2_streamer/server/handler/ResourceHandler.java diff --git a/Android/r2-streamer/r2-server/src/main/java/org/readium/r2_streamer/server/handler/ResourceHandler.java b/Android/r2-streamer/r2-server/src/main/java/org/readium/r2_streamer/server/handler/ResourceHandler.java new file mode 100755 index 0000000..b697cbe --- /dev/null +++ b/Android/r2-streamer/r2-server/src/main/java/org/readium/r2_streamer/server/handler/ResourceHandler.java @@ -0,0 +1,185 @@ +package org.readium.r2_streamer.server.handler; + +import org.readium.r2_streamer.fetcher.EpubFetcher; +import org.readium.r2_streamer.fetcher.EpubFetcherException; +import org.readium.r2_streamer.model.publication.Encryption; +import org.readium.r2_streamer.model.publication.link.Link; +import org.readium.r2_streamer.parser.EncryptionDecoder; +import org.readium.r2_streamer.server.ResponseStatus; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import fi.iki.elonen.NanoHTTPD; +import fi.iki.elonen.NanoHTTPD.IHTTPSession; +import fi.iki.elonen.NanoHTTPD.Method; +import fi.iki.elonen.NanoHTTPD.Response; +import fi.iki.elonen.NanoHTTPD.Response.IStatus; +import fi.iki.elonen.NanoHTTPD.Response.Status; +import fi.iki.elonen.router.RouterNanoHTTPD.DefaultHandler; +import fi.iki.elonen.router.RouterNanoHTTPD.UriResource; + +import static fi.iki.elonen.NanoHTTPD.MIME_PLAINTEXT; + +/** + * Created by Shrikant Badwaik on 10-Feb-17. + */ + +public class ResourceHandler extends DefaultHandler { + private static final String TAG = "ResourceHandler"; + + public ResourceHandler() { + } + + @Override + public String getMimeType() { + return null; + } + + private final String[] fonts = {".woff", ".ttf", ".obf", ".woff2", ".eot", ".otf"}; + + @Override + public String getText() { + return ResponseStatus.FAILURE_RESPONSE; + } + + @Override + public IStatus getStatus() { + return Status.OK; + } + + @Override + public Response get(UriResource uriResource, Map urlParams, IHTTPSession session) { + Method method = session.getMethod(); + String uri = session.getUri(); + + System.out.println(TAG + " Method: " + method + ", Url: " + uri); + + try { + EpubFetcher fetcher = uriResource.initParameter(EpubFetcher.class); + int offset = uri.indexOf("/", 0); + int startIndex = uri.indexOf("/", offset + 1); + String filePath = uri.substring(startIndex + 1); + Link link = fetcher.publication.getResourceMimeType(filePath); + String mimeType = link.getTypeLink(); + + // If the content is of type html return the response this is done to + // skip the check for following font deobfuscation check + if (mimeType.equals("application/xhtml+xml")) { + return serveResponse(session, fetcher.getDataInputStream(filePath), mimeType); + } + + // ******************** + // FONT DEOBFUSCATION + // ******************** + if (isFontFile(filePath)) { // Check if the incoming request for the font file is encrypted + Encryption encryption = Encryption.getEncryptionFormFontFilePath( + filePath, + fetcher.publication.encryptions); + if (encryption != null) { // Decode the font file if encryption exists + return serveResponse(session, + EncryptionDecoder.decode( + fetcher.publication.metadata.identifier, + fetcher.getDataInputStream(encryption.getProfile()), + encryption), + mimeType); + } + } + return serveResponse(session, fetcher.getDataInputStream(filePath), mimeType); + } catch (EpubFetcherException e) { + System.out.println(TAG + " EpubFetcherException " + e.toString()); + return NanoHTTPD.newFixedLengthResponse(Status.INTERNAL_ERROR, getMimeType(), ResponseStatus.FAILURE_RESPONSE); + } + } + + private Response serveResponse(IHTTPSession session, InputStream inputStream, String mimeType) { + Response response; + String rangeRequest = session.getHeaders().get("range"); + + try { + // Calculate etag + String etag = Integer.toHexString(inputStream.hashCode()); + + // Support skipping: + long startFrom = 0; + long endAt = -1; + if (rangeRequest != null) { + if (rangeRequest.startsWith("bytes=")) { + rangeRequest = rangeRequest.substring("bytes=".length()); + int minus = rangeRequest.indexOf('-'); + try { + if (minus > 0) { + startFrom = Long.parseLong(rangeRequest.substring(0, minus)); + endAt = Long.parseLong(rangeRequest.substring(minus + 1)); + } + } catch (NumberFormatException ignored) { + } + } + } + + // Change return code and add Content-Range header when skipping is requested + long streamLength = inputStream.available(); + if (rangeRequest != null && startFrom >= 0) { + if (startFrom >= streamLength) { + response = createResponse(Response.Status.RANGE_NOT_SATISFIABLE, MIME_PLAINTEXT, ""); + response.addHeader("Content-Range", "bytes 0-0/" + streamLength); + response.addHeader("ETag", etag); + } else { + if (endAt < 0) { + endAt = streamLength - 1; + } + long newLen = endAt - startFrom + 1; + if (newLen < 0) { + newLen = 0; + } + + final long dataLen = newLen; + inputStream.skip(startFrom); + + response = createResponse(Response.Status.PARTIAL_CONTENT, mimeType, inputStream); + response.addHeader("Content-Length", "" + dataLen); + response.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + streamLength); + response.addHeader("ETag", etag); + } + } else { + if (etag.equals(session.getHeaders().get("if-none-match"))) + response = createResponse(Response.Status.NOT_MODIFIED, mimeType, ""); + else { + response = createResponse(Response.Status.OK, mimeType, inputStream); + response.addHeader("Content-Length", "" + streamLength); + response.addHeader("ETag", etag); + } + } + } catch (IOException | NullPointerException ioe) { + response = getResponse("Forbidden: Reading file failed"); + } + + return (response == null) ? getResponse("Error 404: File not found") : response; + } + + private Response createResponse(Status status, String mimeType, InputStream message) { + Response response = NanoHTTPD.newChunkedResponse(status, mimeType, message); + response.addHeader("Accept-Ranges", "bytes"); + return response; + } + + private Response createResponse(Status status, String mimeType, String message) { + Response response = NanoHTTPD.newFixedLengthResponse(status, mimeType, message); + response.addHeader("Accept-Ranges", "bytes"); + return response; + } + + private Response getResponse(String message) { + return createResponse(Response.Status.OK, "text/plain", message); + } + + private boolean isFontFile(String file) { + for (String font : fonts) { + if (file.endsWith(font)) { + return true; + } + } + return false; + } +} \ No newline at end of file