DAV: Fix behavior when PUT doesn't return ETag (#476)

Thanks to @evert for pointing this out in the #sabredav IRC channel.
This commit is contained in:
Markus Unterwaditzer 2016-06-16 21:40:18 +02:00 committed by GitHub
parent 5b87dcceeb
commit 3228e22621
2 changed files with 27 additions and 10 deletions

View file

@ -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)

View file

@ -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):