Handle 409 Conflict status codes (fixes #563)

pull/2/head
Ricki Hirner 9 years ago
parent 59088086fd
commit ad733ebff1

@ -13,8 +13,10 @@ import android.test.InstrumentationTestCase;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays; import java.util.Arrays;
import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLPeerUnverifiedException;
@ -226,16 +228,24 @@ public class WebDavResourceTest extends InstrumentationTestCase {
} }
} }
public void testPutUpdateDontOverwrite() throws Exception { public void testPutUpdateDontOverwrite() throws Exception {
// should succeed on an existing file // should succeed on an existing file
assertEquals("has-just-been-updated", davExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE)); assertEquals("has-just-been-updated", davExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE));
// should fail on a non-existing file // should fail on a non-existing file (resource has been deleted on server, thus server returns 412)
try { try {
davNonExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE); davNonExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE);
fail(); fail();
} catch(PreconditionFailedException ex) { } catch(PreconditionFailedException ex) {
} }
// should fail on existing file with wrong ETag (resource has changed on server, thus server returns 409)
try {
WebDavResource dav = new WebDavResource(davCollection, new URI("collection/existing.file?conflict=1"));
dav.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE);
fail();
} catch(ConflictException ex) {
}
} }
public void testDelete() throws Exception { public void testDelete() throws Exception {

@ -245,6 +245,9 @@ exports.getBodyParts = function(conf) {
if (req.method == "PUT") { if (req.method == "PUT") {
if (req.headers['if-none-match']) /* requested "don't overwrite", but this file exists */ if (req.headers['if-none-match']) /* requested "don't overwrite", but this file exists */
res.statusCode = 412; res.statusCode = 412;
else if (req.headers['if-match'] && req.queryParams && req.queryParams.conflict)
/* requested "don't overwrite", but this file exists with newer content */
res.statusCode = 409;
else { else {
res.statusCode = 204; res.statusCode = 204;
res.headers["ETag"] = "has-just-been-updated"; res.headers["ETag"] = "has-just-been-updated";

@ -7,9 +7,6 @@
*/ */
package at.bitfire.davdroid.resource; package at.bitfire.davdroid.resource;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable; import java.io.Serializable;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;

@ -21,6 +21,7 @@ import at.bitfire.davdroid.resource.LocalStorageException;
import at.bitfire.davdroid.resource.RecordNotFoundException; import at.bitfire.davdroid.resource.RecordNotFoundException;
import at.bitfire.davdroid.resource.RemoteCollection; import at.bitfire.davdroid.resource.RemoteCollection;
import at.bitfire.davdroid.resource.Resource; import at.bitfire.davdroid.resource.Resource;
import at.bitfire.davdroid.webdav.ConflictException;
import at.bitfire.davdroid.webdav.DavException; import at.bitfire.davdroid.webdav.DavException;
import at.bitfire.davdroid.webdav.HttpException; import at.bitfire.davdroid.webdav.HttpException;
import at.bitfire.davdroid.webdav.NotFoundException; import at.bitfire.davdroid.webdav.NotFoundException;
@ -141,7 +142,7 @@ public class SyncManager {
local.updateETag(res, eTag); local.updateETag(res, eTag);
local.clearDirty(res); local.clearDirty(res);
count++; count++;
} catch (PreconditionFailedException e) { } catch (PreconditionFailedException|ConflictException e) {
Log.i(TAG, "Didn't overwrite existing resource with other content"); Log.i(TAG, "Didn't overwrite existing resource with other content");
} catch (RecordNotFoundException e) { } catch (RecordNotFoundException e) {
Log.wtf(TAG, "Couldn't read new record", e); Log.wtf(TAG, "Couldn't read new record", e);
@ -165,7 +166,7 @@ public class SyncManager {
local.updateETag(res, eTag); local.updateETag(res, eTag);
local.clearDirty(res); local.clearDirty(res);
count++; count++;
} catch (PreconditionFailedException e) { } catch (PreconditionFailedException|ConflictException e) {
Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile"); Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile");
} catch (RecordNotFoundException e) { } catch (RecordNotFoundException e) {
Log.e(TAG, "Couldn't read dirty record", e); Log.e(TAG, "Couldn't read dirty record", e);

@ -10,7 +10,6 @@ package at.bitfire.davdroid.ui.setup;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.text.Html; import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;

@ -9,7 +9,6 @@ package at.bitfire.davdroid.ui.setup;
import android.app.ListFragment; import android.app.ListFragment;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;

@ -0,0 +1,17 @@
/*
* Copyright © 2013 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid.webdav;
import org.apache.http.HttpStatus;
public class ConflictException extends HttpException {
public ConflictException(String reason) {
super(HttpStatus.SC_CONFLICT, reason);
}
}

@ -426,6 +426,8 @@ public class WebDavResource {
throw new NotAuthorizedException(reason); throw new NotAuthorizedException(reason);
case HttpStatus.SC_NOT_FOUND: case HttpStatus.SC_NOT_FOUND:
throw new NotFoundException(reason); throw new NotFoundException(reason);
case HttpStatus.SC_CONFLICT:
throw new ConflictException(reason);
case HttpStatus.SC_PRECONDITION_FAILED: case HttpStatus.SC_PRECONDITION_FAILED:
throw new PreconditionFailedException(reason); throw new PreconditionFailedException(reason);
default: default:

@ -12,7 +12,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.0.0' classpath 'com.android.tools.build:gradle:1.0.1'
} }
} }

@ -30,7 +30,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.0.0' classpath 'com.android.tools.build:gradle:1.0.1'
} }
} }

Loading…
Cancel
Save