* i2psnark: Support HTTP request ranges so in-browser and other http-aware media players work better.

Single range only; no multipart
This commit is contained in:
zzz
2013-04-28 16:46:52 +00:00
parent 0cf7e91475
commit 188ff3392d
4 changed files with 78 additions and 30 deletions

View File

@ -23,6 +23,8 @@ import java.io.OutputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
@ -318,19 +320,47 @@ class BasicServlet extends HttpServlet
out = new WriterOutputStream(response.getWriter()); out = new WriterOutputStream(response.getWriter());
} }
// Write content normally
long content_length = content.getContentLength(); long content_length = content.getContentLength();
writeHeaders(response,content,content_length);
if (content_length >= 0 && request.getMethod().equals("HEAD")) { // see if there are any range headers
// if we know the content length, don't send it to be counted Enumeration reqRanges = request.getHeaders("Range");
if (_log.shouldLog(Log.INFO))
_log.info("HEAD: " + content); if (reqRanges == null || !reqRanges.hasMoreElements()) {
} else { // if there were no ranges, send entire entity
// GET or unknown size for HEAD // Write content normally
copy(in, out); writeHeaders(response,content,content_length);
if (content_length >= 0 && request.getMethod().equals("HEAD")) {
// if we know the content length, don't send it to be counted
if (_log.shouldLog(Log.INFO))
_log.info("HEAD: " + content);
} else {
// GET or unknown size for HEAD
copy(in, out);
}
return;
} }
return;
// Parse the satisfiable ranges
List<InclusiveByteRange> ranges = InclusiveByteRange.satisfiableRanges(reqRanges, content_length);
// if there are no satisfiable ranges, send 416 response
// Completely punt on multiple ranges (unlike Default)
if (ranges == null || ranges.size() != 1) {
writeHeaders(response, content, content_length);
response.setStatus(416);
response.setHeader("Content-Range", InclusiveByteRange.to416HeaderRangeString(content_length));
return;
}
// if there is only a single valid range (must be satisfiable
// since were here now), send that range with a 216 response
InclusiveByteRange singleSatisfiableRange = ranges.get(0);
long singleLength = singleSatisfiableRange.getSize(content_length);
writeHeaders(response, content, singleLength);
response.setStatus(206);
response.setHeader("Content-Range", singleSatisfiableRange.toHeaderRangeString(content_length));
copy(in, singleSatisfiableRange.getFirst(content_length), out, singleLength);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -532,11 +562,29 @@ class BasicServlet extends HttpServlet
* Write from in to out * Write from in to out
*/ */
private void copy(InputStream in, OutputStream out) throws IOException { private void copy(InputStream in, OutputStream out) throws IOException {
copy(in, 0, out, -1);
}
/**
* Write from in to out
*/
private void copy(InputStream in, long skip, OutputStream out, final long len) throws IOException {
ByteArray ba = _cache.acquire(); ByteArray ba = _cache.acquire();
byte[] buf = ba.getData(); byte[] buf = ba.getData();
try { try {
if (skip > 0)
in.skip(skip);
int read = 0; int read = 0;
while ( (read = in.read(buf)) != -1) { long tot = 0;
boolean done = false;
while ( (read = in.read(buf)) != -1 && !done) {
if (len >= 0) {
tot += read;
if (tot >= len) {
read -= (int) (tot - len);
done = true;
}
}
out.write(buf, 0, read); out.write(buf, 0, read);
} }
} finally { } finally {

View File

@ -16,16 +16,13 @@
// ======================================================================== // ========================================================================
// //
package org.eclipse.jetty.server; package org.klomp.snark.web;
import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Byte range inclusive of end points. /** Byte range inclusive of end points.
* <PRE> * <PRE>
@ -49,8 +46,6 @@ import org.eclipse.jetty.util.log.Logger;
*/ */
public class InclusiveByteRange public class InclusiveByteRange
{ {
private static final Logger LOG = Log.getLogger(InclusiveByteRange.class);
long first = 0; long first = 0;
long last = 0; long last = 0;
@ -76,11 +71,11 @@ public class InclusiveByteRange
/** /**
* @param headers Enumeration of Range header fields. * @param headers Enumeration of Range header fields.
* @param size Size of the resource. * @param size Size of the resource.
* @return LazyList of satisfiable ranges * @return List of satisfiable ranges
*/ */
public static List satisfiableRanges(Enumeration headers, long size) public static List<InclusiveByteRange> satisfiableRanges(Enumeration headers, long size)
{ {
Object satRanges=null; List<InclusiveByteRange> satRanges = null;
// walk through all Range headers // walk through all Range headers
headers: headers:
@ -105,7 +100,6 @@ public class InclusiveByteRange
{ {
if ("bytes".equals(t)) if ("bytes".equals(t))
continue; continue;
LOG.warn("Bad range format: {}",t);
continue headers; continue headers;
} }
else if (d == 0) else if (d == 0)
@ -114,7 +108,6 @@ public class InclusiveByteRange
last = Long.parseLong(t.substring(d + 1).trim()); last = Long.parseLong(t.substring(d + 1).trim());
else else
{ {
LOG.warn("Bad range format: {}",t);
continue; continue;
} }
} }
@ -134,25 +127,23 @@ public class InclusiveByteRange
if (first < size) if (first < size)
{ {
if (satRanges == null)
satRanges = new ArrayList(4);
InclusiveByteRange range = new InclusiveByteRange(first,last); InclusiveByteRange range = new InclusiveByteRange(first,last);
satRanges = LazyList.add(satRanges,range); satRanges.add(range);
} }
} }
catch (NumberFormatException e) catch (NumberFormatException e)
{ {
LOG.warn("Bad range format: {}",t);
LOG.ignore(e);
continue; continue;
} }
} }
} }
catch(Exception e) catch(Exception e)
{ {
LOG.warn("Bad range format: {}",t);
LOG.ignore(e);
} }
} }
return LazyList.getList(satRanges,true); return satRanges;
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */

View File

@ -1,3 +1,12 @@
2013-04-28 zzz
* i2psnark:
- Improve page nav
- Ensure current stats and correct event delivered in announce
- Only show lower section on first page
- Dir page CSS tweaks
- Parameter fixes
- Support HTTP request ranges
2013-04-26 zzz 2013-04-26 zzz
* Console: Show log location on /logs even if not opened yet (ticket #905) * Console: Show log location on /logs even if not opened yet (ticket #905)
* HTTP proxy: Verify nonce count in digest auth * HTTP proxy: Verify nonce count in digest auth

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */ /** deprecated */
public final static String ID = "Monotone"; public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION; public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 18; public final static long BUILD = 19;
/** for example "-test" */ /** for example "-test" */
public final static String EXTRA = ""; public final static String EXTRA = "";