mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +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 };
|
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 = {}) {
|
export function createOpenFreeMapSource(options = {}) {
|
||||||
return createVectorTileSource({
|
return createVectorTileSource({
|
||||||
tileJsonUrl: 'https://tiles.openfreemap.org/planet',
|
tileJsonUrl: 'https://tiles.openfreemap.org/planet',
|
||||||
allowedOrigin: 'https://tiles.openfreemap.org',
|
|
||||||
decode: decodeOpenFreeMapTile,
|
decode: decodeOpenFreeMapTile,
|
||||||
maxEntries: 192,
|
maxEntries: 192,
|
||||||
maxCacheBytes: 64 * 1024 * 1024,
|
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));
|
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. */
|
/** A decoded XYZ cache with bounded workers, reads, retained bytes and request lifetimes. */
|
||||||
export function createVectorTileSource({
|
export function createVectorTileSource({
|
||||||
tileJsonUrl,
|
tileJsonUrl,
|
||||||
template,
|
template,
|
||||||
allowedOrigin,
|
allowedOrigin = tileJsonOrigin(tileJsonUrl),
|
||||||
decode,
|
decode,
|
||||||
fetchImpl = (...args) => globalThis.fetch(...args),
|
fetchImpl = (...args) => globalThis.fetch(...args),
|
||||||
maxTiles = 16,
|
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',
|
'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