Skip to content

Conversation

@mrienstra
Copy link

@mrienstra mrienstra commented Jul 12, 2023

I encountered a website (powered by https://www.civicplus.com/civicengage/local-government-website-design ) which adds an s suffix, e.g. 30s instead of 30.

While looking into it, I found this thread, which discusses another non-standard format, 1m0s: urllib3/urllib3#1822

This PR adds support for both of these non-standard formats. AKA invalid formats, but given that it's better to correctly parse the value and respect it (vs. ignoring it), seems worth doing.

Decided to pull these changes out into a parseRetryAfter method for readability. Doing so made it easy to get rid of the nested ifs. Before:

    // The `retry-after` header can come in either <seconds> or
    // A specific date to go check.
    let retryAfter = Number(retryAfterRaw) * 1000 + Date.now();
    if (isNaN(retryAfter)) {
      retryAfter = Date.parse(retryAfterRaw);
      if (isNaN(retryAfter)) {
        // handle invalid response in formats `1m30s` or `30s`
        const matches = retryAfterRaw.match(/^(?:(\d+)m)?(\d+)s$/);
        if (matches) {
          retryAfter =
            (Number(matches[1] || 0) * 60 + Number(matches[2])) * 1000 +
            Date.now();
        } else {
          return false;
        }
      }
    }

After:

  // The `retry-after` header can come in either <seconds> or
  // A specific date to go check.
  private parseRetryAfter(retryAfterRaw: string): number {
    let retryAfter = Number(retryAfterRaw) * 1000 + Date.now();
    if (!isNaN(retryAfter)) return retryAfter;

    retryAfter = Date.parse(retryAfterRaw);
    if (!isNaN(retryAfter)) return retryAfter;

    // handle invalid response in formats `1m30s` or `30s`
    const matches = retryAfterRaw.match(/^(?:(\d+)m)?(\d+)s$/);
    if (!matches) return NaN;
    return (
      (Number(matches[1] || 0) * 60 + Number(matches[2])) * 1000 + Date.now()
    );
  }

JustinBeckwith added a commit that referenced this pull request Oct 20, 2025
Add support for non-standard retry-after header formats used by
CivicPlus and other services that use "30s" or "1m30s" notation
instead of the standard seconds or HTTP date format.

Changes:
- Extract retry-after parsing logic into parseRetryAfter() method
- Add regex support for "Xs" and "XmYs" time formats
- Add tests for non-standard formats (3s, 1m1s)
- Fix missing redirect parameter in 405 and fallback GET requests

Closes #556

Co-authored-by: Michael Rienstra <[email protected]>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant