use of com.liulishuo.filedownloader.connection.FileDownloadConnection in project FileDownloader by lingochamp.
the class ConnectTask method connect.
FileDownloadConnection connect() throws IOException, IllegalAccessException {
FileDownloadConnection connection = CustomComponentHolder.getImpl().createConnection(url);
addUserRequiredHeader(connection);
addRangeHeader(connection);
fixNeededHeader(connection);
// init request
// get the request header in here, because of there are many connection
// component(such as HttpsURLConnectionImpl, HttpURLConnectionImpl in okhttp3) don't
// allow access to the request header after it connected.
requestHeader = connection.getRequestHeaderFields();
if (FileDownloadLog.NEED_LOG) {
FileDownloadLog.d(this, "<---- %s request header %s", downloadId, requestHeader);
}
connection.execute();
redirectedUrlList = new ArrayList<>();
connection = RedirectHandler.process(requestHeader, connection, redirectedUrlList);
if (FileDownloadLog.NEED_LOG) {
FileDownloadLog.d(this, "----> %s response header %s", downloadId, connection.getResponseHeaderFields());
}
return connection;
}
use of com.liulishuo.filedownloader.connection.FileDownloadConnection in project FileDownloader by lingochamp.
the class DownloadRunnable method run.
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
FileDownloadConnection connection = null;
final long beginOffset = connectTask.getProfile().currentOffset;
boolean isConnected = false;
do {
try {
if (paused) {
return;
}
isConnected = false;
connection = connectTask.connect();
final int code = connection.getResponseCode();
if (FileDownloadLog.NEED_LOG) {
FileDownloadLog.d(this, "the connection[%d] for %d, is connected %s with code[%d]", connectionIndex, downloadId, connectTask.getProfile(), code);
}
if (code != HttpURLConnection.HTTP_PARTIAL && code != HttpURLConnection.HTTP_OK) {
throw new SocketException(FileDownloadUtils.formatString("Connection failed with request[%s] response[%s] " + "http-state[%d] on task[%d-%d], which is changed" + " after verify connection, so please try again.", connectTask.getRequestHeader(), connection.getResponseHeaderFields(), code, downloadId, connectionIndex));
}
isConnected = true;
final FetchDataTask.Builder builder = new FetchDataTask.Builder();
if (paused)
return;
fetchDataTask = builder.setDownloadId(downloadId).setConnectionIndex(connectionIndex).setCallback(callback).setHost(this).setWifiRequired(isWifiRequired).setConnection(connection).setConnectionProfile(this.connectTask.getProfile()).setPath(path).build();
fetchDataTask.run();
if (paused)
fetchDataTask.pause();
break;
} catch (IllegalAccessException | IOException | FileDownloadGiveUpRetryException | IllegalArgumentException e) {
if (callback.isRetry(e)) {
if (isConnected && fetchDataTask == null) {
// connected but create fetch data task failed, give up directly.
FileDownloadLog.w(this, "it is valid to retry and connection is valid but" + " create fetch-data-task failed, so give up directly with %s", e);
callback.onError(e);
break;
} else {
if (fetchDataTask != null) {
// update currentOffset in ConnectionProfile
final long downloadedOffset = getDownloadedOffset();
if (downloadedOffset > 0) {
connectTask.updateConnectionProfile(downloadedOffset);
}
}
callback.onRetry(e);
}
} else {
callback.onError(e);
break;
}
} finally {
if (connection != null)
connection.ending();
}
} while (true);
}
use of com.liulishuo.filedownloader.connection.FileDownloadConnection in project FileDownloader by lingochamp.
the class DownloadRunnableTest method run_responseCodeNotMet_error.
@Test
public void run_responseCodeNotMet_error() throws IOException, IllegalAccessException {
final FileDownloadConnection connection = mock(FileDownloadConnection.class);
when(connection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_PRECON_FAILED);
when(mockConnectTask.connect()).thenReturn(connection);
downloadRunnable.run();
// retry first.
verify(mockCallback).onRetry(any(Exception.class));
// then callback error.
verify(mockCallback).onError(any(Exception.class));
}
use of com.liulishuo.filedownloader.connection.FileDownloadConnection in project FileDownloader by lingochamp.
the class FileDownloadRunnable method loop.
@SuppressWarnings("ConstantConditions")
private void loop(FileDownloadModel model) {
int retryingTimes = 0;
boolean revisedInterval = false;
FileDownloadConnection connection = null;
do {
// loop for retry
long soFar = 0;
final int id = mId;
try {
// Step 1, check state
if (checkState()) {
if (FileDownloadLog.NEED_LOG) {
FileDownloadLog.d(this, "already canceled %d %d", id, model.getStatus());
}
onPause();
break;
}
if (FileDownloadLog.NEED_LOG) {
FileDownloadLog.d(FileDownloadRunnable.class, "start download %s %s", id, model.getUrl());
}
// Step 2, handle resume from breakpoint
checkIsResumeAvailable();
connection = mConnectionCreator.create(model.getUrl());
addHeader(connection);
// start download----------------
// Step 3, init request
// get the request header in here, because of there are many connection
// component(such as HttpsURLConnectionImpl, HttpURLConnectionImpl in okhttp3) don't
// allow access to the request header after it connected.
final Map<String, List<String>> requestHeader = connection.getRequestHeaderFields();
if (FileDownloadLog.NEED_LOG) {
FileDownloadLog.d(this, "%s request header %s", id, requestHeader);
}
// Step 4, build connect
connection.execute();
final int code = connection.getResponseCode();
final boolean isSucceedStart = code == HttpURLConnection.HTTP_OK || code == FileDownloadConnection.NO_RESPONSE_CODE;
// if the response status code isn't point to PARTIAL/OFFSET, isSucceedResume will
// be assigned to false, so filedownloader will download the file from very beginning.
final boolean isSucceedResume = ((code == HttpURLConnection.HTTP_PARTIAL) || (code == FileDownloadConnection.RESPONSE_CODE_FROM_OFFSET)) && isResumeDownloadAvailable;
if (isResumeDownloadAvailable && !isSucceedResume) {
FileDownloadLog.d(this, "want to resume from the breakpoint[%d], but the " + "response status code is[%d]", model.getSoFar(), code);
}
if (isSucceedStart || isSucceedResume) {
long total = model.getTotal();
final String transferEncoding = connection.getResponseHeaderField("Transfer-Encoding");
// Step 5, check response's header
if (isSucceedStart || total <= 0) {
if (transferEncoding == null) {
total = FileDownloadUtils.convertContentLengthString(connection.getResponseHeaderField("Content-Length"));
} else {
// if transfer not nil, ignore content-length
total = TOTAL_VALUE_IN_CHUNKED_RESOURCE;
}
}
// TODO consider if not is chunked & http 1.0/(>=http1.1 & connect not be keep live) may not give content-length
if (total < 0) {
// invalid total length
final boolean isEncodingChunked = transferEncoding != null && transferEncoding.equals("chunked");
if (!isEncodingChunked) {
// not chunked transfer encoding data
if (FileDownloadProperties.getImpl().HTTP_LENIENT) {
// do not response content-length either not chunk transfer encoding,
// but HTTP lenient is true, so handle as the case of transfer encoding chunk
total = TOTAL_VALUE_IN_CHUNKED_RESOURCE;
if (FileDownloadLog.NEED_LOG) {
FileDownloadLog.d(this, "%d response header is not legal but " + "HTTP lenient is true, so handle as the case of " + "transfer encoding chunk", id);
}
} else {
throw new FileDownloadGiveUpRetryException("can't know the size of the " + "download file, and its Transfer-Encoding is not Chunked " + "either.\nyou can ignore such exception by add " + "http.lenient=true to the filedownloader.properties");
}
}
}
if (isSucceedResume) {
soFar = model.getSoFar();
}
// Step 6, callback on connected, and update header to db. for save etag.
onConnected(isSucceedResume, total, findEtag(connection), findFilename(connection));
// Step 7, check whether has same task running after got filename from server/local generate.
if (model.isPathAsDirectory()) {
// this scope for caring about the case of there is another task is provided
// the same path to store file and the same url.
final String targetFilePath = model.getTargetFilePath();
// get the ID after got the filename.
final int fileCaseId = FileDownloadUtils.generateId(model.getUrl(), targetFilePath);
// whether the file with the filename has been existed.
if (FileDownloadHelper.inspectAndInflowDownloaded(id, targetFilePath, isForceReDownload, false)) {
helper.remove(id);
break;
}
final FileDownloadModel fileCaseModel = helper.find(fileCaseId);
if (fileCaseModel != null) {
// whether the another task with the same file and url is downloading.
if (FileDownloadHelper.inspectAndInflowDownloading(id, fileCaseModel, threadPoolMonitor, false)) {
//it has been post to upper layer the 'warn' message, so the current
// task no need to continue download.
helper.remove(id);
break;
}
// the another task with the same file name and url is paused
helper.remove(fileCaseId);
deleteTargetFile();
if (FileDownloadMgr.isBreakpointAvailable(fileCaseId, fileCaseModel)) {
model.setSoFar(fileCaseModel.getSoFar());
model.setTotal(fileCaseModel.getTotal());
model.setETag(fileCaseModel.getETag());
helper.update(model);
// re connect to resume from breakpoint.
continue;
}
}
// whether there is an another running task with the same target-file-path.
if (FileDownloadHelper.inspectAndInflowConflictPath(id, model.getSoFar(), getTempFilePath(), targetFilePath, threadPoolMonitor)) {
helper.remove(id);
break;
}
}
// Step 8, start fetch datum from input stream & write to file
if (fetch(connection, isSucceedResume, soFar, total)) {
break;
}
} else {
final FileDownloadHttpException httpException = new FileDownloadHttpException(code, requestHeader, connection.getResponseHeaderFields());
if (revisedInterval) {
throw httpException;
}
revisedInterval = true;
switch(code) {
case HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
deleteTaskFiles();
FileDownloadLog.w(FileDownloadRunnable.class, "%d response code %d, " + "range[%d] isn't make sense, so delete the dirty file[%s]" + ", and try to redownload it from byte-0.", id, code, model.getSoFar(), model.getTempFilePath());
onRetry(httpException, retryingTimes++);
break;
default:
throw httpException;
}
}
} catch (Throwable ex) {
// TODO 决策是否需要重试,是否是用户决定,或者根据错误码处理
if (autoRetryTimes > retryingTimes++ && !(ex instanceof FileDownloadGiveUpRetryException)) {
// retry
onRetry(ex, retryingTimes);
} else {
// error
onError(ex);
break;
}
} finally {
if (connection != null) {
connection.ending();
}
}
} while (true);
}
use of com.liulishuo.filedownloader.connection.FileDownloadConnection in project FileDownloader by lingochamp.
the class DownloadLaunchRunnable method trialConnect.
// the trial connection is for: 1. etag verify; 2. partial support verify.
private void trialConnect() throws IOException, RetryDirectly, IllegalAccessException {
FileDownloadConnection trialConnection = null;
try {
final ConnectionProfile trialConnectionProfile;
if (isNeedForceDiscardRange) {
trialConnectionProfile = ConnectionProfile.ConnectionProfileBuild.buildTrialConnectionProfileNoRange();
} else {
trialConnectionProfile = ConnectionProfile.ConnectionProfileBuild.buildTrialConnectionProfile();
}
final ConnectTask trialConnectTask = new ConnectTask.Builder().setDownloadId(model.getId()).setUrl(model.getUrl()).setEtag(model.getETag()).setHeader(userRequestHeader).setConnectionProfile(trialConnectionProfile).build();
trialConnection = trialConnectTask.connect();
handleTrialConnectResult(trialConnectTask.getRequestHeader(), trialConnectTask, trialConnection);
} finally {
if (trialConnection != null)
trialConnection.ending();
}
}
Aggregations