Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WebDAV] ETag cache is not invalidated by 412 response to GET w/ If-Match #1150

Closed
2 tasks done
vuori opened this issue Dec 2, 2024 · 3 comments · Fixed by #1155
Closed
2 tasks done

[WebDAV] ETag cache is not invalidated by 412 response to GET w/ If-Match #1150

vuori opened this issue Dec 2, 2024 · 3 comments · Fixed by #1155
Assignees
Labels
bug Something isn't working webdav related to WebDAV file access

Comments

@vuori
Copy link

vuori commented Dec 2, 2024

Problem scope

  • I'm sure that this is a DAVx⁵ problem.

App version

  • I'm using the latest available DAVx⁵ version.

Android version and device/firmware type

Android 15 (GrapheneOS, Pixel 8a, F-Droid)

Steps to reproduce

  1. Create a WebDAV share on a server such as Apache mod_dav.
  2. Store a file on the share with DAVx5 and read it once (for example by copying it with the Files app)
  3. Change the file on the server.
  4. Try to use to access the file on Android again; note that this fails because server returns HTTP status 412 because If-Match ETag on the GET that DAVx5 sends is no longer current.
  5. Try once more; note that it still fails with status 412 because DAVx5 still sends the old ETag (despite doing a PROPFIND on the directory and receving an updated ETag in the 412 response).
  6. Reboot (or wait for a while) and note that DAVx5 refreshes its cached ETag with HEAD. Reading the file now works.

Actual result

ETags seem to be cached from a prior HEAD request and not refreshed even when the WebDAV server returns status 412 from If-Match request.

Expected result

When the file changes on WebDAV server and the server returns 412 on GET, DAVx5 should refresh its ETag cache.

Further info

Since the server returns the current ETag with the 412 response to the GET, maybe pick that up? Alternatively since DAVx5 is doing a PROPFIND on the directory anyway, perhaps using the lp1:getetag property would be an option too?

If neither sounds good, re-doing HEAD on the target on 412 response seems like a surefire solution.

@vuori vuori added the bug Something isn't working label Dec 2, 2024
@rfc2822 rfc2822 added the webdav related to WebDAV file access label Dec 2, 2024
@rfc2822
Copy link
Member

rfc2822 commented Dec 2, 2024

I think we should drop headResponseCache completly: for every random-access file request, there should be a HEAD at the beginning that fetches the ETag and then for all operations this ETag is expected (otherwise an I/O error shall be returned).

@ArnyminerZ
Copy link
Member

I've managed to reproduce with the steps given.

