Fix bug 930132 - Chunked webdav-sync is not handled [status: HTTP/1.1 507 Insufficient Storage] [Error: Assert failed: unexepcted endBatch]. r=mmecca
authorPhilipp Kewisch <mozilla@kewis.ch>
Tue, 05 Nov 2013 08:47:55 +0100
changeset 16862 853899c3a88cb639e93277cbaf14afc4593a455f
parent 16861 71bcb1c5da5d0abb0f7adcf23c71fa2ffbfd864a
child 16863 da80de6837fda4e552f2017e20ac07f666a24b05
push id1074
push userbugzilla@standard8.plus.com
push dateMon, 03 Feb 2014 22:47:23 +0000
treeherdercomm-beta@6b791b5369ed [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmmecca
bugs930132
Fix bug 930132 - Chunked webdav-sync is not handled [status: HTTP/1.1 507 Insufficient Storage] [Error: Assert failed: unexepcted endBatch]. r=mmecca
calendar/providers/caldav/calDavRequestHandlers.js
--- a/calendar/providers/caldav/calDavRequestHandlers.js
+++ b/calendar/providers/caldav/calDavRequestHandlers.js
@@ -1,14 +1,15 @@
 /* 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/. */
 
 Components.utils.import("resource://calendar/modules/calUtils.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Timer.jsm");
 
 /**
  * This is a handler for the etag request in calDavCalendar.js' getUpdatedItem.
  * It uses the SAX parser to incrementally parse the items and compose the
  * resulting multiget.
  *
  * @param aCalendar             The (unwrapped) calendar this request belongs to
  * @param aBaseUri              The URI requested (i.e inbox or collection)
@@ -308,16 +309,17 @@ webDavSyncHandler.prototype = {
     newSyncToken: null,
     changeLogListener: null,
     logXML: "",
     isInPropStat : false,
     changeCount : 0,
     unhandledErrors : 0,
     itemsReported: null,
     itemsNeedFetching: null,
+    additionalSyncNeeded: false,
 
     QueryInterface: XPCOMUtils.generateQI([
         Components.interfaces.nsISAXContentHandler,
         Components.interfaces.nsISAXErrorHandler,
         Components.interfaces.nsIRequestObserver,
         Components.interfaces.nsIStreamListener
     ]),
 
@@ -492,16 +494,17 @@ webDavSyncHandler.prototype = {
             }
             this.calendar.finalizeUpdatedItems(this.changeLogListener,
                                                this.baseUri);
         } else {
             let multiget = new multigetSyncHandler(this.itemsNeedFetching,
                                                    this.calendar,
                                                    this.baseUri,
                                                    this.newSyncToken,
+                                                   this.additionalSyncNeeded,
                                                    null,
                                                    this.changeLogListener)
             multiget.doMultiGet();
         }
 
     },
 
     startElement: function wH_startElement(aUri, aLocalName, aQName, aAttributes) {
@@ -582,26 +585,36 @@ webDavSyncHandler.prototype = {
                     this.itemsReported[r.href] = r.getetag;
                     let itemId = this.calendar.mHrefIndex[r.href];
                     let oldEtag = (itemId && this.calendar.mItemInfoCache[itemId].etag);
 
                     if (!oldEtag || oldEtag != r.getetag) {
                         // Etag mismatch, getting new/updated item.
                         this.itemsNeedFetching.push(r.href);
                     }
-                // If the response element is still not handled, log an error
-                // only if the content-type is text/calendar or the
-                // response status is different than 404 not found.
-                // We don't care about response elements
-                // on non-calendar resources or whose status is not indicating
-                // a deleted resource.
+                } else if (r.status.indexOf(" 507") > -1) {
+                    // webdav-sync says that if a 507 is encountered and the
+                    // url matches the request, the current token should be
+                    // saved and another request should be made. We don't
+                    // actually compare the URL, its too easy to get this
+                    // wrong.
+
+                    // The 507 doesn't mean the data received is invalid, so
+                    // continue processing.
+                    this.additionalSyncNeeded = true;
                 } else if ((r.getcontenttype &&
                             r.getcontenttype.substr(0,13) == "text/calendar") ||
                            (r.status &&
                             r.status.indexOf(" 404") == -1)) {
+                    // If the response element is still not handled, log an
+                    // error only if the content-type is text/calendar or the
+                    // response status is different than 404 not found.  We
+                    // don't care about response elements on non-calendar
+                    // resources or whose status is not indicating a deleted
+                    // resource.
                     cal.WARN("CalDAV: Unexpected response, status: " + r.status + ", href: " + r.href);
                     this.unhandledErrors++;
                 } else {
                     cal.LOG("CalDAV: Unhandled response element, status: " + r.status + ", href: " + r.href + " contenttype:" + r.getcontenttype);
                 }
                 break;
             case "sync-token":
                 this.newSyncToken = this.currentResponse[this.tag];
@@ -626,45 +639,50 @@ webDavSyncHandler.prototype = {
  * This is a handler for the multiget request.
  * It uses the SAX parser to incrementally parse the items and compose the
  * resulting multiget.
  *
  * @param aItemsNeedFetching    The array of items to fetch, this must be an
  *                              array of un-encoded paths.
  * @param aCalendar             The (unwrapped) calendar this request belongs to
  * @param aBaseUri              The URI requested (i.e inbox or collection)
+ * @param aAdditionalSyncNeeded (optional) If true, the passed sync token is not the
+ *                                latest, another webdav sync run should be
+ *                                done after completion.
  * @param aNewSyncToken         (optional) new Sync token to set if operation successful
  * @param aListener             (optional) The listener to notify
  * @param aChangeLogListener    (optional) for cached calendars, the listener to
  *                                notify.
  */
-function multigetSyncHandler(aItemsNeedFetching, aCalendar, aBaseUri, aNewSyncToken, aListener, aChangeLogListener) {
+function multigetSyncHandler(aItemsNeedFetching, aCalendar, aBaseUri, aNewSyncToken, aAdditionalSyncNeeded, aListener, aChangeLogListener) {
     this.calendar = aCalendar;
     this.baseUri = aBaseUri;
     this.listener = aListener;
     this.newSyncToken = aNewSyncToken;
     this.changeLogListener = aChangeLogListener;
     this._reader = Components.classes["@mozilla.org/saxparser/xmlreader;1"]
                              .createInstance(Components.interfaces.nsISAXXMLReader);
     this._reader.contentHandler = this;
     this._reader.errorHandler = this;
     this._reader.parseAsync(null);
     this.itemsNeedFetching = aItemsNeedFetching;
+    this.additionalSyncNeeded = aAdditionalSyncNeeded;
 }
 multigetSyncHandler.prototype = {
     currentResponse: null,
     tag: null,
     calendar: null,
     baseUri: null,
     newSyncToken: null,
     listener: null,
     changeLogListener: null,
     logXML: null,
     unhandledErrors : 0,
     itemsNeedFetching: null,
+    additionalSyncNeeded: false,
 
     QueryInterface: XPCOMUtils.generateQI([
         Components.interfaces.nsISAXContentHandler,
         Components.interfaces.nsISAXErrorHandler,
         Components.interfaces.nsIRequestObserver,
         Components.interfaces.nsIStreamListener
     ]),
 
@@ -749,18 +767,27 @@ multigetSyncHandler.prototype = {
         }
         if (this.itemsNeedFetching.length == 0) {
             if (this.newSyncToken) {
                 this.calendar.mWebdavSyncToken = this.newSyncToken;
                 this.calendar.saveCalendarProperties();
               cal.LOG("CalDAV: New webdav-sync Token: " + this.calendar.mWebdavSyncToken);
             }
 
-            this.calendar.finalizeUpdatedItems(this.changeLogListener,
-                                               this.baseUri);
+            if (this.additionalSyncNeeded) {
+                setTimeout(() => {
+                    let wds = new webDavSyncHandler(this.calendar,
+                                                    this.baseUri,
+                                                    this.changeLogListener);
+                    wds.doWebDAVSync();
+                }, 0);
+            } else {
+                this.calendar.finalizeUpdatedItems(this.changeLogListener,
+                                                   this.baseUri);
+            }
         }
         if (!this._reader) {
             // No reader means there was a request error. The error is already
             // notified in onStartRequest, so no need to do it here.
             cal.LOG("CalDAV: onStopRequest: no reader");
             return;
         }
         try {