Handle 429 responses
Some checks failed
Java CI / build (push) Has been cancelled
Java CI / javadoc-latest (push) Has been cancelled
Java CI / build-java7 (push) Has been cancelled
Java with IzPack Snapshot Setup / setup (push) Has been cancelled
Sync Primary Repository to GitHub Mirror / sync (push) Has been cancelled

untested
This commit is contained in:
zzz
2025-06-04 16:06:31 -04:00
parent 4f5a236fdc
commit 95bcf2977a

View File

@ -19,6 +19,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import net.i2p.CoreVersion;
@ -57,6 +58,7 @@ class RemoteSearch {
private static final AtomicInteger _lastSent = new AtomicInteger();
private static final AtomicInteger _lastRcvd = new AtomicInteger();
private static final AtomicReference<String> _currentSearch = new AtomicReference<String>();
private static final AtomicLong _bannedUntil = new AtomicLong();
// positive/negative cache, Result.result may be null
private static final Map<String, Result> _cache = new LHMCache<String, Result>(16);
//private static final List<Thread> _waiting = new ArrayList<Thread>(16);
@ -175,6 +177,19 @@ class RemoteSearch {
return null;
}
long currentBan = _bannedUntil.get();
if (currentBan > 0) {
long now = _ctx.clock().now();
if (currentBan > now) {
long retry = currentBan - now;
if (retry <= 10*60*1000)
return _t("Rate limited for {0}", DataHelper.formatDuration(retry));
else
return _t("Rate limited until {0}", DataHelper.formatTime(currentBan));
}
_bannedUntil.compareAndSet(currentBan, 0);
}
// if a new search comes in within 650 ms, interrupt the old one
if (isXHR) {
Thread us = Thread.currentThread();
@ -239,7 +254,11 @@ class RemoteSearch {
code = -1;
if (code > 0) {
buf.append(code);
String text = get.getStatusText();
String text;
if (code == 429)
text = process429(out);
else
text = get.getStatusText();
if (text != null)
buf.append(' ').append(DataHelper.escapeHTML(text));
} else {
@ -304,6 +323,27 @@ class RemoteSearch {
return buf.toString();
}
private String process429(ByteArrayStream out) {
try {
Reader in = new InputStreamReader(out.asInputStream(), "UTF-8");
JsonObject map = (JsonObject) Jsoner.deserialize(in);
long retry = map.getIntegerOrDefault(RETRY) * 1000L;
long until = _ctx.clock().now() + retry;
long currentBan = _bannedUntil.get();
while (until > currentBan && !_bannedUntil.compareAndSet(currentBan, until)) {
currentBan = _bannedUntil.get();
}
if (retry <= 10*60*1000)
return _t("Rate limited for {0}", DataHelper.formatDuration(retry));
else
return _t("Rate limited until {0}", DataHelper.formatTime(until));
} catch (Exception e) {
if (_log.shouldWarn())
_log.warn("json error input=\"" + DataHelper.getUTF8(out.toByteArray()), e);
return null;
}
}
// keys with defaults
private static final JsonKey ADDED = Jsoner.mintJsonKey("added", "");
private static final JsonKey ALIVE = Jsoner.mintJsonKey("is_alive", Integer.valueOf(0));
@ -319,6 +359,7 @@ class RemoteSearch {
private static final JsonKey LINK = Jsoner.mintJsonKey("download_link", "");
private static final JsonKey NAME = Jsoner.mintJsonKey("name", "");
private static final JsonKey OWNER = Jsoner.mintJsonKey("owner", "");
private static final JsonKey RETRY = Jsoner.mintJsonKey("retry_after", Integer.valueOf(60));
private static final JsonKey SEEDS = Jsoner.mintJsonKey("seeders", Integer.valueOf(0));
private static final JsonKey SIZE = Jsoner.mintJsonKey("size", Long.valueOf(0));
private static final JsonKey TORRENTS = Jsoner.mintJsonKey("torrents", null);