package diskCacheV111.util; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; import dmg.util.HttpException; public class HttpByteRange { private final long _lower; private final long _upper; public HttpByteRange(long lower, long upper){ _lower = lower; _upper = upper; } public long getLower() { return _lower; } public long getUpper() { return _upper; } public long getSize(){ return _upper - _lower + 1; } /* * ranges-specifier = byte-ranges-specifier * byte-ranges-specifier = bytes-unit "=" byte-range-set * byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec ) * byte-range-spec = first-byte-pos "-" [last-byte-pos] * first-byte-pos = 1*DIGIT * last-byte-pos = 1*DIGIT * suffix-byte-range-spec = "-" suffix-length * suffix-length = 1*DIGIT */ public static List<HttpByteRange> parseRanges(String rangeString, long lower, long upper) throws HttpException { String []splitEquals=rangeString.split("="); if(splitEquals.length != 2 || !"bytes".equals(splitEquals[0])) { throw new HttpException(HttpServletResponse.SC_BAD_REQUEST, "Invalid definition of ranges"); } String[] csl = splitEquals[1].split(","); List<HttpByteRange> ret = new ArrayList<>(csl.length); for(String rangeSpec: csl){ try{ // byte-range-set may contain "optional linear whitespace" rangeSpec = rangeSpec.trim(); ret.add(parseRange(rangeSpec,lower,upper)); }catch(HttpException e){ //RFC: The recipient of an invalid byte-range-spec // must ignore it. //The RFC doesn't seem to say anything about invalid suffix-range-specs //but I'm assuming they should be treated equally. } } if(ret.isEmpty()) { throw new HttpException(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, "Invalid (empty) list of valid ranges"); } return ret; } /* * range = ( byte-range-spec | suffix-byte-range-spec ) */ private static HttpByteRange parseRange(String rangeSpec, long lower, long upper) throws HttpException{ HttpByteRange ret; if(rangeSpec.isEmpty()) { throw new HttpException(HttpServletResponse.SC_BAD_REQUEST, "Invalid (empty) range"); } if(Character.isDigit(rangeSpec.charAt(0))){ ret = parseRangeSpec( rangeSpec, lower, upper); }else if(rangeSpec.charAt(0) == '-'){ ret = parseSuffixRangeSpec( rangeSpec, lower, upper); }else { throw new HttpException(HttpServletResponse.SC_BAD_REQUEST, "Invalid range"); } return ret; } /* byte-range-spec = first-byte-pos "-" [last-byte-pos] */ private static HttpByteRange parseRangeSpec(String rangeSpec, long lower, long upper) throws HttpException { String[] bounds = rangeSpec.split("-"); if(bounds.length > 2) { throw new HttpException(HttpServletResponse.SC_BAD_REQUEST, "Invalid number of range components"); } HttpByteRange ret; try{ long lowerV = Long.parseLong(bounds[0]); long upperV = bounds.length==1 ? upper : Math.min(upper,Long.parseLong(bounds[1])); /* semantics check*/ if(lowerV >= lower && lowerV<=upperV) { ret = new HttpByteRange(lowerV, upperV); } else { throw new HttpException(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, "Invalid range bounds"); } }catch(NumberFormatException e){ throw new HttpException(HttpServletResponse.SC_BAD_REQUEST, "Invalid numeric value"); } return ret; } /* suffix-byte-range-spec = "-" suffix-length */ private static HttpByteRange parseSuffixRangeSpec(String rangeSpec, long lower, long upper) throws HttpException { String bound = rangeSpec.substring(1); HttpByteRange ret; try{ long suffix = Long.valueOf(bound)-1; if(suffix < 0) { throw new HttpException(HttpServletResponse.SC_BAD_REQUEST, "Invalid suffix range"); } long lowerV = Math.max(upper - suffix,lower); ret = new HttpByteRange(lowerV,upper); }catch(NumberFormatException e){ throw new HttpException(HttpServletResponse.SC_BAD_REQUEST, "Invalid numeric value"); } return ret; } }