forked from I2P_Developers/i2p.i2p
* 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:
@ -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 {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -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
|
||||||
|
@ -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 = "";
|
||||||
|
Reference in New Issue
Block a user