Search in sources :

Example 1 with FileDownloadConnection

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;
}
Also used : FileDownloadConnection(com.liulishuo.filedownloader.connection.FileDownloadConnection)

Example 2 with FileDownloadConnection

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);
}
Also used : SocketException(java.net.SocketException) FileDownloadConnection(com.liulishuo.filedownloader.connection.FileDownloadConnection) FileDownloadGiveUpRetryException(com.liulishuo.filedownloader.exception.FileDownloadGiveUpRetryException) IOException(java.io.IOException)

Example 3 with FileDownloadConnection

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));
}
Also used : FileDownloadConnection(com.liulishuo.filedownloader.connection.FileDownloadConnection) IOException(java.io.IOException) Test(org.junit.Test)

Example 4 with FileDownloadConnection

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);
}
Also used : FileDownloadHttpException(com.liulishuo.filedownloader.exception.FileDownloadHttpException) FileDownloadConnection(com.liulishuo.filedownloader.connection.FileDownloadConnection) FileDownloadGiveUpRetryException(com.liulishuo.filedownloader.exception.FileDownloadGiveUpRetryException) FileDownloadModel(com.liulishuo.filedownloader.model.FileDownloadModel) List(java.util.List)

Example 5 with FileDownloadConnection

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();
    }
}
Also used : FileDownloadConnection(com.liulishuo.filedownloader.connection.FileDownloadConnection)

Aggregations

FileDownloadConnection (com.liulishuo.filedownloader.connection.FileDownloadConnection)5 FileDownloadGiveUpRetryException (com.liulishuo.filedownloader.exception.FileDownloadGiveUpRetryException)2 IOException (java.io.IOException)2 FileDownloadHttpException (com.liulishuo.filedownloader.exception.FileDownloadHttpException)1 FileDownloadModel (com.liulishuo.filedownloader.model.FileDownloadModel)1 SocketException (java.net.SocketException)1 List (java.util.List)1 Test (org.junit.Test)1