JETTY-983 range handling cleanup
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1267 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
ec3a5b4f61
commit
37fe212f35
|
@ -14,6 +14,7 @@ jetty-7.0.2-SNAPSHOT
|
|||
+ 302246 redirect loop using form authenticator
|
||||
+ JETTY-776 Make new session-tests module to concentrate all reusable session clustering test code
|
||||
+ JETTY-910 Allow request listeners to access session
|
||||
+ JETTY-983 Range handling cleanup
|
||||
+ JETTY-1153 System property for UrlEncoded charset
|
||||
+ JETTY-1155 HttpConnection.close notifies HttpExchange
|
||||
+ JETTY-1156 SSL blocking close with JVM Bug busy key fix
|
||||
|
|
|
@ -70,7 +70,7 @@ public class InclusiveByteRange
|
|||
* @param size Size of the resource.
|
||||
* @return LazyList of satisfiable ranges
|
||||
*/
|
||||
public static List satisfiableRanges(Enumeration headers, boolean allowRelativeRange, long size)
|
||||
public static List satisfiableRanges(Enumeration headers, long size)
|
||||
{
|
||||
Object satRanges=null;
|
||||
|
||||
|
@ -126,11 +126,6 @@ public class InclusiveByteRange
|
|||
|
||||
if (first < size)
|
||||
{
|
||||
// Relative range end points not allowed (in some cases)
|
||||
if ((!allowRelativeRange) && ((first < 0) || (last < 0)))
|
||||
{
|
||||
continue headers;
|
||||
}
|
||||
InclusiveByteRange range = new InclusiveByteRange(first,last);
|
||||
satRanges = LazyList.add(satRanges,range);
|
||||
}
|
||||
|
|
|
@ -518,7 +518,11 @@ public class ResourceCache extends AbstractLifeCycle
|
|||
public long getContentLength()
|
||||
{
|
||||
if (_buffer==null)
|
||||
{
|
||||
if (_resource!=null)
|
||||
return _resource.length();
|
||||
return -1;
|
||||
}
|
||||
return _buffer.length();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,12 @@ import junit.framework.TestCase;
|
|||
public class InclusiveByteRangeTest extends TestCase
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
private void assertInvalidRange(String rangeString, boolean allowRelativeRange)
|
||||
private void assertInvalidRange(String rangeString)
|
||||
{
|
||||
Vector strings = new Vector();
|
||||
strings.add(rangeString);
|
||||
|
||||
List ranges = InclusiveByteRange.satisfiableRanges(strings.elements(),allowRelativeRange,200);
|
||||
List ranges = InclusiveByteRange.satisfiableRanges(strings.elements(),200);
|
||||
assertNull("Invalid Range [" + rangeString + "] should result in no satisfiable ranges",ranges);
|
||||
}
|
||||
|
||||
|
@ -41,9 +41,9 @@ public class InclusiveByteRangeTest extends TestCase
|
|||
assertEquals(msg + " - header range string",expectedHeader,actualRange.toHeaderRangeString(size));
|
||||
}
|
||||
|
||||
private void assertSimpleRange(int expectedFirst, int expectedLast, String rangeId, int size, boolean allowRelativeRanges)
|
||||
private void assertSimpleRange(int expectedFirst, int expectedLast, String rangeId, int size)
|
||||
{
|
||||
InclusiveByteRange range = parseRange(rangeId,size,allowRelativeRanges);
|
||||
InclusiveByteRange range = parseRange(rangeId,size);
|
||||
|
||||
assertEquals("Range [" + rangeId + "] - first",expectedFirst,range.getFirst(size));
|
||||
assertEquals("Range [" + rangeId + "] - last",expectedLast,range.getLast(size));
|
||||
|
@ -52,25 +52,25 @@ public class InclusiveByteRangeTest extends TestCase
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private InclusiveByteRange parseRange(String rangeString, int size, boolean allowRelativeRange)
|
||||
private InclusiveByteRange parseRange(String rangeString, int size)
|
||||
{
|
||||
Vector strings = new Vector();
|
||||
strings.add(rangeString);
|
||||
|
||||
List ranges = InclusiveByteRange.satisfiableRanges(strings.elements(),allowRelativeRange,size);
|
||||
List ranges = InclusiveByteRange.satisfiableRanges(strings.elements(),size);
|
||||
assertNotNull("Satisfiable Ranges should not be null",ranges);
|
||||
assertEquals("Satisfiable Ranges of [" + rangeString + "] count",1,ranges.size());
|
||||
return (InclusiveByteRange)ranges.get(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<InclusiveByteRange> parseRanges(String rangeString, int size, boolean allowRelativeRange)
|
||||
private List<InclusiveByteRange> parseRanges(String rangeString, int size)
|
||||
{
|
||||
Vector strings = new Vector();
|
||||
strings.add(rangeString);
|
||||
|
||||
List<InclusiveByteRange> ranges;
|
||||
ranges = InclusiveByteRange.satisfiableRanges(strings.elements(),allowRelativeRange,size);
|
||||
ranges = InclusiveByteRange.satisfiableRanges(strings.elements(),size);
|
||||
assertNotNull("Satisfiable Ranges should not be null",ranges);
|
||||
return ranges;
|
||||
}
|
||||
|
@ -83,19 +83,11 @@ public class InclusiveByteRangeTest extends TestCase
|
|||
|
||||
public void testInvalidRanges()
|
||||
{
|
||||
boolean relativeRange = true;
|
||||
|
||||
// Invalid if parsing "Range" header
|
||||
assertInvalidRange("bytes=a-b",relativeRange); // letters invalid
|
||||
assertInvalidRange("byte=10-3",relativeRange); // key is bad
|
||||
assertInvalidRange("onceuponatime=5-10",relativeRange); // key is bad
|
||||
assertInvalidRange("bytes=300-310",relativeRange); // outside of size (200)
|
||||
|
||||
// Invalid if parsing "Content-Range" header
|
||||
relativeRange = false;
|
||||
assertInvalidRange("bytes=-5",relativeRange); // relative from end
|
||||
assertInvalidRange("bytes=10-",relativeRange); // relative from start
|
||||
assertInvalidRange("bytes=250-300",relativeRange); // outside of size (200)
|
||||
assertInvalidRange("bytes=a-b"); // letters invalid
|
||||
assertInvalidRange("byte=10-3"); // key is bad
|
||||
assertInvalidRange("onceuponatime=5-10"); // key is bad
|
||||
assertInvalidRange("bytes=300-310"); // outside of size (200)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,13 +95,12 @@ public class InclusiveByteRangeTest extends TestCase
|
|||
*/
|
||||
public void testMultipleAbsoluteRanges()
|
||||
{
|
||||
boolean relativeRange = false;
|
||||
int size = 50;
|
||||
String rangeString;
|
||||
|
||||
rangeString = "bytes=5-20,35-65";
|
||||
|
||||
List<InclusiveByteRange> ranges = parseRanges(rangeString,size,relativeRange);
|
||||
List<InclusiveByteRange> ranges = parseRanges(rangeString,size);
|
||||
assertEquals("Satisfiable Ranges of [" + rangeString + "] count",2,ranges.size());
|
||||
assertRange("Range [" + rangeString + "]",5,20,size,ranges.get(0));
|
||||
assertRange("Range [" + rangeString + "]",35,49,size,ranges.get(1));
|
||||
|
@ -124,7 +115,7 @@ public class InclusiveByteRangeTest extends TestCase
|
|||
|
||||
rangeString = "bytes=5-20,35-65,-5";
|
||||
|
||||
List<InclusiveByteRange> ranges = parseRanges(rangeString,50,true);
|
||||
List<InclusiveByteRange> ranges = parseRanges(rangeString,50);
|
||||
assertEquals("Satisfiable Ranges of [" + rangeString + "] count",3,ranges.size());
|
||||
assertRange("Range [" + rangeString + "]",5,20,50,ranges.get(0));
|
||||
assertRange("Range [" + rangeString + "]",35,49,50,ranges.get(1));
|
||||
|
@ -137,7 +128,7 @@ public class InclusiveByteRangeTest extends TestCase
|
|||
|
||||
rangeString = "bytes=5-20,15-25";
|
||||
|
||||
List<InclusiveByteRange> ranges = parseRanges(rangeString,200,true);
|
||||
List<InclusiveByteRange> ranges = parseRanges(rangeString,200);
|
||||
assertEquals("Satisfiable Ranges of [" + rangeString + "] count",2,ranges.size());
|
||||
assertRange("Range [" + rangeString + "]",5,20,200,ranges.get(0));
|
||||
assertRange("Range [" + rangeString + "]",15,25,200,ranges.get(1));
|
||||
|
@ -146,10 +137,9 @@ public class InclusiveByteRangeTest extends TestCase
|
|||
public void testMultipleRangesSplit()
|
||||
{
|
||||
String rangeString;
|
||||
|
||||
rangeString = "bytes=5-10,15-20";
|
||||
|
||||
List<InclusiveByteRange> ranges = parseRanges(rangeString,200,true);
|
||||
List<InclusiveByteRange> ranges = parseRanges(rangeString,200);
|
||||
assertEquals("Satisfiable Ranges of [" + rangeString + "] count",2,ranges.size());
|
||||
assertRange("Range [" + rangeString + "]",5,10,200,ranges.get(0));
|
||||
assertRange("Range [" + rangeString + "]",15,20,200,ranges.get(1));
|
||||
|
@ -157,12 +147,9 @@ public class InclusiveByteRangeTest extends TestCase
|
|||
|
||||
public void testSimpleRange()
|
||||
{
|
||||
boolean relativeRange = true;
|
||||
|
||||
assertSimpleRange(5,10,"bytes=5-10",200,relativeRange);
|
||||
assertSimpleRange(195,199,"bytes=-5",200,relativeRange);
|
||||
assertSimpleRange(50,119,"bytes=50-150",120,relativeRange);
|
||||
assertSimpleRange(50,119,"bytes=50-",120,relativeRange);
|
||||
assertSimpleRange(5,10,"bytes=5-10",200);
|
||||
assertSimpleRange(195,199,"bytes=-5",200);
|
||||
assertSimpleRange(50,119,"bytes=50-150",120);
|
||||
assertSimpleRange(50,119,"bytes=50-",120);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -339,7 +339,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
String servletPath=null;
|
||||
String pathInfo=null;
|
||||
Enumeration reqRanges = null;
|
||||
boolean byteRangeRules = false;
|
||||
Boolean included =request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI)!=null;
|
||||
if (included!=null && included.booleanValue())
|
||||
{
|
||||
|
@ -357,21 +356,10 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
servletPath = request.getServletPath();
|
||||
pathInfo = request.getPathInfo();
|
||||
|
||||
// Is this a Content-Range request?
|
||||
reqRanges = request.getHeaders(HttpHeaders.CONTENT_RANGE);
|
||||
// Is this a Range request?
|
||||
reqRanges = request.getHeaders(HttpHeaders.RANGE);
|
||||
if (!hasDefinedRange(reqRanges))
|
||||
{
|
||||
// Is this a Range request?
|
||||
reqRanges = request.getHeaders(HttpHeaders.RANGE);
|
||||
if (hasDefinedRange(reqRanges))
|
||||
{
|
||||
byteRangeRules = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reqRanges = null;
|
||||
}
|
||||
}
|
||||
reqRanges = null;
|
||||
}
|
||||
|
||||
String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
|
||||
|
@ -474,7 +462,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
if (mt!=null)
|
||||
response.setContentType(mt);
|
||||
}
|
||||
sendData(request,response,included.booleanValue(),resource,content,reqRanges,byteRangeRules);
|
||||
sendData(request,response,included.booleanValue(),resource,content,reqRanges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -724,18 +712,17 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
boolean include,
|
||||
Resource resource,
|
||||
HttpContent content,
|
||||
Enumeration reqRanges,
|
||||
boolean byteRangeRules)
|
||||
Enumeration reqRanges)
|
||||
throws IOException
|
||||
{
|
||||
long content_length=resource.length();
|
||||
long content_length=content==null?resource.length():content.getContentLength();
|
||||
|
||||
// Get the output stream (or writer)
|
||||
OutputStream out =null;
|
||||
try{out = response.getOutputStream();}
|
||||
catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());}
|
||||
|
||||
if ( reqRanges == null || !reqRanges.hasMoreElements())
|
||||
if ( reqRanges == null || !reqRanges.hasMoreElements() || content_length<0)
|
||||
{
|
||||
// if there were no ranges, send entire entity
|
||||
if (include)
|
||||
|
@ -774,7 +761,9 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
else
|
||||
{
|
||||
// Parse the satisfiable ranges
|
||||
List ranges =InclusiveByteRange.satisfiableRanges(reqRanges,byteRangeRules,content_length);
|
||||
List ranges =InclusiveByteRange.satisfiableRanges(reqRanges,content_length);
|
||||
|
||||
System.err.println(ranges+" "+request.getHeader(HttpHeaders.RANGE)+" "+content_length);
|
||||
|
||||
// if there are no satisfiable ranges, send 416 response
|
||||
if (ranges==null || ranges.size()==0)
|
||||
|
@ -802,18 +791,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
return;
|
||||
}
|
||||
|
||||
// If not following byte range rules (such as when using the "Content-Range" request)
|
||||
// There is no possibility of sending a multi-part range.
|
||||
// See http://tools.ietf.org/html/rfc2616#section-14.16
|
||||
if (!byteRangeRules)
|
||||
{
|
||||
writeHeaders(response,content,content_length);
|
||||
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
response.setHeader(HttpHeaders.CONTENT_RANGE,InclusiveByteRange.to416HeaderRangeString(content_length));
|
||||
resource.writeTo(out,0,content_length);
|
||||
return;
|
||||
}
|
||||
|
||||
// multiple non-overlapping valid ranges cause a multipart
|
||||
// 216 response which does not require an overall
|
||||
// content-length header
|
||||
|
|
|
@ -1152,89 +1152,6 @@ public abstract class RFC2616BaseTest extends AbstractJettyTestCase
|
|||
response.assertBody(ALPHA);
|
||||
}
|
||||
|
||||
private void assertBadContentRange(String rangedef) throws IOException
|
||||
{
|
||||
//
|
||||
// server should ignore all range headers which include
|
||||
// at least one syntactically invalid range
|
||||
//
|
||||
|
||||
StringBuffer req1 = new StringBuffer();
|
||||
req1.append("GET /rfc2616-webapp/alpha.txt HTTP/1.1\n");
|
||||
req1.append("Host: localhost\n");
|
||||
req1.append("Content-Range: ").append(rangedef).append("\n"); // Invalid range
|
||||
req1.append("Connection: close\n");
|
||||
req1.append("\n");
|
||||
|
||||
response = http.request(req1);
|
||||
|
||||
response.assertStatus("BadRange: '" + rangedef + "'",HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE_416);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Content-Range (Header Field) - Bad Range Request
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2616#section-14.16">RFC 2616 (section 14.16)</a>
|
||||
*/
|
||||
@Test
|
||||
public void test14_16_BadContentRange_InvalidSyntax() throws Exception
|
||||
{
|
||||
// server should ignore all range headers which include
|
||||
// at least one syntactically invalid range
|
||||
|
||||
assertBadContentRange("bytes=a-b"); // Invalid due to non-digit entries
|
||||
assertBadContentRange("bytes=-"); // Invalid due to missing range ends
|
||||
assertBadContentRange("bytes=-1-"); // Invalid due negative to end range
|
||||
assertBadContentRange("doublehalfwords=1-2"); // Invalid due to bad key 'doublehalfwords'
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Content-Range (Header Field) - Bad Range Request
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2616#section-14.16">RFC 2616 (section 14.16)</a>
|
||||
*/
|
||||
@Test
|
||||
public void test14_16_BadContentRange_NegativeUse() throws Exception
|
||||
{
|
||||
// server should ignore all range headers which include
|
||||
// at least one syntactically invalid range
|
||||
|
||||
assertBadContentRange("bytes=-1"); // Invalid due negative range use
|
||||
assertBadContentRange("bytes=-1-"); // Invalid due negative to end range
|
||||
assertBadContentRange("bytes=-1-2"); // Invalid due to use of negative range start (not allowed)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Content-Range (Header Field) - Bad Range Request
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2616#section-14.16">RFC 2616 (section 14.16)</a>
|
||||
*/
|
||||
@Test
|
||||
public void test14_16_BadContentRange_MultipleRanges() throws Exception
|
||||
{
|
||||
// server should ignore all range headers which include
|
||||
// at least one syntactically invalid range
|
||||
|
||||
assertBadContentRange("bytes=1-2,4-5"); // Invalid due to multiple entries
|
||||
assertBadContentRange("bytes=a-b,-1-1-1"); // Invalid due to multiple entries, non-digits, and invalid range syntax
|
||||
assertBadContentRange("bytes=-1-2,2-3"); // Invalid because of 2 entries, and use of negative range end
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Content-Range (Header Field) - Bad Range Request
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2616#section-14.16">RFC 2616 (section 14.16)</a>
|
||||
*/
|
||||
@Test
|
||||
public void test14_16_BadContentRange_OutOfRange() throws Exception
|
||||
{
|
||||
// server should ignore all range headers which include
|
||||
// at least one syntactically invalid range
|
||||
|
||||
// A syntactically valid range, but is a range outside of the
|
||||
// size of the data is also considered bad.
|
||||
assertBadContentRange("bytes=50,60");
|
||||
}
|
||||
|
||||
private void assertPartialContentRange(String rangedef, String expectedRange, String expectedBody) throws IOException
|
||||
{
|
||||
|
@ -1244,7 +1161,7 @@ public abstract class RFC2616BaseTest extends AbstractJettyTestCase
|
|||
StringBuffer req1 = new StringBuffer();
|
||||
req1.append("GET /rfc2616-webapp/alpha.txt HTTP/1.1\n");
|
||||
req1.append("Host: localhost\n");
|
||||
req1.append("Content-Range: ").append(rangedef).append("\n"); // Invalid range
|
||||
req1.append("Range: ").append(rangedef).append("\n"); // Invalid range
|
||||
req1.append("Connection: close\n");
|
||||
req1.append("\n");
|
||||
|
||||
|
|
Loading…
Reference in New Issue