Logs
Couldn't read from WebDAV resource
 java.util.concurrent.ExecutionException: at.bitfire.dav4jvm.exception.PreconditionFailedException: HTTP 412 Precondition Failed
  at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:596)
  at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:555)
  at com.google.common.util.concurrent.AbstractFuture$TrustedFuture.get(AbstractFuture.java:111)
  at com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly(Uninterruptibles.java:209)
  at com.google.common.cache.LocalCache$Segment.getAndRecordStats(LocalCache.java:2259)
  at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2227)
  at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2185)
  at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2075)
  at com.google.common.cache.LocalCache.get(LocalCache.java:3900)
  at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3923)
  at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4806)
  at at.bitfire.davdroid.webdav.PagingReader.readPage(PagingReader.kt:109)
  at at.bitfire.davdroid.webdav.PagingReader.read(PagingReader.kt:66)
  at at.bitfire.davdroid.webdav.RandomAccessCallback.onRead(RandomAccessCallback.kt:121)
  at at.bitfire.davdroid.webdav.RandomAccessCallbackWrapper.onRead$lambda$2(RandomAccessCallbackWrapper.kt:178)
  at at.bitfire.davdroid.webdav.RandomAccessCallbackWrapper.$r8$lambda$MKhQ4ZVTnbKivp3sLdqdmLv8qq0(Unknown Source:0)
  at at.bitfire.davdroid.webdav.RandomAccessCallbackWrapper$$ExternalSyntheticLambda2.invoke(D8$$SyntheticClass:0)
  at at.bitfire.davdroid.webdav.RandomAccessCallbackWrapper.requireCallback(RandomAccessCallbackWrapper.kt:151)
  at at.bitfire.davdroid.webdav.RandomAccessCallbackWrapper.onRead(RandomAccessCallbackWrapper.kt:178)
  at com.android.internal.os.FuseAppLoop.handleMessage(FuseAppLoop.java:181)
  at android.os.Handler.dispatchMessage(Handler.java:102)
  at android.os.Looper.loopOnce(Looper.java:205)
  at android.os.Looper.loop(Looper.java:294)
  at android.os.HandlerThread.run(HandlerThread.java:67)
 Caused by: at.bitfire.dav4jvm.exception.PreconditionFailedException: HTTP 412 Precondition Failed
  at at.bitfire.dav4jvm.DavResource.checkStatus(DavResource.kt:646)
  at at.bitfire.dav4jvm.DavResource.checkStatus(DavResource.kt:624)
  at at.bitfire.dav4jvm.DavResource.getRange(DavResource.kt:404)
  at at.bitfire.davdroid.webdav.RandomAccessCallback$PageLoader$load$job$1.invokeSuspend$lambda$1(RandomAccessCallback.kt:203)
  at at.bitfire.davdroid.webdav.RandomAccessCallback$PageLoader$load$job$1.$r8$lambda$dnHSGQG-OAPkzEM2ChSXsplrZok(Unknown Source:0)
  at at.bitfire.davdroid.webdav.RandomAccessCallback$PageLoader$load$job$1$$ExternalSyntheticLambda1.invoke(D8$$SyntheticClass:0)
  at kotlinx.coroutines.InterruptibleKt.runInterruptibleInExpectedContext(Interruptible.kt:48)
  at kotlinx.coroutines.InterruptibleKt.access$runInterruptibleInExpectedContext(Interruptible.kt:1)
  at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invokeSuspend(Interruptible.kt:40)
  at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invoke(Unknown Source:8)
  at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invoke(Unknown Source:4)
  at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:42)
  at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:156)
  at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
  at kotlinx.coroutines.InterruptibleKt.runInterruptible(Interruptible.kt:39)
  at kotlinx.coroutines.InterruptibleKt.runInterruptible$default(Interruptible.kt:36)
  at at.bitfire.davdroid.webdav.RandomAccessCallback$PageLoader$load$job$1.invokeSuspend(RandomAccessCallback.kt:201)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:101)
13:02:43.048  W  	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:113)
  at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:89)
  at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:589)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:823)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:720)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:707)
Request
--> GET http://davtest.dev001.net:81/webdav/test-file.txt http/1.1
If-Match: "2d-62884a9c76e03"
Accept: text/plain, */*;q=0.8
Range: bytes=0-44
User-Agent: DAVx5/4.4.4-ose (dav4jvm; okhttp/4.12.0) Android/14
Accept-Language: en-US, en;q=0.7, *;q=0.5
Host: davtest.dev001.net:81
Connection: Keep-Alive
--> END GET
Response
<-- 412 Precondition Failed http://davtest.dev001.net:81/webdav/test-file.txt (42ms)
Date: Thu, 05 Dec 2024 12:02:43 GMT
Server: Apache/2.4.52 (Ubuntu)
Last-Modified: Thu, 05 Dec 2024 12:02:35 GMT
ETag: "33-62884ac7ecd5a"
Accept-Ranges: bytes
Content-Length: 0
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: text/plain
<-- END HTTP

Clearly, ETags do not match

@ArnyminerZ ArnyminerZ linked a pull request Dec 5, 2024 that will close this issue
4 tasks
@ArnyminerZ
Copy link
Member

I've opened #1155 and it's now working fine.

@github-project-automation github-project-automation bot moved this from Todo to Done in DAVx⁵ Releases Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working webdav related to WebDAV file access
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

3 participants