diff --git a/tests/test_sync.py b/tests/test_sync.py index d3b57de..db2443a 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -404,8 +404,11 @@ class SyncMachine(RuleBasedStateMachine): def _get_items(storage): return sorted(item.raw for etag, item in storage.items.values()) - @rule(target=Storage, read_only=st.booleans(), flaky_etags=st.booleans()) - def newstorage(self, read_only, flaky_etags): + @rule(target=Storage, + read_only=st.booleans(), + flaky_etags=st.booleans(), + null_etag_on_upload=st.booleans()) + def newstorage(self, read_only, flaky_etags, null_etag_on_upload): s = MemoryStorage() s.read_only = read_only if flaky_etags: @@ -416,6 +419,12 @@ class SyncMachine(RuleBasedStateMachine): return item, etag s.get = get + if null_etag_on_upload: + _old_upload = s.upload + _old_update = s.update + s.upload = lambda item: (_old_upload(item)[0], None) + s.update = lambda h, i, e: _old_update(h, i, e) and None + return s @rule(target=Status) diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 1d28a62..0025678 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -465,16 +465,24 @@ class DavStorage(Storage): data=item.raw.encode('utf-8'), headers=headers ) + + # The server may not return an etag under certain conditions: + # + # An origin server MUST NOT send a validator header field (Section + # 7.2), such as an ETag or Last-Modified field, in a successful + # response to PUT unless the request's representation data was saved + # without any transformation applied to the body (i.e., the + # resource's new representation data is identical to the + # representation data received in the PUT request) and the validator + # field value reflects the new representation. + # + # -- https://tools.ietf.org/html/rfc7231#section-4.3.4 + # + # In such cases we return None as etag. The next synchronization will + # then detect an etag change (None != some string) and will download + # the new item. etag = response.headers.get('etag', None) href = self._normalize_href(response.url) - if not etag: - # The server violated the RFC and didn't send an etag. This is - # technically a race-condition, but too many popular servers do it. - # - # ownCloud: https://github.com/owncloud/contacts/issues/920 - dav_logger.debug('Server did not send etag, fetching {!r}' - .format(href)) - item2, etag = self.get(href) return href, etag def update(self, href, item, etag):