Skip to content

Extension point for rewriting data URLs before fetch (igvxhr mapUrl) #2088

Description

@turner

The ask

A supported way for an embedder to rewrite a data URL immediately before igv fetches it — composed on top of igv's existing built-in rewrites, not replacing them. Something like:

igv.setUrlMapper(url => /* return url, or a rewritten one */)

Unset by default, so an application that never calls it behaves exactly as it does today.

Why

www.encodeproject.org puts AWS WAF in front of its files and answers any Origin not on its allowlist with a CAPTCHA page, under a misleading 405. localhost is never allowlisted, so ENCODE-hosted data cannot be loaded during local development. A background fetch cannot solve a CAPTCHA.

The workable fix is a dev-server middleware that fetches the file from Node with an allowlisted Origin and hands the resulting 307 back to the browser — the bytes still stream directly from S3, so nothing proxies the payload. That requires the client to send the read to the middleware's path instead of the origin URL, which is where the extension point is needed.

Measured against an ENCODE bigWig on 2026-08-03:

Request Result
Direct from localhost, any Origin 405 + x-amzn-waf-action: captcha
Origin: https://aidenlab.org (allowlisted) 307encode-public.s3.amazonaws.com
Through the middleware, ranged 307 → S3 → 206 Partial Content

Why setCORSProxy doesn't cover it

igv.setCORSProxy exists and looked like the answer, but tryCorsProxy is only reachable from xhr.onerror or xhr.status === 0. ENCODE's challenge response is a 405 carrying valid CORS headers, so onload runs with status 405 and goes straight to handleError. The retry never fires.

It is also a fallback rather than a routing decision — it costs a failed request first, and its URL shape is fixed (<proxy>?url=<encoded>). A pre-fetch mapper expresses "this host is known to need routing" directly.

Precedent

hic-straw added exactly this in v4.0.0 (aidenlab/hic-straw#49) as config.mapUrl, composed after its built-in Dropbox/NCBI rewrites — normalize first, then route. It has worked well: the rewrite is host-scoped, so genuine CORS and permissions problems still surface in development exactly as they would in production, which routing everything through a proxy would hide.

juicebox.js embeds both hic-straw and igv. Its .hic reads now work from localhost against ENCODE; its igv-backed tracks on the same host still fail, because there is no equivalent seam. Context: aidenlab/juicebox.js#450, and the rationale in ADR 0001.

Notes

  • A global setter is enough for the dev-mode use case, though a per-Browser or per-config option would work too.
  • The mapper should apply to index/companion reads (.bai, .tbi, .csi) as well as the primary URL, since a blocked host blocks both.
  • Observed on igv.js 3.8.1, where igvxhr's mapUrl is module-private.

Happy to open a PR if the shape sounds right — I'd want to match whatever composition order you prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions