diff --git a/src/source/geojson_worker_source.js b/src/source/geojson_worker_source.js index 9e0353cac70..95defc4aa93 100644 --- a/src/source/geojson_worker_source.js +++ b/src/source/geojson_worker_source.js @@ -20,7 +20,7 @@ import type Actor from '../util/actor'; import type StyleLayerIndex from '../style/style_layer_index'; import type {LoadVectorDataCallback} from './vector_tile_worker_source'; -import type {RequestParameters} from '../util/ajax'; +import type { RequestParameters, ResponseCallback } from '../util/ajax'; import type { Callback } from '../types/callback'; import type {GeoJSONFeature} from '@mapbox/geojson-types'; @@ -33,7 +33,7 @@ export type LoadGeoJSONParameters = { geojsonVtOptions?: Object }; -export type LoadGeoJSON = (params: LoadGeoJSONParameters, callback: Callback) => void; +export type LoadGeoJSON = (params: LoadGeoJSONParameters, callback: ResponseCallback) => void; export interface GeoJSONIndex { getTile(z: number, x: number, y: number): Object; @@ -161,7 +161,7 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { const perf = (params && params.request && params.request.collectResourceTiming) ? new performance.Performance(params.request) : false; - this.loadGeoJSON(params, (err, data) => { + this.loadGeoJSON(params, (err: ?Error, data: ?Object) => { if (err || !data) { return callback(err); } else if (typeof data !== 'object') { @@ -254,7 +254,7 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { * @param [params.url] A URL to the remote GeoJSON data. * @param [params.data] Literal GeoJSON data. Must be provided if `params.url` is not. */ - loadGeoJSON(params: LoadGeoJSONParameters, callback: Callback) { + loadGeoJSON(params: LoadGeoJSONParameters, callback: ResponseCallback) { // Because of same origin issues, urls must either include an explicit // origin or absolute path. // ie: /foo/bar.json or http://example.com/bar.json diff --git a/src/source/load_tilejson.js b/src/source/load_tilejson.js index c69bed8015a..06006e8f611 100644 --- a/src/source/load_tilejson.js +++ b/src/source/load_tilejson.js @@ -12,7 +12,7 @@ import type {TileJSON} from '../types/tilejson'; import type {Cancelable} from '../types/cancelable'; export default function(options: any, requestTransformFn: RequestTransformFunction, callback: Callback): Cancelable { - const loaded = function(err, tileJSON: any) { + const loaded = function(err: ?Error, tileJSON: ?Object) { if (err) { return callback(err); } else if (tileJSON) { diff --git a/src/source/vector_tile_worker_source.js b/src/source/vector_tile_worker_source.js index 2ccc9349747..e64909724a0 100644 --- a/src/source/vector_tile_worker_source.js +++ b/src/source/vector_tile_worker_source.js @@ -1,6 +1,6 @@ // @flow -import {getArrayBuffer} from '../util/ajax'; +import { getArrayBuffer } from '../util/ajax'; import vt from '@mapbox/vector-tile'; import Protobuf from 'pbf'; @@ -43,15 +43,15 @@ export type LoadVectorData = (params: WorkerTileParameters, callback: LoadVector * @private */ function loadVectorTile(params: WorkerTileParameters, callback: LoadVectorDataCallback) { - const request = getArrayBuffer(params.request, (err, response) => { + const request = getArrayBuffer(params.request, (err: ?Error, data: ?ArrayBuffer, cacheControl: ?string, expires: ?string) => { if (err) { callback(err); - } else if (response) { + } else if (data) { callback(null, { - vectorTile: new vt.VectorTile(new Protobuf(response.data)), - rawData: response.data, - cacheControl: response.cacheControl, - expires: response.expires + vectorTile: new vt.VectorTile(new Protobuf(data)), + rawData: data, + cacheControl: cacheControl, + expires: expires }); } }); diff --git a/src/source/worker.js b/src/source/worker.js index 98fcb3a7419..85f1f15691d 100644 --- a/src/source/worker.js +++ b/src/source/worker.js @@ -32,6 +32,7 @@ export default class Worker { workerSourceTypes: { [string]: Class }; workerSources: { [string]: { [string]: { [string]: WorkerSource } } }; demWorkerSources: { [string]: { [string]: RasterDEMTileWorkerSource } }; + referrer: ?string; constructor(self: WorkerGlobalScopeInterface) { this.self = self; @@ -65,6 +66,10 @@ export default class Worker { }; } + setReferrer(mapID: string, referrer: string) { + this.referrer = referrer; + } + setLayers(mapId: string, layers: Array, callback: WorkerTileCallback) { this.getLayerIndex(mapId).replace(layers); callback(); @@ -196,5 +201,5 @@ export default class Worker { if (typeof WorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof WorkerGlobalScope) { - new Worker(self); + self.worker = new Worker(self); } diff --git a/src/style/load_glyph_range.js b/src/style/load_glyph_range.js index 13208efcf87..2e1061f0604 100644 --- a/src/style/load_glyph_range.js +++ b/src/style/load_glyph_range.js @@ -3,6 +3,7 @@ import { normalizeGlyphsURL } from '../util/mapbox'; import { getArrayBuffer, ResourceType } from '../util/ajax'; + import parseGlyphPBF from './parse_glyph_pbf'; import type {StyleGlyph} from './style_glyph'; @@ -23,13 +24,13 @@ export default function (fontstack: string, .replace('{range}', `${begin}-${end}`), ResourceType.Glyphs); - getArrayBuffer(request, (err, response) => { + getArrayBuffer(request, (err: ?Error, data: ?ArrayBuffer) => { if (err) { callback(err); - } else if (response) { + } else if (data) { const glyphs = {}; - for (const glyph of parseGlyphPBF(response.data)) { + for (const glyph of parseGlyphPBF(data)) { glyphs[glyph.id] = glyph; } diff --git a/src/style/load_sprite.js b/src/style/load_sprite.js index 6b887383f44..eb22f092fc7 100644 --- a/src/style/load_sprite.js +++ b/src/style/load_sprite.js @@ -17,7 +17,7 @@ export default function(baseURL: string, let json: any, image, error; const format = browser.devicePixelRatio > 1 ? '@2x' : ''; - let jsonRequest = getJSON(transformRequestCallback(normalizeSpriteURL(baseURL, format, '.json'), ResourceType.SpriteJSON), (err, data) => { + let jsonRequest = getJSON(transformRequestCallback(normalizeSpriteURL(baseURL, format, '.json'), ResourceType.SpriteJSON), (err: ?Error, data: ?Object) => { jsonRequest = null; if (!error) { error = err; diff --git a/src/style/style.js b/src/style/style.js index a23fc7e4c60..977fa39361f 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -11,7 +11,7 @@ import GlyphManager from '../render/glyph_manager'; import Light from './light'; import LineAtlas from '../render/line_atlas'; import { pick, clone, extend, deepEqual, filterObject, mapObject } from '../util/util'; -import { getJSON, ResourceType } from '../util/ajax'; +import { getJSON, getReferrer, ResourceType } from '../util/ajax'; import { isMapboxURL, normalizeStyleURL } from '../util/mapbox'; import browser from '../util/browser'; import Dispatcher from '../util/dispatcher'; @@ -144,6 +144,8 @@ class Style extends Evented { this._resetUpdates(); + this.dispatcher.broadcast('setReferrer', getReferrer()); + const self = this; this._rtlTextPluginCallback = Style.registerForPluginAvailability((args) => { self.dispatcher.broadcast('loadRTLTextPlugin', args.pluginURL, args.completionCallback); @@ -188,12 +190,12 @@ class Style extends Evented { url = normalizeStyleURL(url, options.accessToken); const request = this.map._transformRequest(url, ResourceType.Style); - this._request = getJSON(request, (error, json) => { + this._request = getJSON(request, (error: ?Error, json: ?Object) => { this._request = null; if (error) { this.fire(new ErrorEvent(error)); } else if (json) { - this._load((json: any), validate); + this._load(json, validate); } }); } diff --git a/src/types/window.js b/src/types/window.js index 729bc21b3d2..54bf7d2f8c5 100644 --- a/src/types/window.js +++ b/src/types/window.js @@ -18,6 +18,7 @@ export interface Window extends EventTarget, IDBEnvironment { +isSecureContext: boolean; +length: number; +location: Location; + +origin: string; name: string; +navigator: Navigator; offscreenBuffering: string | boolean; @@ -131,6 +132,8 @@ export interface Window extends EventTarget, IDBEnvironment { WheelEvent: typeof WheelEvent; Worker: typeof Worker; XMLHttpRequest: typeof XMLHttpRequest; + Request: typeof Request; + AbortController: any; alert(message?: any): void; blur(): void; diff --git a/src/util/ajax.js b/src/util/ajax.js index befd99aefc3..d8fa3890392 100644 --- a/src/util/ajax.js +++ b/src/util/ajax.js @@ -2,6 +2,7 @@ import window from './window'; import { extend } from './util'; +import { isMapboxHTTPURL } from './mapbox'; import type { Callback } from '../types/callback'; import type { Cancelable } from '../types/cancelable'; @@ -39,14 +40,21 @@ export type RequestParameters = { url: string, headers?: Object, method?: 'GET' | 'POST' | 'PUT', + body?: string, + type?: 'string' | 'json' | 'arrayBuffer', credentials?: 'same-origin' | 'include', collectResourceTiming?: boolean }; +export type ResponseCallback = (error: ?Error, data: ?T, cacheControl: ?string, expires: ?string) => void; + class AJAXError extends Error { status: number; url: string; constructor(message: string, status: number, url: string) { + if (status === 401 && isMapboxHTTPURL(url)) { + message += ': you may have provided an invalid Mapbox access token. See https://www.mapbox.com/api-documentation/#access-tokens'; + } super(message); this.status = status; this.url = url; @@ -61,84 +69,101 @@ class AJAXError extends Error { } } -function makeRequest(requestParameters: RequestParameters): XMLHttpRequest { +// Ensure that we're sending the correct referrer from blob URL worker bundles. +// For files loaded from the local file system, `location.origin` will be set +// to the string(!) "null" (Firefox), or "file://" (Chrome, Safari, Edge, IE), +// and we will set an empty referrer. Otherwise, we're using the document's URL. +/* global self, WorkerGlobalScope */ +export const getReferrer = typeof WorkerGlobalScope !== 'undefined' && + typeof self !== 'undefined' && + self instanceof WorkerGlobalScope ? + () => self.worker && self.worker.referrer : + () => { + const origin = window.location.origin; + if (origin && origin !== 'null' && origin !== 'file://') { + return origin + window.location.pathname; + } + }; + +function makeFetchRequest(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { + const controller = new window.AbortController(); + const request = new window.Request(requestParameters.url, { + method: requestParameters.method || 'GET', + body: requestParameters.body, + credentials: requestParameters.credentials, + headers: requestParameters.headers, + referrer: getReferrer(), + signal: controller.signal + }); + + if (requestParameters.type === 'json') { + request.headers.set('Accept', 'application/json'); + } + + window.fetch(request).then(response => { + if (response.ok) { + response[requestParameters.type || 'text']().then(result => { + callback(null, result, response.headers.get('Cache-Control'), response.headers.get('Expires')); + }).catch(callback); + } else { + callback(new AJAXError(response.statusText, response.status, requestParameters.url)); + } + }).catch((error) => { + callback(new Error(error.message)); + }); + + return { cancel: () => controller.abort() }; +} + +function makeXMLHttpRequest(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { const xhr: XMLHttpRequest = new window.XMLHttpRequest(); xhr.open(requestParameters.method || 'GET', requestParameters.url, true); + if (requestParameters.type === 'arrayBuffer') { + xhr.responseType = 'arraybuffer'; + } for (const k in requestParameters.headers) { xhr.setRequestHeader(k, requestParameters.headers[k]); } + if (requestParameters.type === 'json') { + xhr.setRequestHeader('Accept', 'application/json'); + } xhr.withCredentials = requestParameters.credentials === 'include'; - return xhr; -} - -export const getJSON = function(requestParameters: RequestParameters, callback: Callback): Cancelable { - const xhr = makeRequest(requestParameters); - xhr.setRequestHeader('Accept', 'application/json'); - xhr.onerror = function() { + xhr.onerror = () => { callback(new Error(xhr.statusText)); }; - xhr.onload = function() { - if (((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) && xhr.response) { - let data; - try { - data = JSON.parse(xhr.response); - } catch (err) { - return callback(err); - } - callback(null, data); - } else { - if (xhr.status === 401 && requestParameters.url.match(/mapbox.com/)) { - callback(new AJAXError(`${xhr.statusText}: you may have provided an invalid Mapbox access token. See https://www.mapbox.com/api-documentation/#access-tokens`, xhr.status, requestParameters.url)); - } else { - callback(new AJAXError(xhr.statusText, xhr.status, requestParameters.url)); + xhr.onload = () => { + if (((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) && xhr.response !== null) { + let data: mixed = xhr.response; + if (requestParameters.type === 'json') { + // We're manually parsing JSON here to get better error messages. + try { + data = JSON.parse(xhr.response); + } catch (err) { + return callback(err); + } } - } - }; - xhr.send(); - return { cancel: () => xhr.abort() }; -}; - -export const getArrayBuffer = function(requestParameters: RequestParameters, callback: Callback<{data: ArrayBuffer, cacheControl: ?string, expires: ?string}>): Cancelable { - const xhr = makeRequest(requestParameters); - xhr.responseType = 'arraybuffer'; - xhr.onerror = function() { - callback(new Error(xhr.statusText)); - }; - xhr.onload = function() { - const response: ArrayBuffer = xhr.response; - if (response.byteLength === 0 && xhr.status === 200) { - return callback(new Error('http status 200 returned without content.')); - } - if (((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) && xhr.response) { - callback(null, { - data: response, - cacheControl: xhr.getResponseHeader('Cache-Control'), - expires: xhr.getResponseHeader('Expires') - }); + callback(null, data, xhr.getResponseHeader('Cache-Control'), xhr.getResponseHeader('Expires')); } else { callback(new AJAXError(xhr.statusText, xhr.status, requestParameters.url)); } }; - xhr.send(); + xhr.send(requestParameters.body); return { cancel: () => xhr.abort() }; +} + +const makeRequest = window.fetch && window.Request && window.AbortController ? makeFetchRequest : makeXMLHttpRequest; + +export const getJSON = function(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { + return makeRequest(extend(requestParameters, { type: 'json' }), callback); }; -export const postData = function(requestParameters: RequestParameters, payload: string, callback: Callback): Cancelable { - const xhr = makeRequest(extend(requestParameters, {method: 'POST'})); +export const getArrayBuffer = function(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { + return makeRequest(extend(requestParameters, { type: 'arrayBuffer' }), callback); +}; - xhr.onerror = function() { - callback(new Error(xhr.statusText)); - }; - xhr.onload = function() { - if (xhr.status >= 200 && xhr.status < 300) { - callback(null, xhr.response); - } else { - callback(new AJAXError(xhr.statusText, xhr.status, requestParameters.url)); - } - }; - xhr.send(payload); - return { cancel: () => xhr.abort() }; +export const postData = function(requestParameters: RequestParameters, callback: ResponseCallback): Cancelable { + return makeRequest(extend(requestParameters, { method: 'POST' }), callback); }; function sameOrigin(url) { @@ -152,10 +177,10 @@ const transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA export const getImage = function(requestParameters: RequestParameters, callback: Callback): Cancelable { // request the image with XHR to work around caching issues // see https://github.com/mapbox/mapbox-gl-js/issues/1470 - return getArrayBuffer(requestParameters, (err, imgData) => { + return getArrayBuffer(requestParameters, (err: ?Error, data: ?ArrayBuffer, cacheControl: ?string, expires: ?string) => { if (err) { callback(err); - } else if (imgData) { + } else if (data) { const img: HTMLImageElement = new window.Image(); const URL = window.URL || window.webkitURL; img.onload = () => { @@ -163,10 +188,10 @@ export const getImage = function(requestParameters: RequestParameters, callback: URL.revokeObjectURL(img.src); }; 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(imgData.data)], { type: 'image/png' }); - (img: any).cacheControl = imgData.cacheControl; - (img: any).expires = imgData.expires; - img.src = imgData.data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl; + 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; } }); }; diff --git a/src/util/mapbox.js b/src/util/mapbox.js index bad2e266269..356a2f734db 100644 --- a/src/util/mapbox.js +++ b/src/util/mapbox.js @@ -46,7 +46,12 @@ function isMapboxURL(url: string) { return url.indexOf('mapbox:') === 0; } -export { isMapboxURL }; +const mapboxHTTPURLRe = /^((https?:)?\/\/)?([^\/]+\.)?mapbox\.c(n|om)(\/|\?|$)/i; +function isMapboxHTTPURL(url: string): boolean { + return mapboxHTTPURLRe.test(url); +} + +export { isMapboxURL, isMapboxHTTPURL }; export const normalizeStyleURL = function(url: string, accessToken?: string): string { if (!isMapboxURL(url)) return url; @@ -146,7 +151,7 @@ export class TurnstileEvent { // mapbox tiles. if (config.ACCESS_TOKEN && Array.isArray(tileUrls) && - tileUrls.some((url) => { return /(mapbox\.c)(n|om)/i.test(url); })) { + tileUrls.some(url => isMapboxHTTPURL(url))) { this.queueRequest(browser.now()); } } @@ -199,25 +204,25 @@ export class TurnstileEvent { return this.processRequests(); } - const evenstUrlObject: UrlObject = parseUrl(config.EVENTS_URL); - evenstUrlObject.params.push(`access_token=${config.ACCESS_TOKEN || ''}`); + const eventsUrlObject: UrlObject = parseUrl(config.EVENTS_URL); + eventsUrlObject.params.push(`access_token=${config.ACCESS_TOKEN || ''}`); + const request: RequestParameters = { - url: formatUrl(evenstUrlObject), + url: formatUrl(eventsUrlObject), headers: { - 'Content-Type': 'text/plain' //Skip the pre-flight OPTIONS request - } + 'Content-Type': 'text/plain' // Skip the pre-flight OPTIONS request + }, + body: JSON.stringify([{ + event: 'appUserTurnstile', + created: (new Date(nextUpdate)).toISOString(), + sdkIdentifier: 'mapbox-gl-js', + sdkVersion: version, + 'enabled.telemetry': false, + userId: this.eventData.anonId + }]) }; - const payload = JSON.stringify([{ - event: 'appUserTurnstile', - created: (new Date(nextUpdate)).toISOString(), - sdkIdentifier: 'mapbox-gl-js', - sdkVersion: version, - 'enabled.telemetry': false, - userId: this.eventData.anonId - }]); - - this.pendingRequest = postData(request, payload, (error) => { + this.pendingRequest = postData(request, (error: ?Error) => { this.pendingRequest = null; if (!error) { this.eventData.lastSuccess = nextUpdate; diff --git a/test/ajax_stubs.js b/test/ajax_stubs.js index 495dcdc8579..d3e808ff8c2 100644 --- a/test/ajax_stubs.js +++ b/test/ajax_stubs.js @@ -34,6 +34,8 @@ function cached(data, callback) { }); } +export const getReferrer = () => undefined; + export const getJSON = function({ url }, callback) { if (cache[url]) return cached(cache[url], callback); return request(url, (error, response, body) => { @@ -56,8 +58,8 @@ export const getArrayBuffer = function({ url }, callback) { if (cache[url]) return cached(cache[url], callback); return request({ url, encoding: null }, (error, response, body) => { if (!error && response.statusCode >= 200 && response.statusCode < 300) { - cache[url] = {data: body}; - callback(null, {data: body}); + cache[url] = body; + callback(null, body); } else { if (!error) error = { status: +response.statusCode }; callback(error); @@ -65,10 +67,10 @@ export const getArrayBuffer = function({ url }, callback) { }); }; -export const postData = function({ url }, payload, callback) { - return request.post(url, payload, (error, response, body) => { +export const postData = function({ url, body }, callback) { + return request.post(url, body, (error, response, body) => { if (!error && response.statusCode >= 200 && response.statusCode < 300) { - callback(null, {data: body}); + callback(null, body); } else { callback(error || new Error(response.statusCode)); } diff --git a/test/unit/util/ajax.test.js b/test/unit/util/ajax.test.js index 4d7a1b392dd..7adcb25a6e6 100644 --- a/test/unit/util/ajax.test.js +++ b/test/unit/util/ajax.test.js @@ -17,18 +17,6 @@ test('ajax', (t) => { callback(); }); - t.test('getArrayBuffer, no content error', (t) => { - window.server.respondWith(request => { - request.respond(200, {'Content-Type': 'image/png'}, ''); - }); - getArrayBuffer({ url:'' }, (error) => { - t.pass('called getArrayBuffer'); - t.ok(error, 'should error when the status is 200 without content.'); - t.end(); - }); - window.server.respond(); - }); - t.test('getArrayBuffer, 404', (t) => { window.server.respondWith(request => { request.respond(404); @@ -102,7 +90,7 @@ test('ajax', (t) => { window.server.respondWith(request => { request.respond(204); }); - postData({ url:'api.mapbox.com' }, {}, (error) => { + postData({ url:'api.mapbox.com' }, (error) => { t.equal(error, null); t.end(); }); diff --git a/test/unit/util/mapbox.test.js b/test/unit/util/mapbox.test.js index 52876d81b97..433802a4933 100644 --- a/test/unit/util/mapbox.test.js +++ b/test/unit/util/mapbox.test.js @@ -6,6 +6,16 @@ import window from '../../../src/util/window'; import { uuid } from '../../../src/util/util'; import { version } from '../../../package.json'; +const mapboxTileURLs = [ + 'https://a.tiles.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7/{z}/{x}/{y}.vector.pbf', + 'https://b.tiles.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7/{z}/{x}/{y}.vector.pbf' +]; + +const nonMapboxTileURLs = [ + 'https://a.example.com/tiles/{z}/{x}/{y}.mvt', + 'https://b.example.com/tiles/{z}/{x}/{y}.mvt' +]; + test("mapbox", (t) => { const mapboxSource = 'mapbox://user.map'; const nonMapboxSource = 'http://www.example.com/tiles.json'; @@ -15,6 +25,18 @@ test("mapbox", (t) => { callback(); }); + t.test('.isMapboxHTTPURL', (t) => { + t.ok(mapbox.isMapboxHTTPURL('http://mapbox.com')); + t.ok(mapbox.isMapboxHTTPURL('https://mapbox.com')); + t.ok(mapbox.isMapboxHTTPURL('https://mapbox.com/')); + t.ok(mapbox.isMapboxHTTPURL('https://mapbox.com?')); + t.ok(mapbox.isMapboxHTTPURL('https://api.mapbox.com/tiles')); + t.ok(mapbox.isMapboxHTTPURL('https://api.mapbox.cn/tiles')); + t.ok(mapbox.isMapboxHTTPURL('http://a.tiles.mapbox.cn/tiles')); + t.notOk(mapbox.isMapboxHTTPURL('http://example.com/mapbox.com')); + t.end(); + }); + t.test('.normalizeStyleURL', (t) => { t.test('returns an API URL with access_token parameter when no query string', (t) => { t.equal(mapbox.normalizeStyleURL('mapbox://styles/user/style'), 'https://api.mapbox.com/styles/v1/user/style?access_token=key'); @@ -300,14 +322,14 @@ test("mapbox", (t) => { t.test('does not POST when mapboxgl.ACCESS_TOKEN is not set', (t) => { config.ACCESS_TOKEN = null; - event.postTurnstileEvent([' a.tiles.mapxbox.com']); + event.postTurnstileEvent(mapboxTileURLs); t.equal(window.server.requests.length, 0); t.end(); }); t.test('does not POST when url does not point to mapbox.com', (t) => { - event.postTurnstileEvent(['a.tiles.boxmap.com']); + event.postTurnstileEvent(nonMapboxTileURLs); t.equal(window.server.requests.length, 0); t.end(); @@ -317,7 +339,7 @@ test("mapbox", (t) => { const previousUrl = config.API_URL; config.API_URL = 'https://api.mapbox.cn'; - event.postTurnstileEvent(['a.tiles.mapbox.cn']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -361,7 +383,7 @@ test("mapbox", (t) => { t.stub(browser, 'now').callsFake(() => now + 5); // A bit later - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); t.false(window.server.requests.length); t.end(); @@ -377,7 +399,7 @@ test("mapbox", (t) => { t.stub(browser, 'now').callsFake(() => now + ms25Hours); // next day - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -397,7 +419,7 @@ test("mapbox", (t) => { t.stub(browser, 'now').callsFake(() => now); // Past relative ot lastSuccess - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -410,12 +432,12 @@ test("mapbox", (t) => { t.test('does not POST appuserTurnstile event second time within same calendar day', (t) => { let now = +Date.now(); t.stub(browser, 'now').callsFake(() => now); - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); //Post second event const firstEvent = now; now += (60 * 1000); // A bit later - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -431,12 +453,12 @@ test("mapbox", (t) => { t.test('does not POST appuserTurnstile event second time when clock goes backwards less than a day', (t) => { let now = +Date.now(); t.stub(browser, 'now').callsFake(() => now); - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); //Post second event const firstEvent = now; now -= (60 * 1000); // A bit earlier - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -452,7 +474,7 @@ test("mapbox", (t) => { t.test('POSTs appuserTurnstile event when access token changes', (t) => { config.ACCESS_TOKEN = 'pk.new.*'; - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -467,7 +489,7 @@ test("mapbox", (t) => { t.test('when LocalStorage is not available', (t) => { t.test('POSTs appuserTurnstile event', (t) => { - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -486,11 +508,11 @@ test("mapbox", (t) => { let now = +Date.now(); const firstEvent = now; t.stub(browser, 'now').callsFake(() => now); - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); //Post second event now += (60 * 1000); // A bit later - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -507,11 +529,11 @@ test("mapbox", (t) => { let now = +Date.now(); const firstEvent = now; t.stub(browser, 'now').callsFake(() => now); - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); //Post second event now -= (60 * 1000); // A bit earlier - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -527,7 +549,7 @@ test("mapbox", (t) => { t.test('POSTs appuserTurnstile event when access token changes', (t) => { config.ACCESS_TOKEN = 'pk.new.*'; - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const req = window.server.requests[0]; req.respond(200); @@ -546,11 +568,11 @@ test("mapbox", (t) => { let now = +Date.now(); t.stub(browser, 'now').callsFake(() => now); - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); now += ms25Hours; // Add a day const tomorrow = now; - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); let req = window.server.requests[0]; req.respond(200); @@ -573,15 +595,15 @@ test("mapbox", (t) => { t.stub(browser, 'now').callsFake(() => now); const today = now; - event.postTurnstileEvent(['a.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const laterToday = now + 1; now = laterToday; - event.postTurnstileEvent(['b.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const tomorrow = laterToday + ms25Hours; // Add a day now = tomorrow; - event.postTurnstileEvent(['c.tiles.mapbox.com']); + event.postTurnstileEvent(mapboxTileURLs); const reqToday = window.server.requests[0]; reqToday.respond(200);