Skip to content

Commit

Permalink
Always retain raster tile cache control data (#10494)
Browse files Browse the repository at this point in the history
  • Loading branch information
andycalder authored Mar 24, 2021
1 parent 119b3df commit da4ce59
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 17 deletions.
6 changes: 2 additions & 4 deletions src/source/raster_dem_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class RasterDEMTileSource extends RasterTileSource implements Source {
const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme), false, this.tileSize);
tile.request = getImage(this.map._requestManager.transformRequest(url, ResourceType.Tile), imageLoaded.bind(this));

function imageLoaded(err, img) {
function imageLoaded(err, img, cacheControl, expires) {
delete tile.request;
if (tile.aborted) {
tile.state = 'unloaded';
Expand All @@ -41,9 +41,7 @@ class RasterDEMTileSource extends RasterTileSource implements Source {
tile.state = 'errored';
callback(err);
} else if (img) {
if (this.map._refreshExpiredTiles) tile.setExpiryData(img);
delete (img: any).cacheControl;
delete (img: any).expires;
if (this.map._refreshExpiredTiles) tile.setExpiryData({cacheControl, expires});
const transfer = window.ImageBitmap && img instanceof window.ImageBitmap && offscreenCanvasSupported();
// DEMData uses 1px padding. Handle cases with image buffer of 1 and 2 pxs, the rest assume default buffer 0
// in order to keep the previous implementation working (no validation against tileSize).
Expand Down
6 changes: 2 additions & 4 deletions src/source/raster_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class RasterTileSource extends Evented implements Source {
loadTile(tile: Tile, callback: Callback<void>) {
const use2x = browser.devicePixelRatio >= 2;
const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme), use2x, this.tileSize);
tile.request = getImage(this.map._requestManager.transformRequest(url, ResourceType.Tile), (err, img) => {
tile.request = getImage(this.map._requestManager.transformRequest(url, ResourceType.Tile), (err, img, cacheControl, expires) => {
delete tile.request;

if (tile.aborted) {
Expand All @@ -122,9 +122,7 @@ class RasterTileSource extends Evented implements Source {
tile.state = 'errored';
callback(err);
} else if (img) {
if (this.map._refreshExpiredTiles) tile.setExpiryData(img);
delete (img: any).cacheControl;
delete (img: any).expires;
if (this.map._refreshExpiredTiles) tile.setExpiryData({cacheControl, expires});

const context = this.map.painter.context;
const gl = context.gl;
Expand Down
15 changes: 6 additions & 9 deletions src/util/ajax.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import config from './config.js';
import assert from 'assert';
import {cacheGet, cachePut} from './tile_request_cache.js';
import webpSupported from './webp_supported.js';
import offscreenCanvasSupported from './offscreen_canvas_supported.js';

import type {Callback} from '../types/callback.js';
import type {Cancelable} from '../types/cancelable.js';
Expand Down Expand Up @@ -273,7 +272,7 @@ function sameOrigin(url) {

const transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';

function arrayBufferToImage(data: ArrayBuffer, callback: (err: ?Error, image: ?HTMLImageElement) => void, cacheControl: ?string, expires: ?string) {
function arrayBufferToImage(data: ArrayBuffer, callback: Callback<HTMLImageElement>) {
const img: HTMLImageElement = new window.Image();
const URL = window.URL;
img.onload = () => {
Expand All @@ -287,12 +286,10 @@ function arrayBufferToImage(data: ArrayBuffer, callback: (err: ?Error, image: ?H
};
img.onerror = () => callback(new Error('Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.'));
const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'});
(img: any).cacheControl = cacheControl;
(img: any).expires = expires;
img.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl;
}

function arrayBufferToImageBitmap(data: ArrayBuffer, callback: (err: ?Error, image: ?ImageBitmap) => void) {
function arrayBufferToImageBitmap(data: ArrayBuffer, callback: Callback<ImageBitmap>) {
const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'});
window.createImageBitmap(blob).then((imgBitmap) => {
callback(null, imgBitmap);
Expand All @@ -308,7 +305,7 @@ export const resetImageRequestQueue = () => {
};
resetImageRequestQueue();

export const getImage = function(requestParameters: RequestParameters, callback: Callback<HTMLImageElement | ImageBitmap>): Cancelable {
export const getImage = function(requestParameters: RequestParameters, callback: ResponseCallback<HTMLImageElement | ImageBitmap>): Cancelable {
if (webpSupported.supported) {
if (!requestParameters.headers) {
requestParameters.headers = {};
Expand Down Expand Up @@ -353,10 +350,10 @@ export const getImage = function(requestParameters: RequestParameters, callback:
if (err) {
callback(err);
} else if (data) {
if (offscreenCanvasSupported()) {
arrayBufferToImageBitmap(data, callback);
if (window.createImageBitmap) {
arrayBufferToImageBitmap(data, (err, imgBitmap) => callback(err, imgBitmap, cacheControl, expires));
} else {
arrayBufferToImage(data, callback, cacheControl, expires);
arrayBufferToImage(data, (err, img) => callback(err, img, cacheControl, expires));
}
}
});
Expand Down
54 changes: 54 additions & 0 deletions test/unit/util/ajax.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,5 +213,59 @@ test('ajax', (t) => {
window.server.respond();
});

t.test('getImage retains cache control headers when using arrayBufferToImage', (t) => {
resetImageRequestQueue();

const headers = {
'Content-Type': 'image/webp',
'Cache-Control': 'max-age=43200,s-maxage=604800',
'Expires': 'Wed, 21 Oct 2015 07:28:00 GMT'
};

window.server.respondWith(request => request.respond(200, headers, ''));

// jsdom doesn't call image onload; fake it https://github.com/jsdom/jsdom/issues/1816
window.Image = class {
set src(src) {
setTimeout(() => {
if (this.onload) this.onload();
});
}
};

getImage({url: ''}, (err, img, cacheControl, expires) => {
if (err) t.fail();
t.equals(cacheControl, headers['Cache-Control']);
t.equals(expires, headers['Expires']);
t.end();
});

window.server.respond();
});

t.test('getImage retains cache control headers when using arrayBufferToImageBitmap', (t) => {
resetImageRequestQueue();

const headers = {
'Content-Type': 'image/webp',
'Cache-Control': 'max-age=43200,s-maxage=604800',
'Expires': 'Wed, 21 Oct 2015 07:28:00 GMT'
};

window.server.respondWith(request => request.respond(200, headers, ''));

// jsdom doesn't support createImageBitmap; fake it
window.createImageBitmap = () => Promise.resolve();

getImage({url: ''}, (err, img, cacheControl, expires) => {
if (err) t.fail();
t.equals(cacheControl, headers['Cache-Control']);
t.equals(expires, headers['Expires']);
t.end();
});

window.server.respond();
});

t.end();
});

0 comments on commit da4ce59

Please sign in to comment.