int head = 0;
boolean isFirstChunk = true;
String boundary = null;
int off = range.indexOf("bytes=", head);
ServletOutputStream os = null;
if (off < 0)
return false;
off += 6;
while (off > 0 && off < length) {
boolean hasFirst = false;
long first = 0;
boolean hasLast = false;
long last = 0;
int ch = -1;
// Skip whitespace
for (; off < length && (ch = range.charAt(off)) == ' '; off++) {
}
// read range start (before '-')
for (;
off < length && (ch = range.charAt(off)) >= '0' && ch <= '9';
off++) {
first = 10 * first + ch - '0';
hasFirst = true;
}
if (length <= off && ! isFirstChunk)
break;
else if (ch != '-')
return false;
// read range end (before '-')
for (off++;
off < length && (ch = range.charAt(off)) >= '0' && ch <= '9';
off++) {
last = 10 * last + ch - '0';
hasLast = true;
}
// #3766 - browser errors in range
if (off < length && ch != ' ' && ch != ',')
return false;
// Skip whitespace
for (; off < length && (ch = range.charAt(off)) == ' '; off++) {
}
head = off;
long cacheLength = cache.getLength();
if (! hasLast) {
if (first == 0)
return false;
last = cacheLength - 1;
}
// suffix
if (! hasFirst) {
first = cacheLength - last;
last = cacheLength - 1;
}
if (last < first)
break;
if (cacheLength <= last) {
// XXX: actually, an error
break;
}
res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
StringBuilder cb = new StringBuilder();
cb.append("bytes ");
cb.append(first);
cb.append('-');
cb.append(last);
cb.append('/');
cb.append(cacheLength);
String chunkRange = cb.toString();
if (hasMore) {
if (isFirstChunk) {
StringBuilder cb1 = new StringBuilder();
cb1.append("--");
Base64.encode(cb1, RandomUtil.getRandomLong());
boundary = cb1.toString();
res.setContentType("multipart/byteranges; boundary=" + boundary);
os = res.getOutputStream();
}
else {
os.write('\r');
os.write('\n');
}
isFirstChunk = false;
os.write('-');
os.write('-');
os.print(boundary);
os.print("\r\nContent-Type: ");
os.print(mime);
os.print("\r\nContent-Range: ");
os.print(chunkRange);
os.write('\r');
os.write('\n');
os.write('\r');
os.write('\n');
}
else {
res.setContentLength((int) (last - first + 1));
res.addHeader("Content-Range", chunkRange);
}
ReadStream is = null;
try {
is = cache.getPath().openRead();
is.skip(first);
os = res.getOutputStream();
is.writeToStream(os, (int) (last - first + 1));
} finally {
if (is != null)
is.close();
}
for (off--; off < length && range.charAt(off) != ','; off++) {
}
off++;
}
if (hasMore) {
os = res.getOutputStream();
os.write('\r');
os.write('\n');
os.write('-');
os.write('-');
os.print(boundary);
os.write('-');
os.write('-');
os.write('\r');
os.write('\n');
}
return true;
}