diff --git a/src/org/geometerplus/android/fbreader/httpd/DataServer.java b/src/org/geometerplus/android/fbreader/httpd/DataServer.java index fa0faf14c..149db0e24 100644 --- a/src/org/geometerplus/android/fbreader/httpd/DataServer.java +++ b/src/org/geometerplus/android/fbreader/httpd/DataServer.java @@ -19,12 +19,15 @@ package org.geometerplus.android.fbreader.httpd; +import java.io.InputStream; import java.io.IOException; +import java.util.Map; import fi.iki.elonen.NanoHTTPD; import org.geometerplus.zlibrary.core.filesystem.ZLFile; import org.geometerplus.zlibrary.core.util.MimeType; +import org.geometerplus.zlibrary.core.util.SliceInputStream; public class DataServer extends NanoHTTPD { DataServer() { @@ -50,16 +53,14 @@ public class DataServer extends NanoHTTPD { i = item; path.append((char)Short.parseShort(item, 16)); } - final Response res = new Response( - Response.Status.OK, + return serveFile( + ZLFile.createFileByPath(path.toString()), MimeType.VIDEO_WEBM.toString(), - ZLFile.createFileByPath(path.toString()).getInputStream() + session.getHeaders() ); - res.addHeader("Accept-Ranges", "bytes"); - return res; } catch (Exception e) { return new Response( - Response.Status.NOT_FOUND, + Response.Status.FORBIDDEN, MimeType.TEXT_HTML.toString(), "

" + e.getMessage() + "

\n(" + uri + ")\n(" + encodedPath + ")" ); @@ -71,4 +72,63 @@ public class DataServer extends NanoHTTPD { "

Not found: " + uri + "

" ); } + + private static final String BYTES_PREFIX = "bytes="; + + private Response serveFile(ZLFile file, String mime, Map headers) throws IOException { + final Response res; + final InputStream baseStream = file.getInputStream(); + final int fileLength = baseStream.available(); + final String etag = Integer.toHexString(file.getPath().hashCode()); + + final String range = headers.get("range"); + if (range == null || !range.startsWith(BYTES_PREFIX)) { + if (etag.equals(headers.get("if-none-match"))) + res = new Response(Response.Status.NOT_MODIFIED, mime, ""); + else { + res = new Response(Response.Status.OK, mime, baseStream); + res.addHeader("Content-Length", String.valueOf(fileLength)); + res.addHeader("ETag", etag); + } + } else { + int start = 0; + int end = -1; + final String bytes = range.substring(BYTES_PREFIX.length()); + final int minus = bytes.indexOf('-'); + if (minus > 0) { + try { + start = Integer.parseInt(bytes.substring(0, minus)); + final String endString = bytes.substring(minus + 1).trim(); + if (!endString.isEmpty()) { + end = Integer.parseInt(endString); + } + } catch (NumberFormatException e) { + } + } + if (start >= fileLength) { + res = new Response( + Response.Status.RANGE_NOT_SATISFIABLE, + MimeType.TEXT_PLAIN.toString(), + "" + ); + res.addHeader("ETag", etag); + res.addHeader("Content-Range", "bytes 0-0/" + fileLength); + } else { + if (end == -1 || end >= fileLength) { + end = fileLength - 1; + } + res = new Response( + Response.Status.PARTIAL_CONTENT, + mime, + new SliceInputStream(baseStream, start, end - start + 1) + ); + res.addHeader("ETag", etag); + res.addHeader("Content-Length", String.valueOf(end - start + 1)); + res.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength); + } + } + + res.addHeader("Accept-Ranges", "bytes"); + return res; + } }