libretro / libretro/RetroArch

Cloud Sync (WebDAV): uploads succeed on the server but report HTTP -1, so the sync is marked as failed

Open
#19,457 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
14.1k
Forks
2.2k
Avg merge
7h 35m
Merged PRs (30d)
51

Description

Description

Cloud Sync (WebDAV driver) reports Cloud Sync finished with failures while
every file actually reaches the server. The frontend log shows:

[WARN] [webdav] Failed: manifest.server: HTTP -1
[ERROR] [CloudSync] Uploading updated manifest failed.

but the server's access log for the very same requests shows them succeeding:

"PUT  /remote.php/dav/files/<user>/<dir>/manifest.server HTTP/1.1" 201
"PUT  /remote.php/dav/files/<user>/<dir>/manifest.server HTTP/1.1" 204
"MKCOL /remote.php/dav/files/<user>/<dir>/saves/ HTTP/1.1"        405

HTTP -1 means net_http never parsed a status line, so webdav_update_cb()
computes success = false even though the upload completed. The user is told
the sync failed and, because task_cloud_sync_update_manifest_cb() only calls
task_cloud_sync_commit_local_manifest() on success, the local manifest is
left stale — which produces spurious Conflicting change of ... on the next
run.

Cause (as far as I could trace it)

net_http.c keeps a connection pool (net_http_conn_pool_add /
net_http_conn_pool_remove). When a pooled connection is reused after the
server has closed it (Apache's default KeepAliveTimeout is 5 s), the read
fails and both error paths set response->status = -1:

/* net_http.c, receive_header_failed / receive_body_failed */
net_http_conn_pool_remove(state->conn);
state->conn      = NULL;
state->err       = true;
response->part   = P_DONE;
response->status = -1;

There is no retry: grep -i retry libretro-common/net/net_http.c returns
nothing. A single transparent retry on a reused connection that fails before
a status line is read is the usual remedy for this, and would not change
behaviour for fresh connections.

Note the driver code itself looks correct — webdav_update_cb() accepts any
2xx and webdav_mkdir_cb() already tolerates the 405 that WebDAV returns for
an existing collection. The problem is that it never receives a status to
judge.

Workaround (server side)

Telling Apache not to keep connections alive for this client removes the
problem completely:

BrowserMatch "libretro" nokeepalive downgrade-1.0 force-response-1.0

Before: ~75 s per sync, several HTTP -1, finished with failures every time.
After: 1.7 s, no failures, Uploading updated manifest succeeded.

This only affects the libretro user agent; everything else keeps HTTP/1.1
with keep-alive.

Steps to reproduce
  1. WebDAV Cloud Sync against a server with keep-alive enabled (Apache default).
  2. Sync a directory with enough files that the sync spans more than
    KeepAliveTimeout seconds — the deeper the tree the easier, since the driver
    issues one MKCOL per path component per file.
  3. Some transfers report HTTP -1 although the server logged them as 201/204.
Versions
  • RetroArch 1.22.2 (nightly, Windows x64) and the Steam build
  • Server: Nextcloud 34 on Apache 2.4.68, KeepAliveTimeout 5,
    MaxKeepAliveRequests 100
  • Also reported with other WebDAV servers in the (now closed) #16834, which
    showed the same HTTP -1, suggesting the client rather than any one server.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in libretro-common/net/net_http.c, tracing connection-pool reuse through receive_header_failed and receive_body_failed, then inspect webdav_update_cb() and task_cloud_sync_update_manifest_cb(). Reproduce against a WebDAV server with keep-alive enabled and verify that a reused-connection failure before the status line is read no longer reports HTTP -1, while successful uploads commit the local manifest.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
cloud, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.