зеркало из https://github.com/mozilla/gecko-dev.git
Bug 747065 - Precondition failed on clients PUT. r=nalexander, a=blocking-fennec
This commit is contained in:
Родитель
97b5e3bdb0
Коммит
0cdab01fea
|
@ -191,7 +191,9 @@ public class SyncConfiguration implements CredentialsSource {
|
|||
public String prefsPath;
|
||||
public PrefsSource prefsSource;
|
||||
|
||||
public static final String CLIENT_RECORD_TIMESTAMP = "serverClientRecordTimestamp";
|
||||
public static final String CLIENTS_COLLECTION_TIMESTAMP = "serverClientsTimestamp"; // When the collection was touched.
|
||||
public static final String CLIENT_RECORD_TIMESTAMP = "serverClientRecordTimestamp"; // When our record was touched.
|
||||
|
||||
public static final String PREF_CLUSTER_URL = "clusterURL";
|
||||
public static final String PREF_SYNC_ID = "syncID";
|
||||
|
||||
|
@ -298,6 +300,10 @@ public class SyncConfiguration implements CredentialsSource {
|
|||
(trailingSlash ? "/storage/" : "/storage");
|
||||
}
|
||||
|
||||
public URI collectionURI(String collection) throws URISyntaxException {
|
||||
return new URI(storageURL(true) + collection);
|
||||
}
|
||||
|
||||
public URI collectionURI(String collection, boolean full) throws URISyntaxException {
|
||||
// Do it this way to make it easier to add more params later.
|
||||
// It's pretty ugly, I'll grant.
|
||||
|
@ -371,6 +377,10 @@ public class SyncConfiguration implements CredentialsSource {
|
|||
return this.getPrefs().edit();
|
||||
}
|
||||
|
||||
/**
|
||||
* We persist two different clients timestamps: our own record's,
|
||||
* and the timestamp for the collection.
|
||||
*/
|
||||
public void persistServerClientRecordTimestamp(long timestamp) {
|
||||
getEditor().putLong(SyncConfiguration.CLIENT_RECORD_TIMESTAMP, timestamp).commit();
|
||||
}
|
||||
|
@ -379,6 +389,14 @@ public class SyncConfiguration implements CredentialsSource {
|
|||
return getPrefs().getLong(SyncConfiguration.CLIENT_RECORD_TIMESTAMP, 0);
|
||||
}
|
||||
|
||||
public void persistServerClientsTimestamp(long timestamp) {
|
||||
getEditor().putLong(SyncConfiguration.CLIENTS_COLLECTION_TIMESTAMP, timestamp).commit();
|
||||
}
|
||||
|
||||
public long getPersistedServerClientsTimestamp() {
|
||||
return getPrefs().getLong(SyncConfiguration.CLIENTS_COLLECTION_TIMESTAMP, 0);
|
||||
}
|
||||
|
||||
public void purgeCryptoKeys() {
|
||||
if (collectionKeys != null) {
|
||||
collectionKeys.clear();
|
||||
|
|
|
@ -180,7 +180,12 @@ public class Utils {
|
|||
}
|
||||
|
||||
public static String millisecondsToDecimalSecondsString(long ms) {
|
||||
return new BigDecimal(ms).movePointLeft(3).toString();
|
||||
return millisecondsToDecimalSeconds(ms).toString();
|
||||
}
|
||||
|
||||
// For dumping into JSON without quotes.
|
||||
public static BigDecimal millisecondsToDecimalSeconds(long ms) {
|
||||
return new BigDecimal(ms).movePointLeft(3);
|
||||
}
|
||||
|
||||
// This lives until Bug 708956 lands, and we don't have to do it any more.
|
||||
|
|
|
@ -1,39 +1,6 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Android Sync Client.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.sync.net;
|
||||
|
||||
|
@ -41,10 +8,12 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.mozilla.gecko.sync.CryptoRecord;
|
||||
import org.mozilla.gecko.sync.ThreadPool;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.entity.StringEntity;
|
||||
|
||||
/**
|
||||
|
@ -88,20 +57,36 @@ public class SyncStorageRecordRequest extends SyncStorageRequest {
|
|||
return new SyncStorageRecordResourceDelegate(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for turning a JSON object into a payload.
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
protected StringEntity jsonEntity(JSONObject body) throws UnsupportedEncodingException {
|
||||
StringEntity e = new StringEntity(body.toJSONString(), "UTF-8");
|
||||
protected static StringEntity stringEntity(String s) throws UnsupportedEncodingException {
|
||||
StringEntity e = new StringEntity(s, "UTF-8");
|
||||
e.setContentType("application/json");
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for turning a JSON object into a payload.
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
protected static StringEntity jsonEntity(JSONObject body) throws UnsupportedEncodingException {
|
||||
return stringEntity(body.toJSONString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for turning a JSON array into a payload.
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
protected static HttpEntity jsonEntity(JSONArray toPOST) throws UnsupportedEncodingException {
|
||||
return stringEntity(toPOST.toJSONString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void post(JSONObject body) {
|
||||
// Let's do this the trivial way for now.
|
||||
// Note that POSTs should be an array, so we wrap here.
|
||||
final JSONArray toPOST = new JSONArray();
|
||||
toPOST.add(body);
|
||||
try {
|
||||
this.resource.post(jsonEntity(body));
|
||||
this.resource.post(jsonEntity(toPOST));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
this.delegate.handleRequestError(e);
|
||||
}
|
||||
|
|
|
@ -1,47 +1,13 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Android Sync Client.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.sync.net;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.util.Log;
|
||||
import org.mozilla.gecko.sync.Logger;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
|
||||
|
@ -81,7 +47,7 @@ public class SyncStorageResponse extends SyncResponse {
|
|||
SERVER_ERROR_MESSAGES = errors;
|
||||
}
|
||||
public static String getServerErrorMessage(String body) {
|
||||
Log.d(LOG_TAG, "Looking up message for body \"" + body + "\"");
|
||||
Logger.debug(LOG_TAG, "Looking up message for body \"" + body + "\"");
|
||||
if (SERVER_ERROR_MESSAGES.containsKey(body)) {
|
||||
return SERVER_ERROR_MESSAGES.get(body);
|
||||
}
|
||||
|
|
|
@ -64,11 +64,21 @@ public class ClientRecord extends Record {
|
|||
putPayload(payload, "type", this.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof ClientRecord) || !super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.equalPayloads(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalPayloads(Object o) {
|
||||
if (!(o instanceof ClientRecord) || !super.equalPayloads(o)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClientRecord other = (ClientRecord) o;
|
||||
if (!RepoUtils.stringsEqual(other.name, this.name) ||
|
||||
!RepoUtils.stringsEqual(other.type, this.type)) {
|
||||
|
|
|
@ -109,7 +109,11 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
|||
|
||||
@Override
|
||||
public void handleRequestSuccess(SyncStorageResponse response) {
|
||||
BaseResource.consumeEntity(response); // We don't need the response at all.
|
||||
|
||||
// Hang onto the server's last modified timestamp to use
|
||||
// in X-If-Unmodified-Since for upload.
|
||||
session.config.persistServerClientsTimestamp(response.normalizedWeaveTimestamp());
|
||||
BaseResource.consumeEntity(response);
|
||||
|
||||
// If we successfully downloaded all records but ours was not one of them
|
||||
// then reset the timestamp.
|
||||
|
@ -170,13 +174,9 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
|||
r = (ClientRecord) factory.createRecord(record.decrypt());
|
||||
if (clientsDelegate.isLocalGUID(r.guid)) {
|
||||
Logger.info(LOG_TAG, "Local client GUID exists on server and was downloaded");
|
||||
localAccountGUIDDownloaded = true;
|
||||
// Oh hey! Our record is on the server. This is the authoritative
|
||||
// server timestamp, so let's hang on to it to decide whether we
|
||||
// need to upload.
|
||||
session.config.persistServerClientRecordTimestamp(r.lastModified);
|
||||
|
||||
// Process commands.
|
||||
localAccountGUIDDownloaded = true;
|
||||
session.config.persistServerClientRecordTimestamp(r.lastModified);
|
||||
processCommands(r.commands);
|
||||
}
|
||||
RepoUtils.logClient(r);
|
||||
|
@ -208,7 +208,8 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
|||
|
||||
@Override
|
||||
public String ifUnmodifiedSince() {
|
||||
Long timestampInMilliseconds = session.config.getPersistedServerClientRecordTimestamp();
|
||||
// Use the timestamp for the whole collection per Sync storage 1.1 spec.
|
||||
Long timestampInMilliseconds = session.config.getPersistedServerClientsTimestamp();
|
||||
|
||||
// It's the first upload so we don't care about X-If-Unmodified-Since.
|
||||
if (timestampInMilliseconds == 0) {
|
||||
|
@ -225,12 +226,14 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
|||
commandsProcessedShouldUpload = false;
|
||||
uploadAttemptsCount.set(0);
|
||||
|
||||
long timestamp = Utils.decimalSecondsToMilliseconds(response.body());
|
||||
// Persist the timestamp for the record we just uploaded,
|
||||
// and bump the collection timestamp, too.
|
||||
long timestamp = response.normalizedWeaveTimestamp();
|
||||
session.config.persistServerClientRecordTimestamp(timestamp);
|
||||
session.config.persistServerClientsTimestamp(timestamp);
|
||||
BaseResource.consumeEntity(response);
|
||||
|
||||
Logger.debug(LOG_TAG, "Timestamp from body is: " + timestamp);
|
||||
Logger.debug(LOG_TAG, "Timestamp from header is: " + response.normalizedWeaveTimestamp());
|
||||
Logger.debug(LOG_TAG, "Timestamp is " + timestamp);
|
||||
} catch (Exception e) {
|
||||
session.abort(e, "Unable to fetch timestamp.");
|
||||
return;
|
||||
|
@ -289,7 +292,9 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
|||
@Override
|
||||
public void resetLocal() {
|
||||
// Clear timestamps and local data.
|
||||
session.config.persistServerClientRecordTimestamp(0L);
|
||||
session.config.persistServerClientRecordTimestamp(0L); // TODO: roll these into one.
|
||||
session.config.persistServerClientsTimestamp(0L);
|
||||
|
||||
session.getClientsDelegate().setClientsCount(0);
|
||||
try {
|
||||
getClientsDatabaseAccessor().wipe();
|
||||
|
@ -391,14 +396,16 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a client record via HTTP POST to the parent collection.
|
||||
*/
|
||||
protected void uploadClientRecord(CryptoRecord record) {
|
||||
Logger.debug(LOG_TAG, "Uploading client record " + record.guid);
|
||||
try {
|
||||
URI putURI = session.config.wboURI(COLLECTION_NAME, record.guid);
|
||||
|
||||
SyncStorageRecordRequest request = new SyncStorageRecordRequest(putURI);
|
||||
URI postURI = session.config.collectionURI(COLLECTION_NAME);
|
||||
SyncStorageRecordRequest request = new SyncStorageRecordRequest(postURI);
|
||||
request.delegate = clientUploadDelegate;
|
||||
request.put(record);
|
||||
request.post(record);
|
||||
} catch (URISyntaxException e) {
|
||||
session.abort(e, "Invalid URI.");
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче