mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(tiles): derive the tile origin from the configured TileJSON (#933)
`createVectorTileSource` took `tileJsonUrl` and `allowedOrigin` as independent options, and `createOpenFreeMapSource` hardcoded both to the default host. A caller that repointed only `tileJsonUrl` — the obvious single change — kept the default `allowedOrigin`, so the TileJSON's absolute upstream `tiles[0]` passed the origin check and every tile was fetched from the default host instead. No error, no warning, and nothing reached the configured mirror. `allowedOrigin` now defaults to the origin of `tileJsonUrl`, so repointing one option is enough, and a mirror that serves the default host's TileJSON verbatim fails with the existing `Invalid vector tile origin` instead of silently bypassing itself. The origin check is unchanged: a TileJSON still cannot point the client at a host the caller did not configure. All three existing callers pass `allowedOrigin` explicitly (`openFreeMap.js`, `alpr/source.js`, `traffic/flowSource.js`), and an explicit value still overrides the derived one, so behaviour is unchanged for every current call site.
This commit is contained in:
1 parent
e1cc7afacf
commit
c6b43a365d
4 files changed
+106
-3
No files matched your search
@@ -214,11 +214,18 @@ function decodeLayers(bytes, z, x, y, layerNames) {
|
||||
return { roads, military };
|
||||
}
|
||||
|
||||
/** Construct an immutable-version road/military tile source without starting I/O. */
|
||||
/**
|
||||
* Construct an immutable-version road/military tile source without starting I/O.
|
||||
*
|
||||
* `allowedOrigin` is deliberately not set here: `createVectorTileSource`
|
||||
* derives it from whichever `tileJsonUrl` is in effect, so a caller that
|
||||
* repoints only `tileJsonUrl` gets tiles from that host or a named error —
|
||||
* never a silent fall back to the default host (#933). Passing
|
||||
* `allowedOrigin` explicitly still overrides both.
|
||||
*/
|
||||
export function createOpenFreeMapSource(options = {}) {
|
||||
return createVectorTileSource({
|
||||
tileJsonUrl: 'https://tiles.openfreemap.org/planet',
|
||||
allowedOrigin: 'https://tiles.openfreemap.org',
|
||||
decode: decodeOpenFreeMapTile,
|
||||
maxEntries: 192,
|
||||
maxCacheBytes: 64 * 1024 * 1024,
|
||||
|
||||
@@ -320,3 +320,41 @@ test('Capitol, greenbelt and commercial property fixtures exclude non-public dri
|
||||
assert.ok(records.some((r) => r.area === area && !r.drivable));
|
||||
}
|
||||
});
|
||||
|
||||
test('repointing tileJsonUrl at a mirror never falls back to the default tile host', async () => {
|
||||
const urls = [];
|
||||
const mirror = 'http://127.0.0.1:12348';
|
||||
const source = createOpenFreeMapSource({
|
||||
fetchImpl: async (url) => {
|
||||
urls.push(url);
|
||||
return url.endsWith('/planet')
|
||||
? Response.json({ tiles: [`${mirror}/planet/v/{z}/{x}/{y}.pbf`] })
|
||||
: new Response(fixture('ofm-austin-14-3743-6745.pbf'));
|
||||
},
|
||||
tileJsonUrl: `${mirror}/planet`,
|
||||
});
|
||||
await source.fetchBounds(
|
||||
{ south: 30.264, north: 30.271, west: -97.75, east: -97.74 },
|
||||
{ zoom: 14 },
|
||||
);
|
||||
assert.ok(urls.length > 1, 'tiles were fetched');
|
||||
assert.deepEqual(
|
||||
urls.filter((url) => !url.startsWith(mirror)),
|
||||
[],
|
||||
'a repointed source must not fetch from tiles.openfreemap.org',
|
||||
);
|
||||
});
|
||||
|
||||
test('a mirror that serves the default host TileJSON verbatim fails loudly', async () => {
|
||||
// A transparent caching proxy returns upstream's TileJSON unchanged, whose
|
||||
// tiles[0] is an absolute upstream URL. That must be an error, not 100% of
|
||||
// tile traffic quietly leaving the configured mirror.
|
||||
const source = createOpenFreeMapSource({
|
||||
fetchImpl: async () =>
|
||||
Response.json({
|
||||
tiles: ['https://tiles.openfreemap.org/planet/v/{z}/{x}/{y}.pbf'],
|
||||
}),
|
||||
tileJsonUrl: 'http://127.0.0.1:12348/planet',
|
||||
});
|
||||
await assert.rejects(source.getMetadata(), { retryable: false });
|
||||
});
|
||||
@@ -16,11 +16,27 @@ export function validTileBounds(box) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The origin a configured TileJSON is served from, or undefined when there is
|
||||
* no absolute URL to read one off. Deriving `allowedOrigin` from it means
|
||||
* repointing `tileJsonUrl` at a mirror cannot silently keep loading tiles from
|
||||
* the old host: the origin check below then names the mismatch instead of
|
||||
* passing it (#933).
|
||||
*/
|
||||
function tileJsonOrigin(tileJsonUrl) {
|
||||
if (typeof tileJsonUrl !== 'string') return undefined;
|
||||
try {
|
||||
return new URL(tileJsonUrl).origin;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** A decoded XYZ cache with bounded workers, reads, retained bytes and request lifetimes. */
|
||||
export function createVectorTileSource({
|
||||
tileJsonUrl,
|
||||
template,
|
||||
allowedOrigin,
|
||||
allowedOrigin = tileJsonOrigin(tileJsonUrl),
|
||||
decode,
|
||||
fetchImpl = (...args) => globalThis.fetch(...args),
|
||||
maxTiles = 16,
|
||||
|
||||
@@ -203,3 +203,45 @@ test('polygon-selected tiles keep nearest-first order and cached revisits issue
|
||||
'over-budget selection is rejected before fetching',
|
||||
);
|
||||
});
|
||||
|
||||
test('a configured tileJsonUrl sets the tile origin, and a foreign tiles[0] is still refused', async () => {
|
||||
// Without an explicit allowedOrigin the mirror's own TileJSON is honoured…
|
||||
const mirrored = createVectorTileSource({
|
||||
tileJsonUrl: 'http://127.0.0.1:12348/planet',
|
||||
decode: () => [],
|
||||
fetchImpl: async () =>
|
||||
Response.json({ tiles: ['http://127.0.0.1:12348/p/{z}/{x}/{y}.pbf'] }),
|
||||
});
|
||||
assert.equal(
|
||||
(await mirrored.getMetadata()).template,
|
||||
'http://127.0.0.1:12348/p/{z}/{x}/{y}.pbf',
|
||||
);
|
||||
|
||||
// …while a TileJSON that names a different host is refused rather than
|
||||
// silently fetched, which is what the origin check is for.
|
||||
const foreign = createVectorTileSource({
|
||||
tileJsonUrl: 'http://127.0.0.1:12348/planet',
|
||||
decode: () => [],
|
||||
fetchImpl: async () =>
|
||||
Response.json({
|
||||
tiles: ['https://tiles.example/planet/{z}/{x}/{y}.pbf'],
|
||||
}),
|
||||
});
|
||||
await assert.rejects(foreign.getMetadata(), { retryable: false });
|
||||
});
|
||||
|
||||
test('an explicit allowedOrigin still wins over the one derived from tileJsonUrl', async () => {
|
||||
const source = createVectorTileSource({
|
||||
tileJsonUrl: 'http://127.0.0.1:12348/planet',
|
||||
allowedOrigin: 'https://tiles.example',
|
||||
decode: () => [],
|
||||
fetchImpl: async () =>
|
||||
Response.json({
|
||||
tiles: ['https://tiles.example/planet/{z}/{x}/{y}.pbf'],
|
||||
}),
|
||||
});
|
||||
assert.equal(
|
||||
(await source.getMetadata()).template,
|
||||
'https://tiles.example/planet/{z}/{x}/{y}.pbf',
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user