fpaste: retry on pow failure (max 5 attempts)
This commit is contained in:
95
fpaste
95
fpaste
@@ -227,57 +227,82 @@ def cmd_create(args, config):
|
|||||||
if not args.quiet:
|
if not args.quiet:
|
||||||
print(" done", file=sys.stderr)
|
print(" done", file=sys.stderr)
|
||||||
|
|
||||||
headers = {}
|
# Build base headers (without PoW)
|
||||||
|
base_headers = {}
|
||||||
if config["cert_sha1"]:
|
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
|
# Add burn-after-read header
|
||||||
if args.burn:
|
if args.burn:
|
||||||
headers["X-Burn-After-Read"] = "true"
|
base_headers["X-Burn-After-Read"] = "true"
|
||||||
|
|
||||||
# Add custom expiry header
|
# Add custom expiry header
|
||||||
if args.expiry:
|
if args.expiry:
|
||||||
headers["X-Expiry"] = str(args.expiry)
|
base_headers["X-Expiry"] = str(args.expiry)
|
||||||
|
|
||||||
# Add password header
|
# Add password header
|
||||||
if args.password:
|
if args.password:
|
||||||
headers["X-Paste-Password"] = args.password
|
base_headers["X-Paste-Password"] = args.password
|
||||||
|
|
||||||
# Get and solve PoW challenge if required
|
|
||||||
challenge = get_challenge(config)
|
|
||||||
if challenge:
|
|
||||||
if not args.quiet:
|
|
||||||
print(f"solving pow (difficulty={challenge['difficulty']})...", 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("/") + "/"
|
url = config["server"].rstrip("/") + "/"
|
||||||
status, body, _ = request(
|
max_retries = 5
|
||||||
url, method="POST", data=content, headers=headers, ssl_context=config.get("ssl_context")
|
last_error = None
|
||||||
)
|
|
||||||
|
|
||||||
if status == 201:
|
for attempt in range(max_retries):
|
||||||
data = json.loads(body)
|
headers = dict(base_headers)
|
||||||
# Append encryption key to URL fragment if encrypted
|
|
||||||
key_fragment = ""
|
|
||||||
if encryption_key:
|
|
||||||
key_fragment = "#" + encode_key(encryption_key)
|
|
||||||
|
|
||||||
if args.raw:
|
# Get and solve PoW challenge if required
|
||||||
print(config["server"].rstrip("/") + data["raw"] + key_fragment)
|
challenge = get_challenge(config)
|
||||||
elif args.quiet:
|
if challenge:
|
||||||
print(data["id"] + key_fragment)
|
if attempt > 0 and not args.quiet:
|
||||||
else:
|
print(f"retry {attempt}/{max_retries - 1}...", file=sys.stderr)
|
||||||
print(config["server"].rstrip("/") + data["url"] + key_fragment)
|
if not args.quiet:
|
||||||
else:
|
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)
|
||||||
|
|
||||||
|
status, body, _ = request(
|
||||||
|
url, method="POST", data=content, headers=headers, ssl_context=config.get("ssl_context")
|
||||||
|
)
|
||||||
|
|
||||||
|
if status == 201:
|
||||||
|
data = json.loads(body)
|
||||||
|
# Append encryption key to URL fragment if encrypted
|
||||||
|
key_fragment = ""
|
||||||
|
if encryption_key:
|
||||||
|
key_fragment = "#" + encode_key(encryption_key)
|
||||||
|
|
||||||
|
if args.raw:
|
||||||
|
print(config["server"].rstrip("/") + data["raw"] + key_fragment)
|
||||||
|
elif args.quiet:
|
||||||
|
print(data["id"] + key_fragment)
|
||||||
|
else:
|
||||||
|
print(config["server"].rstrip("/") + data["url"] + key_fragment)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse error
|
||||||
try:
|
try:
|
||||||
err = json.loads(body).get("error", body.decode())
|
last_error = json.loads(body).get("error", body.decode())
|
||||||
except (json.JSONDecodeError, UnicodeDecodeError):
|
except (json.JSONDecodeError, UnicodeDecodeError):
|
||||||
err = body.decode(errors="replace")
|
last_error = body.decode(errors="replace")
|
||||||
die(f"create failed ({status}): {err}")
|
|
||||||
|
# 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):
|
def cmd_get(args, config):
|
||||||
|
|||||||
Reference in New Issue
Block a user