
org.carlspring.commons.http.range.ByteRangeHeaderParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-http Show documentation
Show all versions of commons-http Show documentation
A library for common HTTP operations.
The newest version!
package org.carlspring.commons.http.range;
import org.carlspring.commons.http.range.validation.ByteRangeValidationException;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* @author mtodorov
* @author Pablo Tirado
*/
public class ByteRangeHeaderParser
{
static final String BYTE_RANGE_NOT_VALID_MESSAGE = "The byte range provided is not valid.";
private String headerContents;
private Validator validator;
public ByteRangeHeaderParser(String headerContents)
{
this.headerContents = headerContents;
initValidationFactory();
}
private void initValidationFactory()
{
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
/**
* Returns the list of ranges denoted by the "Range:" header.
*
* @return
*/
public List getRanges()
{
List byteRanges = new ArrayList<>();
String byteRangesHeader = headerContents.substring(headerContents.lastIndexOf('=') + 1);
long length = byteRangesHeader.contains("/") && !byteRangesHeader.endsWith("/*") ?
Long.parseLong(byteRangesHeader.substring(byteRangesHeader.lastIndexOf('/') + 1)) : 0;
String[] ranges = byteRangesHeader.split(",");
String[] rangesWithoutLength = Arrays.stream(ranges)
.map(range -> range.contains("/") ?
range.substring(0, range.indexOf('/')) :
range)
.toArray(String[]::new);
for (String range : rangesWithoutLength)
{
ByteRange byteRange = createByteRangeFromPosition(range);
if (byteRange != null)
{
byteRange.setTotalLength(length);
Set> violations = validator.validate(byteRange);
if (!violations.isEmpty())
{
handleByteRangeConstraintViolations(violations, byteRange);
}
byteRanges.add(byteRange);
}
else
{
throw new ByteRangeValidationException(BYTE_RANGE_NOT_VALID_MESSAGE);
}
}
return byteRanges;
}
private ByteRange createByteRangeFromPosition(String range)
{
ByteRange byteRange = null;
final String firstNBytesRegex = "^\\d+-?$";
final String betweenNBytesRegex = "^(\\d+-\\d+)$";
final String lastNBytesRegex = "^-\\d+$";
if (range.matches(firstNBytesRegex))
{
Long start;
if (range.endsWith("-"))
{
// Example: 1000- ; Read all bytes after 1000.
start = Long.parseLong(range.substring(0, range.length() - 1));
}
else
{
// Example: 2000 ; Read all bytes after 2000.
start = Long.parseLong(range);
}
byteRange = new ByteRange(start);
}
else if (range.matches(betweenNBytesRegex))
{
// Example: 1000-2000 ; Read bytes 1000-2000 (incl.)
String[] rangeElements = range.split("-");
Long start = Long.parseLong(rangeElements[0]);
Long end = Long.parseLong(rangeElements[1]);
byteRange = new ByteRange(start, end);
}
else if (range.matches(lastNBytesRegex))
{
// Example: -2000 ; Read the last 2000 bytes.
Long start = 0L;
Long end = Long.parseLong(range);
byteRange = new ByteRange(start, end);
}
return byteRange;
}
private void handleByteRangeConstraintViolations(Set> violations,
ByteRange byteRange)
{
Optional errorMessageOptional = violations.stream().findFirst().map(ConstraintViolation::getMessage);
if (errorMessageOptional.isPresent())
{
String byteRangeStr =
byteRange.getTotalLength() != 0 ? byteRange.toString() + "/" + byteRange.getTotalLength() :
byteRange.toString();
throw new ByteRangeValidationException(byteRangeStr + ": " + errorMessageOptional.get());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy