Files
AndroidVideoCache/library/src/main/java/com/danikula/videocache/HttpProxyCache.java
2016-08-05 14:36:25 +03:00

108 lines
4.0 KiB
Java

package com.danikula.videocache;
import android.text.TextUtils;
import com.danikula.videocache.file.FileCache;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import static com.danikula.videocache.ProxyCacheUtils.DEFAULT_BUFFER_SIZE;
/**
* {@link ProxyCache} that read http url and writes data to {@link Socket}
*
* @author Alexey Danilov (danikula@gmail.com).
*/
class HttpProxyCache extends ProxyCache {
private static final float NO_CACHE_BARRIER = .2f;
private final HttpUrlSource source;
private final FileCache cache;
private CacheListener listener;
public HttpProxyCache(HttpUrlSource source, FileCache cache) {
super(source, cache);
this.cache = cache;
this.source = source;
}
public void registerCacheListener(CacheListener cacheListener) {
this.listener = cacheListener;
}
public void processRequest(GetRequest request, Socket socket) throws IOException, ProxyCacheException {
OutputStream out = new BufferedOutputStream(socket.getOutputStream());
String responseHeaders = newResponseHeaders(request);
out.write(responseHeaders.getBytes("UTF-8"));
long offset = request.rangeOffset;
if (isUseCache(request)) {
responseWithCache(out, offset);
} else {
responseWithoutCache(out, offset);
}
}
private boolean isUseCache(GetRequest request) throws ProxyCacheException {
int sourceLength = source.length();
boolean sourceLengthKnown = sourceLength > 0;
int cacheAvailable = cache.available();
// do not use cache for partial requests which too far from available cache. It seems user seek video.
return !sourceLengthKnown || !request.partial || request.rangeOffset <= cacheAvailable + sourceLength * NO_CACHE_BARRIER;
}
private String newResponseHeaders(GetRequest request) throws IOException, ProxyCacheException {
String mime = source.getMime();
boolean mimeKnown = !TextUtils.isEmpty(mime);
int length = cache.isCompleted() ? cache.available() : source.length();
boolean lengthKnown = length >= 0;
long contentLength = request.partial ? length - request.rangeOffset : length;
boolean addRange = lengthKnown && request.partial;
return new StringBuilder()
.append(request.partial ? "HTTP/1.1 206 PARTIAL CONTENT\n" : "HTTP/1.1 200 OK\n")
.append("Accept-Ranges: bytes\n")
.append(lengthKnown ? String.format("Content-Length: %d\n", contentLength) : "")
.append(addRange ? String.format("Content-Range: bytes %d-%d/%d\n", request.rangeOffset, length - 1, length) : "")
.append(mimeKnown ? String.format("Content-Type: %s\n", mime) : "")
.append("\n") // headers end
.toString();
}
private void responseWithCache(OutputStream out, long offset) throws ProxyCacheException, IOException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int readBytes;
while ((readBytes = read(buffer, offset, buffer.length)) != -1) {
out.write(buffer, 0, readBytes);
offset += readBytes;
}
out.flush();
}
private void responseWithoutCache(OutputStream out, long offset) throws ProxyCacheException, IOException {
HttpUrlSource newSourceNoCache = new HttpUrlSource(this.source);
try {
newSourceNoCache.open((int) offset);
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int readBytes;
while ((readBytes = newSourceNoCache.read(buffer)) != -1) {
out.write(buffer, 0, readBytes);
offset += readBytes;
}
out.flush();
} finally {
newSourceNoCache.close();
}
}
@Override
protected void onCachePercentsAvailableChanged(int percents) {
if (listener != null) {
listener.onCacheAvailable(cache.file, source.getUrl(), percents);
}
}
}