UNPKG

@giro3d/giro3d

Version:

A JS/WebGL framework for 3D geospatial data visualization

195 lines (189 loc) 6.39 kB
class PrefixEntry { /** * @param urlPrefix - The URL prefix for this host entry. */ constructor(urlPrefix) { this.urlPrefix = urlPrefix; this.headers = new Map(); } /** * Sets a custom header applicable for URLs that match the prefix. * * @param name - The header name. * @param value - The header value. */ setHeader(name, value) { this.headers.set(name, value); } } const perHostProperties = new Map(); /** * Update the request options with stored configuration applicable to this URL. * * @param input - The URL. * @param options - The request options. * @returns The updated options, if any. If no options object is passed, and no * configuration applies to this URL, then returns `undefined`. * @example * HttpConfiguration.setHeader('http://example.com', 'Foo', 'bar'); * * const fetchOptions = \{ * method: 'POST', * body: 'whatever', * headers: \{ * Width: 200, * \} * \}; * * // Let's update the options with headers applicable to 'http://example.com' * HttpConfiguration.applyConfiguration('http://example.com', fetchOptions); * * // now fetchOptions should be * // \{ * // method: 'POST', * // body: 'whatever', * // headers: \{ * // Width: 200, * // Foo: 'bar', * // \} * // \} * * // We can now send our HTTP request with correct headers * fetch('http://example.com/index.html', fetchOptions); */ function applyConfiguration(input, options) { if (perHostProperties.size === 0) { return options; } let urlObj; if (typeof input === 'string') { urlObj = new URL(input); } else if (input instanceof URL) { urlObj = input; } else { urlObj = new URL(input.url); } const properties = perHostProperties.get(urlObj.hostname); if (!properties) { // Nothing to do return options; } if (!options) { options = {}; } const headers = options.headers ?? {}; const urlString = urlObj.toString(); for (const entry of properties) { if (urlString.startsWith(entry.urlPrefix)) { for (const [k, v] of entry.headers.entries()) { if (headers[k]) { // The request already has a header with the same name. // We may not override it as it was already set either by the user manually, // or by a previous entry with higher precedence. continue; } headers[k] = v; } } } options.headers = headers; return options; } /** * Sets the header for all HTTP requests that match the provided URL prefix. * * Note: The URL prefix must be a valid URL (e.g must contain a scheme and and host). * * @param urlPrefix - The URL prefix. * @param name - The header name. * @param value - The header value. */ function setHeader(urlPrefix, name, value) { const url = new URL(urlPrefix); const hostname = url.hostname; let hostEntry = perHostProperties.get(hostname); if (!hostEntry) { hostEntry = []; perHostProperties.set(hostname, hostEntry); } let prefixEntry = hostEntry.find(entry => entry.urlPrefix === urlPrefix); if (!prefixEntry) { prefixEntry = new PrefixEntry(urlPrefix); hostEntry.push(prefixEntry); // We want prefixes to be ordered from longer (more specific) to shorter (more global) hostEntry.sort((a, b) => b.urlPrefix.length - a.urlPrefix.length); } prefixEntry.setHeader(name, value); } /** * Sets the 'Authorization' header for the specified URL prefix. * * Note: this is a convenience function that calls {@link setHeader} internally: * * ```js * setHeader(urlPrefix, 'Authorization', value) * ``` * * @param urlPrefix - The URL prefix. * @param value - The header value * @example * // We wish to set the Authorization header for the 'example.com' * // domain to 'Bearer TOPLEVEL', except for the resources under * // 'example.com/sub/resource', where we use 'Bearer SUBRESOURCE'. * // * // Since 'example.com/sub/resource' is a longer prefix than 'example.com', * // its headers will have precedence and will be applied to HTTP requests * // that match this URL prefix. * HttpConfiguration.setAuth('https://example.com', 'Bearer TOPLEVEL'); * HttpConfiguration.setAuth('https://example.com/sub/resource', 'Bearer SUBRESOURCE'); * * HttpConfiguration.applyConfiguration('https://example.com/index.html') * // \{ 'Authorization', 'Bearer TOPLEVEL' \} * HttpConfiguration.applyConfiguration('https://example.com/sub/resource/index.html') * // \{ 'Authorization', 'Bearer SUBRESOURCE' \} */ function setAuth(urlPrefix, value) { setHeader(urlPrefix, 'Authorization', value); } /** * Removes all configurations. */ function clear() { perHostProperties.clear(); } /** * Contains configuration for HTTP requests. * * Configuration is based on _URL prefixes_: each configuration entry applies to an URL prefix and * will apply to any URL that matches this prefix. Longer prefixes have more precedence over shorter * ones, so that you can cascade configurations. For example, you can have a general configuration * for the `example.com` domain, then more specific configuration entries for sub-paths in the same * domain. * * Note: URL prefixes must be valid absolute URLs (including scheme): `http://example.com/foo` is a * valid prefix, but `example.com/foo` is not. * * Note: If you plan to use the same configuration for different schemes (e.g `http` and `https`, * you must register the configuration twice, one for each scheme). * * Important: this module do _not_ automatically process outgoing HTTP requests. It is not a service * worker or a middleware. The `Fetcher` module automatically processes the requests by querying * configuration from this module, but if you can't or don't want to use `Fetcher`, then you have to * patch the request yourself (see the example below). * * @example * // Set the `Accept-Language` header to `fr-CH` for all requests under `http://example.com`. * HttpConfiguration.setHeader('http://example.com', 'Accept-Language', 'fr-CH'); * * // Later, query the configuration for a resource under `http://example.com` * const fetchOptions = HttpConfiguration.applyConfiguration('http://example.com/myPage.html'); * * // And put the options in the fetch request. * fetch('http://example.com/myPage.html', fetchOptions); */ export default { setAuth, setHeader, applyConfiguration, clear };