Skip to content

Commit dde5413

Browse files
jpnurmiclaude
andcommitted
feat(crashpad): consent-aware offline caching
Wires UploadsPaused alongside UploadsEnabled from the consent handler: when consent is revoked and cache_keep or http_retry is enabled, pause crashpad uploads so pending reports are retained rather than skipped to completed. On consent transitions that re-enable uploads, call RequestRetry() so the handler wakes immediately instead of waiting for its next periodic scan. The crashpad submodule bump pulls in the matching UploadsPaused setting and RequestRetry IPC additions on the handler side. An integration test exercises the full flow: a crash captured while consent is revoked stays in crashpad's pending queue (no upload, not moved to completed), and giving consent in a subsequent run drains the pending report to the server. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 34a6171 commit dde5413

3 files changed

Lines changed: 70 additions & 2 deletions

File tree

src/backends/sentry_backend_crashpad.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,19 @@ crashpad_backend_user_consent_changed(sentry_backend_t *backend)
153153
if (!data->db || !data->db->GetSettings()) {
154154
return;
155155
}
156-
data->db->GetSettings()->SetUploadsEnabled(!sentry__should_skip_upload());
156+
const bool enabled = !sentry__should_skip_upload();
157+
bool paused = false;
158+
if (!enabled) {
159+
SENTRY_WITH_OPTIONS (options) {
160+
paused = options->cache_keep || options->http_retry;
161+
}
162+
}
163+
auto *settings = data->db->GetSettings();
164+
settings->SetUploadsEnabled(enabled);
165+
settings->SetUploadsPaused(paused);
166+
if (enabled && data->client) {
167+
data->client->RequestRetry();
168+
}
157169
}
158170

159171
#ifdef SENTRY_PLATFORM_WINDOWS

tests/test_integration_crashpad.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,62 @@ def test_crashpad_cache_keep(cmake, httpserver, cache_keep):
866866
assert cache_files[0].stem == dmp_files[0].stem
867867

868868

869+
def test_crashpad_cache_consent(cmake, httpserver):
870+
"""Crash while consent is revoked + cache_keep: the minidump stays in
871+
crashpad's pending queue (no upload, not moved to completed). Once
872+
consent is given, RequestRetry() wakes the handler and the minidump
873+
is uploaded."""
874+
tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"})
875+
db_path = tmp_path.joinpath(".sentry-native")
876+
env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver))
877+
878+
# 1) Crash with consent revoked. No upload should happen.
879+
run(
880+
tmp_path,
881+
"sentry_example",
882+
[
883+
"log",
884+
"require-user-consent",
885+
"user-consent-revoke",
886+
"cache-keep",
887+
"crash",
888+
],
889+
expect_failure=True,
890+
env=env,
891+
)
892+
893+
# Give the (out-of-process) handler a moment to write the report.
894+
time.sleep(1)
895+
assert len(httpserver.log) == 0
896+
897+
# On Linux/macOS the database lays out pending/ and completed/ as
898+
# separate directories; on Windows a single reports/ dir tracks state
899+
# in metadata, so only assert the dir-based invariants off-Windows.
900+
if sys.platform != "win32":
901+
pending_dir = db_path.joinpath("pending")
902+
completed_dir = db_path.joinpath("completed")
903+
assert pending_dir.exists()
904+
assert len(list(pending_dir.glob("*.dmp"))) >= 1
905+
assert not completed_dir.exists() or not any(completed_dir.glob("*.dmp"))
906+
907+
# 2) Give consent. The handler should wake via RequestRetry() and upload.
908+
httpserver.expect_oneshot_request("/api/123456/minidump/").respond_with_data("OK")
909+
with httpserver.wait(timeout=10) as waiting:
910+
run(
911+
tmp_path,
912+
"sentry_example",
913+
[
914+
"log",
915+
"require-user-consent",
916+
"user-consent-give",
917+
"cache-keep",
918+
"no-setup",
919+
],
920+
env=env,
921+
)
922+
assert waiting.result
923+
924+
869925
def test_crashpad_cache_max_size(cmake, httpserver):
870926
tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"})
871927
cache_dir = tmp_path.joinpath(".sentry-native/cache")

0 commit comments

Comments
 (0)