mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-09-23 01:25:31 -04:00
fix: extend Retry-After parsing to RDAP 503 and fix block pattern across newlines
- Parse `Retry-After` on RDAP `503` responses (previously only `429`), exposing `retryAfterMs` on the attempt and on the top-level result when `rdapOnly` makes it terminal - Fix the WHOIS `BLOCK_PATTERNS` regex to use `[\s\S]` instead of `.` so a block notice that spans multiple lines (e.g. `"Your IP address\nhas been blocked"`) is still classified as `blocked` - Update README to document `retryAfterMs` on `LookupResult`, clarify `rate_limited` / `blocked` / `unparseable` semantics, and note referral-host validation behaviour
This commit is contained in:
@@ -91,6 +91,19 @@ describe("throttle and empty-response guards", () => {
|
||||
});
|
||||
|
||||
describe("rdapOnly rate limiting", () => {
|
||||
it("parses Retry-After on a 503", async () => {
|
||||
const customFetch: FetchLike = vi.fn(
|
||||
async () => new Response("", { status: 503, headers: { "retry-after": "5" } }),
|
||||
);
|
||||
const res = await lookup("example.com", {
|
||||
customBootstrapData: bootstrap,
|
||||
customFetch,
|
||||
rdapOnly: true,
|
||||
});
|
||||
expect(res.errorCode).toBe("rdap_unavailable");
|
||||
expect(res.retryAfterMs).toBe(5_000);
|
||||
});
|
||||
|
||||
it("surfaces retryAfterMs on the result", async () => {
|
||||
const customFetch: FetchLike = vi.fn(
|
||||
async () => new Response("", { status: 429, headers: { "retry-after": "12" } }),
|
||||
|
||||
+7
-1
@@ -64,7 +64,13 @@ export async function fetchRdapDomain(
|
||||
}
|
||||
if (!res.ok) {
|
||||
const bodyText = await res.text().catch(() => "");
|
||||
throw new RdapperError("http_error", `RDAP ${res.status}: ${bodyText.slice(0, 500)}`);
|
||||
const retryAfterMs =
|
||||
res.status === 503 ? parseRetryAfterMs(res.headers.get("retry-after")) : undefined;
|
||||
throw new RdapperError(
|
||||
"http_error",
|
||||
`RDAP ${res.status}: ${bodyText.slice(0, 500)}`,
|
||||
retryAfterMs !== undefined ? { retryAfterMs } : undefined,
|
||||
);
|
||||
}
|
||||
const json = await res.json();
|
||||
return { url, json };
|
||||
|
||||
@@ -15,6 +15,7 @@ describe("detectWhoisRefusal", () => {
|
||||
it.each([
|
||||
"Requests of this client are not permitted. Please use https://www.nic.ch/whois/ for queries.",
|
||||
"Your IP address has been blocked",
|
||||
"Your IP address\nhas been blocked",
|
||||
])("classifies %j as blocked", (text) => {
|
||||
expect(detectWhoisRefusal(text)).toBe("blocked");
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ const THROTTLE_PATTERNS: RegExp[] = [
|
||||
// Permanent: this client is refused outright, so retrying will not help.
|
||||
const BLOCK_PATTERNS: RegExp[] = [
|
||||
/requests\s+of\s+this\s+client\s+are\s+not\s+permitted/i, // .ch/.li
|
||||
/\b(your|this)\s+(ip|address|client)\b.{0,60}\b(blocked|banned|blacklisted|not\s+permitted)\b/i,
|
||||
/\b(your|this)\s+(ip|address|client)\b[\s\S]{0,60}\b(blocked|banned|blacklisted|not\s+permitted)\b/i,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user