fpaste: retry on pow failure (max 5 attempts)
Some checks failed
CI / Lint & Format (push) Successful in 16s
CI / Security Scan (push) Failing after 19s
CI / Tests (push) Failing after 35s

This commit is contained in:
Username
2025-12-20 21:09:14 +01:00
parent 89ac2af161
commit c6b3dd410a

47
fpaste
View File

@@ -227,34 +227,44 @@ def cmd_create(args, config):
if not args.quiet:
print(" done", file=sys.stderr)
headers = {}
# Build base headers (without PoW)
base_headers = {}
if config["cert_sha1"]:
headers["X-SSL-Client-SHA1"] = config["cert_sha1"]
base_headers["X-SSL-Client-SHA1"] = config["cert_sha1"]
# Add burn-after-read header
if args.burn:
headers["X-Burn-After-Read"] = "true"
base_headers["X-Burn-After-Read"] = "true"
# Add custom expiry header
if args.expiry:
headers["X-Expiry"] = str(args.expiry)
base_headers["X-Expiry"] = str(args.expiry)
# Add password header
if args.password:
headers["X-Paste-Password"] = args.password
base_headers["X-Paste-Password"] = args.password
url = config["server"].rstrip("/") + "/"
max_retries = 5
last_error = None
for attempt in range(max_retries):
headers = dict(base_headers)
# Get and solve PoW challenge if required
challenge = get_challenge(config)
if challenge:
if attempt > 0 and not args.quiet:
print(f"retry {attempt}/{max_retries - 1}...", file=sys.stderr)
if not args.quiet:
print(f"solving pow (difficulty={challenge['difficulty']})...", end="", file=sys.stderr)
diff = challenge["difficulty"]
print(f"solving pow (difficulty={diff})...", end="", file=sys.stderr)
solution = solve_pow(challenge["nonce"], challenge["difficulty"])
if not args.quiet:
print(" done", file=sys.stderr)
headers["X-PoW-Token"] = challenge["token"]
headers["X-PoW-Solution"] = str(solution)
url = config["server"].rstrip("/") + "/"
status, body, _ = request(
url, method="POST", data=content, headers=headers, ssl_context=config.get("ssl_context")
)
@@ -272,12 +282,27 @@ def cmd_create(args, config):
print(data["id"] + key_fragment)
else:
print(config["server"].rstrip("/") + data["url"] + key_fragment)
else:
return
# Parse error
try:
err = json.loads(body).get("error", body.decode())
last_error = json.loads(body).get("error", body.decode())
except (json.JSONDecodeError, UnicodeDecodeError):
err = body.decode(errors="replace")
die(f"create failed ({status}): {err}")
last_error = body.decode(errors="replace")
# Check if PoW-related error (worth retrying)
err_lower = last_error.lower()
is_pow_error = status == 400 and ("pow" in err_lower or "proof-of-work" in err_lower)
if not is_pow_error:
# Non-PoW error, don't retry
die(f"create failed ({status}): {last_error}")
# PoW error, will retry if attempts remain
if not args.quiet:
print(f"pow rejected: {last_error}", file=sys.stderr)
# All retries exhausted
die(f"create failed after {max_retries} attempts: {last_error}")
def cmd_get(args, config):