handle Apache and Nginx ETags when checking if index is current
fdroid/fdroidclient#1708
This commit is contained in:
		
							parent
							
								
									9323ccdfd1
								
							
						
					
					
						commit
						afe6de94a0
					
				@ -95,12 +95,35 @@ public class HttpDownloader extends Downloader {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a remote file, checking the HTTP response code and the {@code etag}.
 | 
			
		||||
     * In order to prevent the {@code etag} from being used as a form of tracking
 | 
			
		||||
     * cookie, this code never sends the {@code etag} to the server.  Instead, it
 | 
			
		||||
     * uses a {@code HEAD} request to get the {@code etag} from the server, then
 | 
			
		||||
     * only issues a {@code GET} if the {@code etag} has changed.
 | 
			
		||||
     * Get a remote file, checking the HTTP response code, if it has changed since
 | 
			
		||||
     * the last time a download was tried.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * If the {@code ETag} does not match, it could be caused by the previous
 | 
			
		||||
     * download of the same file coming from a mirror running on a different
 | 
			
		||||
     * webserver, e.g. Apache vs Nginx.  {@code Content-Length} and
 | 
			
		||||
     * {@code Last-Modified} are used to check whether the file has changed since
 | 
			
		||||
     * those are more standardized than {@code ETag}.  Plus, Nginx and Apache 2.4
 | 
			
		||||
     * defaults use only those two values to generate the {@code ETag} anyway.
 | 
			
		||||
     * Unfortunately, other webservers and CDNs have totally different methods
 | 
			
		||||
     * for generating the {@code ETag}.  And mirrors that are syncing using a
 | 
			
		||||
     * method other than {@code rsync} could easily have different {@code Last-Modified}
 | 
			
		||||
     * times on the exact same file.  On top of that, some services like GitHub's
 | 
			
		||||
     * raw file support {@code raw.githubusercontent.com} and GitLab's raw file
 | 
			
		||||
     * support do not set the {@code Last-Modified} header at all.  So ultimately,
 | 
			
		||||
     * then {@code ETag} needs to be used first and foremost, then this calculated
 | 
			
		||||
     * {@code ETag} can serve as a common fallback.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * In order to prevent the {@code ETag} from being used as a form of tracking
 | 
			
		||||
     * cookie, this code never sends the {@code ETag} to the server.  Instead, it
 | 
			
		||||
     * uses a {@code HEAD} request to get the {@code ETag} from the server, then
 | 
			
		||||
     * only issues a {@code GET} if the {@code ETag} has changed.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This uses a integer value for {@code Last-Modified} to avoid enabling the
 | 
			
		||||
     * use of that value as some kind of "cookieless cookie".  One second time
 | 
			
		||||
     * resolution should be plenty since these files change more on the time
 | 
			
		||||
     * space of minutes or hours.
 | 
			
		||||
     *
 | 
			
		||||
     * @see <a href="https://gitlab.com/fdroid/fdroidclient/issues/1708">update index from any available mirror</a>
 | 
			
		||||
     * @see <a href="http://lucb1e.com/rp/cookielesscookies">Cookieless cookies</a>
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
@ -108,7 +131,6 @@ public class HttpDownloader extends Downloader {
 | 
			
		||||
        // get the file size from the server
 | 
			
		||||
        HttpURLConnection tmpConn = getConnection();
 | 
			
		||||
        tmpConn.setRequestMethod("HEAD");
 | 
			
		||||
        String etag = tmpConn.getHeaderField(HEADER_FIELD_ETAG);
 | 
			
		||||
 | 
			
		||||
        int contentLength = -1;
 | 
			
		||||
        int statusCode = tmpConn.getResponseCode();
 | 
			
		||||
@ -116,10 +138,21 @@ public class HttpDownloader extends Downloader {
 | 
			
		||||
        newFileAvailableOnServer = false;
 | 
			
		||||
        switch (statusCode) {
 | 
			
		||||
            case HttpURLConnection.HTTP_OK:
 | 
			
		||||
                String headETag = tmpConn.getHeaderField(HEADER_FIELD_ETAG);
 | 
			
		||||
                contentLength = tmpConn.getContentLength();
 | 
			
		||||
                if (!TextUtils.isEmpty(etag) && etag.equals(cacheTag)) {
 | 
			
		||||
                    Utils.debugLog(TAG, urlString + " is cached, not downloading");
 | 
			
		||||
                if (!TextUtils.isEmpty(cacheTag)) {
 | 
			
		||||
                    if (cacheTag.equals(headETag)) {
 | 
			
		||||
                        Utils.debugLog(TAG, urlString + " cached, not downloading: " + headETag);
 | 
			
		||||
                        return;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        String calcedETag = String.format("\"%x-%x\"",
 | 
			
		||||
                                tmpConn.getLastModified() / 1000, contentLength);
 | 
			
		||||
                        if (calcedETag.equals(headETag)) {
 | 
			
		||||
                            Utils.debugLog(TAG, urlString + " cached based on calced ETag, not downloading: " +
 | 
			
		||||
                                    headETag);
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                newFileAvailableOnServer = true;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user