UNPKG

@architect/http-proxy

Version:

Architect HTTP proxy distribution, extracted from @architect/functions (arc.http.proxy)

1,946 lines (1,864 loc) 210 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.proxy = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ /** * HTTP error response generator and template */ module.exports = { httpError, proxyConfig: proxyConfig() } function proxyConfig () { let title = 'Index not found' let message = `No static asset bucket or <code>get /</code> function found in your project. Possible solutions:<br? <ul> <li>Add <code>@static</code> to your project manifest</li> <li>Add <code>get /</code> to the <code>@http</code> pragma of your project manifest</li> <li>Manually specify an S3 bucket (containing an <code>index.html</code> file) with the <code>ARC_STATIC_BUCKET</code> env var</li> <li>If using <code>arc.http.proxy</code>, pass in a valid config object</li> </ul> <a href="https://arc.codes/primitives/static" target="_blank">Learn more</a>` return httpError({title, message}) } function httpError ({statusCode=502, title='Unknown error', message=''}) { title = title === 'Error' ? `${statusCode} error` : `${statusCode} error: ${title}` return { statusCode, headers: { 'Content-Type': 'text/html; charset=utf8;', 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0' }, body: ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>${title}</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } code { font-size: 1.25rem; color: #00c26e; } .max-width-320 { max-width: 20rem; } .margin-left-8 { margin-left: 0.5rem; } .margin-bottom-16 { margin-bottom: 1rem; } .margin-bottom-8 { margin-bottom: 0.5rem; } .padding-32 { padding: 2rem; } .padding-top-16 { padding-top: 1rem; } a, a:hover { color: #333; } p, li { padding-bottom: 0.5rem; } </style> </head> <body class="padding-32"> <div> <div class="margin-left-8"> <div class="margin-bottom-16"> <h1 class="margin-bottom-16"> ${title} </h1> <p> ${message} </p> </div> <div class="padding-top-16"> <p> View Architect documentation at: </p> <a href="https://arc.codes">https://arc.codes</a> </div> </div> </div> </body> </html> ` } } },{}],2:[function(require,module,exports){ /** * Common binary MIME types */ module.exports = [ 'application/octet-stream', // Docs 'application/epub+zip', 'application/msword', 'application/pdf', 'application/rtf', 'application/vnd.amazon.ebook', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // Fonts 'font/otf', 'font/woff', 'font/woff2', // Images 'image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/tiff', 'image/vnd.microsoft.icon', 'image/webp', // Audio 'audio/3gpp', 'audio/aac', 'audio/basic', 'audio/mpeg', 'audio/ogg', 'audio/wavaudio/webm', 'audio/x-aiff', 'audio/x-midi', 'audio/x-wav', // Video 'video/3gpp', 'video/mp2t', 'video/mpeg', 'video/ogg', 'video/quicktime', 'video/webm', 'video/x-msvideo', // Archives 'application/java-archive', 'application/vnd.apple.installer+xml', 'application/x-7z-compressed', 'application/x-apple-diskimage', 'application/x-bzip', 'application/x-bzip2', 'application/x-gzip', 'application/x-java-archive', 'application/x-rar-compressed', 'application/x-tar', 'application/x-zip', 'application/zip' ] },{}],3:[function(require,module,exports){ // Bundler index + defaults const GetIndexDefaultHandler = require('./index.js') exports.handler = GetIndexDefaultHandler.proxy({ spa: true }) },{"./index.js":8}],4:[function(require,module,exports){ let { gzipSync, gunzipSync, brotliCompressSync, brotliDecompressSync, deflateSync, inflateSync, } = require('zlib') function compressor (direction, type, data) { let compress = direction === 'compress' let exec = { gzip: compress ? gzipSync : gunzipSync, br: compress ? brotliCompressSync : brotliDecompressSync, deflate: compress ? deflateSync : inflateSync } if (!exec[type]) throw ReferenceError('Invalid compression type specified, must be gzip, br, or deflate') return exec[type](data) } module.exports = { compress: compressor.bind({}, 'compress'), decompress: compressor.bind({}, 'decompress') } },{"zlib":undefined}],5:[function(require,module,exports){ let mime = require('mime-types') let path = require('path') let { compress } = require('./compress') /** * Normalizes response shape */ module.exports = function normalizeResponse (params) { let { response, result, Key, isProxy, contentEncoding, config } = params let noCache = [ 'text/html', 'application/json', 'application/vnd.api+json' ] response.headers = response.headers || {} // Establish Content-Type let contentType = response.headers['Content-Type'] || // Possibly get content-type passed via proxy plugins response.headers['content-type'] || // ... result && result.ContentType || // Fall back to what came down from S3's metadata mime.contentType(path.extname(Key)) // Finally, fall back to the mime type database // Set Content-Type response.headers['Content-Type'] = contentType // Set caching headers let neverCache = noCache.some(n => contentType.includes(n)) if (config.cacheControl) { response.headers['Cache-Control'] = config.cacheControl } else if (neverCache) { response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0' } else if (result && result.CacheControl) { response.headers['Cache-Control'] = result.CacheControl } else { response.headers['Cache-Control'] = 'public, max-age=0, must-revalidate' } // Populate optional userland headers if (config.headers) { Object.keys(config.headers).forEach(h => response.headers[h] = config.headers[h]) } // Normalize important common header casings to prevent dupes if (response.headers['content-type']) { response.headers['Content-Type'] = response.headers['content-type'] delete response.headers['content-type'] } // Probably unable to be set via this code path, but normalize jic if (response.headers['cache-control']) { response.headers['Cache-Control'] = response.headers['cache-control'] delete response.headers['cache-control'] } let notArcSix = !process.env.ARC_CLOUDFORMATION let notArcProxy = !process.env.ARC_HTTP || process.env.ARC_HTTP === 'aws' let isArcFive = notArcSix && notArcProxy let isHTML = response.headers['Content-Type'].includes('text/html') if (isArcFive && isHTML && !isProxy) { // This is a deprecated code path that may be removed when Arc 5 exits LTS status // Only return string bodies for certain types, and ONLY in Arc 5 response.body = Buffer.from(response.body).toString() response.type = response.headers['Content-Type'] // Re-set type or it will fall back to JSON } else { if (contentEncoding) { response.body = compress(contentEncoding, response.body) response.headers['Content-Encoding'] = contentEncoding } // Base64 everything else on the way out to enable text + binary support response.body = Buffer.from(response.body).toString('base64') response.isBase64Encoded = true } // Add ETag response.headers.ETag = result.ETag return response } },{"./compress":4,"mime-types":15,"path":undefined}],6:[function(require,module,exports){ module.exports = function templatizeResponse (params) { let { isBinary, assets, response, isLocal=false } = params // Bail early if (isBinary) { return response } else { // Find: ${STATIC('path/filename.ext')} // or: ${arc.static('path/filename.ext')} let staticRegex = /\${(STATIC|arc\.static)\(.*\)}/g // Maybe stringify jic previous steps passed a buffer; perhaps we can remove this step if/when proxy plugins is retired let body = response.body instanceof Buffer ? response.body.toString() : response.body response.body = body.replace(staticRegex, function fingerprint(match) { let start = match.startsWith(`\${STATIC(`) ? 10 : 14 let Key = match.slice(start, match.length-3) // Normalize around no leading slash for static manifest lookups let startsWithSlash = Key.startsWith('/') let lookup = startsWithSlash ? Key.substr(1) : Key if (assets && assets[lookup] && !isLocal) { Key = assets[lookup] Key = startsWithSlash ? `/${Key}` : Key } return Key }) response.body = Buffer.from(response.body) // Re-enbufferize return response } } },{}],7:[function(require,module,exports){ /** * transform reduces {headers, body} with given plugins * * @param args - arguments obj * @param args.Key - the origin S3 bucket Key requested * @param args.config - the entire arc.proxy.public config obj * @param args.defaults - the default {headers, body} in the transform pipeline */ module.exports = function transform({Key, config, isBinary, defaults}) { let filetype = Key.split('.').pop() let plugins = config.plugins? config.plugins[filetype] || [] : [] // early return if there's no processing to do if (plugins.length === 0 || isBinary) return defaults else { defaults.body = defaults.body.toString() // Convert non-binary files to strings for mutation // otherwise walk the supplied plugins return plugins.reduce(function run(response, plugin) { /* eslint global-require: 'off' */ let transformer = typeof plugin === 'function'? plugin: require(plugin) return transformer(Key, response, config) }, defaults) } } },{}],8:[function(require,module,exports){ let read = require('./read') let errors = require('../errors') /** * arc.http.proxy * * Primary interface for reading static assets out of S3 * * @param config - object, for configuration * @param config.alias - object, map of root rel URLs to map to fully qualified root rel URLs * @param config.assets - object, map of local, unfingerprinted filenames to fingerprinted filenames * @param config.bucket - object, {staging, production} override the s3 bucket names * @param config.bucket.staging - object, {staging, production} override the s3 bucket names * @param config.bucket.production - object, {staging, production} override the s3 bucket names * @param config.bucket.folder - string, bucket folder * @param config.cacheControl - string, set a custom Cache-Control max-age header value * @param config.plugins - object, configure proxy-plugin-* transforms per file extension * @param config.spa - boolean, forces index.html no matter the folder depth * * @returns HTTPLambda - an HTTP Lambda function that proxies calls to S3 */ function proxy (config={}) { return async function httpProxy (req) { let { ARC_STATIC_BUCKET, ARC_STATIC_SPA, NODE_ENV } = process.env let deprecated = req.version === undefined || req.version === '1.0' let isProduction = NODE_ENV === 'production' let path = deprecated ? req.path : req.rawPath let isFolder = path.split('/').pop().indexOf('.') === -1 let Key // Assigned below /** * Bucket config */ let configBucket = config.bucket let bucketSetting = isProduction ? configBucket && configBucket['production'] : configBucket && configBucket['staging'] // Ok, all that out of the way, let's set the actual bucket, eh? let Bucket = ARC_STATIC_BUCKET || bucketSetting if (!Bucket) { return errors.proxyConfig } /** * Configure SPA + set up the file to be requested */ let spa = ARC_STATIC_SPA === 'false' ? false : config && config.spa if (!spa) config.spa = false if (spa) { // If SPA: force index.html Key = isFolder ? 'index.html' : path.substring(1) } else { // Return index.html for root, otherwise pass the path let last = path.split('/').filter(Boolean).pop() let isFile = last ? last.includes('.') : false let isRoot = path === '/' Key = isRoot? 'index.html' : path.substring(1) // Always remove leading slash // Append default index.html to requests to folder paths if (isRoot === false && isFile === false) { Key = `${Key.replace(/\/$/, '')}/index.html` } } /** * Alias * Allows a Key to be manually overridden */ let aliasing = config && config.alias && config.alias.hasOwnProperty(path) if (aliasing) { Key = config.alias[path].substring(1) // Always remove leading slash } /** * REST API [very deprecated]: strip `staging/`, `production/` path parts * - Post Architect 5.5 (2019-02-03) which added /{proxy+} this would be an edge case * - e.g. you'd see this if someone put up a proxy in not-`get /` */ if (Key.startsWith('staging/')) Key = Key.replace('staging/', '') if (Key.startsWith('production/')) Key = Key.replace('production/', '') /** * REST API [deprecated]: flag `staging/`, `production/` requests */ let rootPath let reqPath = req.requestContext && req.requestContext.path if (deprecated && reqPath) { if (reqPath && reqPath.startsWith('/staging/')) rootPath = 'staging' if (reqPath && reqPath.startsWith('/production/')) rootPath = 'production' } // Normalize if-none-match header to lower case; it differs between environments let find = k => k.toLowerCase() === 'if-none-match' let IfNoneMatch = req.headers && req.headers[Object.keys(req.headers).find(find)] // Ensure response shape is correct for proxy SPA responses let isProxy = req.resource === '/{proxy+}' || !!req.rawPath return await read({ Key, Bucket, IfNoneMatch, isFolder, isProxy, config, rootPath }) } } module.exports = { proxy, // Default read // Read a specific file } },{"../errors":1,"./read":12}],9:[function(require,module,exports){ let { existsSync, readFileSync } = require('fs') let { extname, join, sep } = require('path') let mime = require('mime-types') let crypto = require('crypto') let binaryTypes = require('../../helpers/binary-types') let { httpError } = require('../../errors') let transform = require('../format/transform') // Soon to be deprecated let templatizeResponse = require('../format/templatize') let normalizeResponse = require('../format/response') let pretty = require('./_pretty') /** * arc.http.proxy.read * * Reads a file from the local filesystem, resolving an HTTP Lambda friendly payload * * @param {Object} params * @param {String} params.Key * @param {String} params.IfNoneMatch * @param {String} params.isFolder * @param {String} params.isProxy * @param {Object} params.config * @returns {Object} {statusCode, headers, body} */ module.exports = async function readLocal (params) { let { ARC_SANDBOX_PATH_TO_STATIC, ARC_STATIC_PREFIX, ARC_STATIC_FOLDER } = process.env let { Key, IfNoneMatch, isFolder, isProxy, config } = params let headers = {} let response = {} // Unlike S3, handle basePath and assets inside the function as Sandbox is long-lived let staticAssets // After 6.x we can rely on this env var in sandbox let basePath = ARC_SANDBOX_PATH_TO_STATIC || join(process.cwd(), '..', '..', '..', 'public') let staticManifest = join(basePath, 'static.json') if (existsSync(staticManifest)) { staticAssets = JSON.parse(readFileSync(staticManifest)) } let assets = config.assets || staticAssets // Look up the blob // Assume we're running from a lambda in src/**/* OR from vendored node_modules/@architect/sandbox let filePath = join(basePath, Key) // Denormalize static folder for local paths (not something we'd do in S3) let staticPrefix = ARC_STATIC_PREFIX || ARC_STATIC_FOLDER if (filePath.includes(staticPrefix)) { filePath = filePath.replace(`${staticPrefix}${sep}`, '') } try { // If client sends If-None-Match, use it in S3 getObject params let matchedETag = false // If the static asset manifest has the key, use that, otherwise fall back to the original Key let contentType = mime.contentType(extname(Key)) if (!existsSync(filePath)) { return await pretty({ Key: filePath, config, isFolder }) } let body = readFileSync(filePath) let ETag = crypto.createHash('sha256').update(body).digest('hex') let result = { ContentType: contentType, ETag } if (IfNoneMatch === ETag) { matchedETag = true headers.ETag = IfNoneMatch response = { statusCode: 304, headers } } // No ETag found, return the blob if (!matchedETag) { let isBinary = binaryTypes.some(type => result.ContentType.includes(type) || contentType.includes(type)) // Transform first to allow for any proxy plugin mutations response = transform({ Key, config, isBinary, defaults: { headers, body } }) // Handle templating response = templatizeResponse({ isBinary, assets, response, isLocal: true }) // Normalize response response = normalizeResponse({ response, result, Key, isProxy, config }) // Add ETag response.headers.ETag = result.ETag } if (!response.statusCode) { response.statusCode = 200 } return response } catch (err) { let notFound = err.name === 'NoSuchKey' if (notFound) { return await pretty({ Key: filePath, config, isFolder }) } else { let title = err.name let message = ` ${err.message}<br> <pre>${err.stack}</pre> ` return httpError({ statusCode: 500, title, message }) } } } },{"../../errors":1,"../../helpers/binary-types":2,"../format/response":5,"../format/templatize":6,"../format/transform":7,"./_pretty":10,"crypto":undefined,"fs":undefined,"mime-types":15,"path":undefined}],10:[function(require,module,exports){ let aws = require('aws-sdk') let { existsSync, readFileSync } = require('fs') let { join } = require('path') let { httpError } = require('../../errors') /** * Peek into a dir without a trailing slash to see if it's got an index.html file * If not, look for a custom 404.html * Finally, return the default 404 */ module.exports = async function pretty (params) { let { Bucket, Key, assets, headers, isFolder, prefix } = params let { ARC_LOCAL, ARC_SANDBOX_PATH_TO_STATIC, NODE_ENV } = process.env let local = NODE_ENV === 'testing' || ARC_LOCAL let s3 = new aws.S3 function getKey (Key) { let lookup = Key.replace(prefix + '/', '') if (assets && assets[lookup]) { Key = assets[lookup] Key = prefix ? `${prefix}/${Key}` : Key } return Key } async function getLocal (file) { let basepath = ARC_SANDBOX_PATH_TO_STATIC if (!file.startsWith(basepath)) { file = join(basepath, file) } if (!existsSync(file)) { let err = ReferenceError(`NoSuchKey: ${Key} not found`) err.name = 'NoSuchKey' throw err } else return { Body: readFileSync(file) } } async function getS3 (Key) { return await s3.getObject({ Bucket, Key }).promise() } async function get (file) { let getter = local ? getLocal : getS3 try { return await getter(file) } catch (err) { if (err.name === 'NoSuchKey') { err.statusCode = 404 return err } else { err.statusCode = 500 return err } } } /** * Enable pretty urls * Peek into a dir without trailing slash to see if it contains index.html */ if (isFolder && !Key.endsWith('/')) { let peek = getKey(`${Key}/index.html`) let result = await get(peek) if (result.Body) { let body = result.Body.toString() return { headers, statusCode: 200, body } } } /** * Enable custom 404s * Check to see if user defined a custom 404 page */ let notFound = getKey(`404.html`) let result = await get(notFound) if (result.Body) { let body = result.Body.toString() return { headers: { 'Content-Type': 'text/html; charset=utf8;', 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0' }, statusCode: 404, body } } else { let err = result let { statusCode } = err let title = err.name let message = ` ${err.message } <pre><b>${ Key }</b></pre><br> <pre>${err.stack}</pre> ` return httpError({ statusCode, title, message }) } } },{"../../errors":1,"aws-sdk":"aws-sdk","fs":undefined,"path":undefined}],11:[function(require,module,exports){ let { existsSync, readFileSync } = require('fs') let { extname, join } = require('path') let mime = require('mime-types') let aws = require('aws-sdk') let binaryTypes = require('../../helpers/binary-types') let { httpError } = require('../../errors') let transform = require('../format/transform') // Soon to be deprecated let templatizeResponse = require('../format/templatize') let normalizeResponse = require('../format/response') let pretty = require('./_pretty') let { decompress } = require('../format/compress') /** * arc.http.proxy.read * * Reads a file from S3, resolving an HTTP Lambda friendly payload * * @param {Object} params * @param {String} params.Key * @param {String} params.Bucket * @param {String} params.IfNoneMatch * @param {String} params.isFolder * @param {String} params.isProxy * @param {Object} params.config * @returns {Object} {statusCode, headers, body} */ module.exports = async function readS3 (params) { let { Bucket, Key, IfNoneMatch, isFolder, isProxy, config, rootPath } = params let { ARC_STATIC_PREFIX, ARC_STATIC_FOLDER } = process.env let prefix = ARC_STATIC_PREFIX || ARC_STATIC_FOLDER || config.bucket && config.bucket.folder let assets = config.assets || staticAssets let headers = {} let response = {} try { // If client sends If-None-Match, use it in S3 getObject params let matchedETag = false let s3 = new aws.S3 // Try to interpolate HTML/JSON requests to fingerprinted filenames let contentType = mime.contentType(extname(Key)) let capture = [ 'text/html', 'application/json' ] let isCaptured = capture.some(type => contentType.includes(type)) if (assets && assets[Key] && isCaptured) { // Not necessary to flag response formatter for anti-caching // Those headers are already set in S3 file metadata Key = assets[Key] } /** * Check for possible fingerprint upgrades and forward valid requests */ if (assets && assets[Key] && !isCaptured) { let location = rootPath ? `/${rootPath}/_static/${assets[Key]}` : `/_static/${assets[Key]}` return { statusCode: 302, headers: { location, 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0' } } } /** * Folder prefix * Enables a bucket folder at root to be specified */ if (prefix) { Key = `${prefix}/${Key}` } let options = { Bucket, Key } if (IfNoneMatch) { options.IfNoneMatch = IfNoneMatch } let result = await s3.getObject(options).promise().catch(err => { // ETag matches (getObject error code of NotModified), so don't transit the whole file if (err.code === 'NotModified') { matchedETag = true headers.ETag = IfNoneMatch response = { statusCode: 304, headers } } else { // Important: do not swallow this error otherwise! throw err } }) // No ETag found, return the blob if (!matchedETag) { let contentEncoding = result.ContentEncoding if (contentEncoding) { result.Body = decompress(contentEncoding, result.Body) } let isBinary = binaryTypes.some(type => result.ContentType.includes(type) || contentType.includes(type)) // Transform first to allow for any proxy plugin mutations response = transform({ Key, config, isBinary, defaults: { headers, body: result.Body } }) // Handle templating response = templatizeResponse({ isBinary, assets, response }) // Normalize response response = normalizeResponse({ response, result, Key, isProxy, contentEncoding, config }) } if (!response.statusCode) { response.statusCode = 200 } return response } catch (err) { let notFound = err.name === 'NoSuchKey' if (notFound) { return await pretty({ Bucket, Key, assets, headers, isFolder, prefix }) } else { let title = err.name let message = ` ${err.message}<br> <pre>${err.stack}</pre> ` return httpError({ statusCode: 500, title, message }) } } } /** * Fingerprinting manifest * Load the manifest, try to hit the disk as infrequently as possible across invocations */ let staticAssets let staticManifest = join(process.cwd(), 'node_modules', '@architect', 'shared', 'static.json') if (staticAssets === false) { null /*noop*/ } else if (existsSync(staticManifest) && !staticAssets) { staticAssets = JSON.parse(readFileSync(staticManifest)) } else { staticAssets = false } },{"../../errors":1,"../../helpers/binary-types":2,"../format/compress":4,"../format/response":5,"../format/templatize":6,"../format/transform":7,"./_pretty":10,"aws-sdk":"aws-sdk","fs":undefined,"mime-types":15,"path":undefined}],12:[function(require,module,exports){ let readLocal = require('./_local') let readS3 = require('./_s3') function read () { let { ARC_LOCAL, NODE_ENV } = process.env let local = NODE_ENV === 'testing' || ARC_LOCAL return local ? readLocal : readS3 } module.exports = read() },{"./_local":9,"./_s3":11}],13:[function(require,module,exports){ module.exports={ "application/1d-interleaved-parityfec": { "source": "iana" }, "application/3gpdash-qoe-report+xml": { "source": "iana", "charset": "UTF-8", "compressible": true }, "application/3gpp-ims+xml": { "source": "iana", "compressible": true }, "application/a2l": { "source": "iana" }, "application/activemessage": { "source": "iana" }, "application/activity+json": { "source": "iana", "compressible": true }, "application/alto-costmap+json": { "source": "iana", "compressible": true }, "application/alto-costmapfilter+json": { "source": "iana", "compressible": true }, "application/alto-directory+json": { "source": "iana", "compressible": true }, "application/alto-endpointcost+json": { "source": "iana", "compressible": true }, "application/alto-endpointcostparams+json": { "source": "iana", "compressible": true }, "application/alto-endpointprop+json": { "source": "iana", "compressible": true }, "application/alto-endpointpropparams+json": { "source": "iana", "compressible": true }, "application/alto-error+json": { "source": "iana", "compressible": true }, "application/alto-networkmap+json": { "source": "iana", "compressible": true }, "application/alto-networkmapfilter+json": { "source": "iana", "compressible": true }, "application/alto-updatestreamcontrol+json": { "source": "iana", "compressible": true }, "application/alto-updatestreamparams+json": { "source": "iana", "compressible": true }, "application/aml": { "source": "iana" }, "application/andrew-inset": { "source": "iana", "extensions": ["ez"] }, "application/applefile": { "source": "iana" }, "application/applixware": { "source": "apache", "extensions": ["aw"] }, "application/atf": { "source": "iana" }, "application/atfx": { "source": "iana" }, "application/atom+xml": { "source": "iana", "compressible": true, "extensions": ["atom"] }, "application/atomcat+xml": { "source": "iana", "compressible": true, "extensions": ["atomcat"] }, "application/atomdeleted+xml": { "source": "iana", "compressible": true, "extensions": ["atomdeleted"] }, "application/atomicmail": { "source": "iana" }, "application/atomsvc+xml": { "source": "iana", "compressible": true, "extensions": ["atomsvc"] }, "application/atsc-dwd+xml": { "source": "iana", "compressible": true, "extensions": ["dwd"] }, "application/atsc-dynamic-event-message": { "source": "iana" }, "application/atsc-held+xml": { "source": "iana", "compressible": true, "extensions": ["held"] }, "application/atsc-rdt+json": { "source": "iana", "compressible": true }, "application/atsc-rsat+xml": { "source": "iana", "compressible": true, "extensions": ["rsat"] }, "application/atxml": { "source": "iana" }, "application/auth-policy+xml": { "source": "iana", "compressible": true }, "application/bacnet-xdd+zip": { "source": "iana", "compressible": false }, "application/batch-smtp": { "source": "iana" }, "application/bdoc": { "compressible": false, "extensions": ["bdoc"] }, "application/beep+xml": { "source": "iana", "charset": "UTF-8", "compressible": true }, "application/calendar+json": { "source": "iana", "compressible": true }, "application/calendar+xml": { "source": "iana", "compressible": true, "extensions": ["xcs"] }, "application/call-completion": { "source": "iana" }, "application/cals-1840": { "source": "iana" }, "application/cap+xml": { "source": "iana", "charset": "UTF-8", "compressible": true }, "application/cbor": { "source": "iana" }, "application/cbor-seq": { "source": "iana" }, "application/cccex": { "source": "iana" }, "application/ccmp+xml": { "source": "iana", "compressible": true }, "application/ccxml+xml": { "source": "iana", "compressible": true, "extensions": ["ccxml"] }, "application/cdfx+xml": { "source": "iana", "compressible": true, "extensions": ["cdfx"] }, "application/cdmi-capability": { "source": "iana", "extensions": ["cdmia"] }, "application/cdmi-container": { "source": "iana", "extensions": ["cdmic"] }, "application/cdmi-domain": { "source": "iana", "extensions": ["cdmid"] }, "application/cdmi-object": { "source": "iana", "extensions": ["cdmio"] }, "application/cdmi-queue": { "source": "iana", "extensions": ["cdmiq"] }, "application/cdni": { "source": "iana" }, "application/cea": { "source": "iana" }, "application/cea-2018+xml": { "source": "iana", "compressible": true }, "application/cellml+xml": { "source": "iana", "compressible": true }, "application/cfw": { "source": "iana" }, "application/clue+xml": { "source": "iana", "compressible": true }, "application/clue_info+xml": { "source": "iana", "compressible": true }, "application/cms": { "source": "iana" }, "application/cnrp+xml": { "source": "iana", "compressible": true }, "application/coap-group+json": { "source": "iana", "compressible": true }, "application/coap-payload": { "source": "iana" }, "application/commonground": { "source": "iana" }, "application/conference-info+xml": { "source": "iana", "compressible": true }, "application/cose": { "source": "iana" }, "application/cose-key": { "source": "iana" }, "application/cose-key-set": { "source": "iana" }, "application/cpl+xml": { "source": "iana", "compressible": true }, "application/csrattrs": { "source": "iana" }, "application/csta+xml": { "source": "iana", "compressible": true }, "application/cstadata+xml": { "source": "iana", "compressible": true }, "application/csvm+json": { "source": "iana", "compressible": true }, "application/cu-seeme": { "source": "apache", "extensions": ["cu"] }, "application/cwt": { "source": "iana" }, "application/cybercash": { "source": "iana" }, "application/dart": { "compressible": true }, "application/dash+xml": { "source": "iana", "compressible": true, "extensions": ["mpd"] }, "application/dashdelta": { "source": "iana" }, "application/davmount+xml": { "source": "iana", "compressible": true, "extensions": ["davmount"] }, "application/dca-rft": { "source": "iana" }, "application/dcd": { "source": "iana" }, "application/dec-dx": { "source": "iana" }, "application/dialog-info+xml": { "source": "iana", "compressible": true }, "application/dicom": { "source": "iana" }, "application/dicom+json": { "source": "iana", "compressible": true }, "application/dicom+xml": { "source": "iana", "compressible": true }, "application/dii": { "source": "iana" }, "application/dit": { "source": "iana" }, "application/dns": { "source": "iana" }, "application/dns+json": { "source": "iana", "compressible": true }, "application/dns-message": { "source": "iana" }, "application/docbook+xml": { "source": "apache", "compressible": true, "extensions": ["dbk"] }, "application/dots+cbor": { "source": "iana" }, "application/dskpp+xml": { "source": "iana", "compressible": true }, "application/dssc+der": { "source": "iana", "extensions": ["dssc"] }, "application/dssc+xml": { "source": "iana", "compressible": true, "extensions": ["xdssc"] }, "application/dvcs": { "source": "iana" }, "application/ecmascript": { "source": "iana", "compressible": true, "extensions": ["ecma","es"] }, "application/edi-consent": { "source": "iana" }, "application/edi-x12": { "source": "iana", "compressible": false }, "application/edifact": { "source": "iana", "compressible": false }, "application/efi": { "source": "iana" }, "application/emergencycalldata.comment+xml": { "source": "iana", "compressible": true }, "application/emergencycalldata.control+xml": { "source": "iana", "compressible": true }, "application/emergencycalldata.deviceinfo+xml": { "source": "iana", "compressible": true }, "application/emergencycalldata.ecall.msd": { "source": "iana" }, "application/emergencycalldata.providerinfo+xml": { "source": "iana", "compressible": true }, "application/emergencycalldata.serviceinfo+xml": { "source": "iana", "compressible": true }, "application/emergencycalldata.subscriberinfo+xml": { "source": "iana", "compressible": true }, "application/emergencycalldata.veds+xml": { "source": "iana", "compressible": true }, "application/emma+xml": { "source": "iana", "compressible": true, "extensions": ["emma"] }, "application/emotionml+xml": { "source": "iana", "compressible": true, "extensions": ["emotionml"] }, "application/encaprtp": { "source": "iana" }, "application/epp+xml": { "source": "iana", "compressible": true }, "application/epub+zip": { "source": "iana", "compressible": false, "extensions": ["epub"] }, "application/eshop": { "source": "iana" }, "application/exi": { "source": "iana", "extensions": ["exi"] }, "application/expect-ct-report+json": { "source": "iana", "compressible": true }, "application/fastinfoset": { "source": "iana" }, "application/fastsoap": { "source": "iana" }, "application/fdt+xml": { "source": "iana", "compressible": true, "extensions": ["fdt"] }, "application/fhir+json": { "source": "iana", "charset": "UTF-8", "compressible": true }, "application/fhir+xml": { "source": "iana", "charset": "UTF-8", "compressible": true }, "application/fido.trusted-apps+json": { "compressible": true }, "application/fits": { "source": "iana" }, "application/flexfec": { "source": "iana" }, "application/font-sfnt": { "source": "iana" }, "application/font-tdpfr": { "source": "iana", "extensions": ["pfr"] }, "application/font-woff": { "source": "iana", "compressible": false }, "application/framework-attributes+xml": { "source": "iana", "compressible": true }, "application/geo+json": { "source": "iana", "compressible": true, "extensions": ["geojson"] }, "application/geo+json-seq": { "source": "iana" }, "application/geopackage+sqlite3": { "source": "iana" }, "application/geoxacml+xml": { "source": "iana", "compressible": true }, "application/gltf-buffer": { "source": "iana" }, "application/gml+xml": { "source": "iana", "compressible": true, "extensions": ["gml"] }, "application/gpx+xml": { "source": "apache", "compressible": true, "extensions": ["gpx"] }, "application/gxf": { "source": "apache", "extensions": ["gxf"] }, "application/gzip": { "source": "iana", "compressible": false, "extensions": ["gz"] }, "application/h224": { "source": "iana" }, "application/held+xml": { "source": "iana", "compressible": true }, "application/hjson": { "extensions": ["hjson"] }, "application/http": { "source": "iana" }, "application/hyperstudio": { "source": "iana", "extensions": ["stk"] }, "application/ibe-key-request+xml": { "source": "iana", "compressible": true }, "application/ibe-pkg-reply+xml": { "source": "iana", "compressible": true }, "application/ibe-pp-data": { "source": "iana" }, "application/iges": { "source": "iana" }, "application/im-iscomposing+xml": { "source": "iana", "charset": "UTF-8", "compressible": true }, "application/index": { "source": "iana" }, "application/index.cmd": { "source": "iana" }, "application/index.obj": { "source": "iana" }, "application/index.response": { "source": "iana" }, "application/index.vnd": { "source": "iana" }, "application/inkml+xml": { "source": "iana", "compressible": true, "extensions": ["ink","inkml"] }, "application/iotp": { "source": "iana" }, "application/ipfix": { "source": "iana", "extensions": ["ipfix"] }, "application/ipp": { "source": "iana" }, "application/isup": { "source": "iana" }, "application/its+xml": { "source": "iana", "compressible": true, "extensions": ["its"] }, "application/java-archive": { "source": "apache", "compressible": false, "extensions": ["jar","war","ear"] }, "application/java-serialized-object": { "source": "apache", "compressible": false, "extensions": ["ser"] }, "application/java-vm": { "source": "apache", "compressible": false, "extensions": ["class"] }, "application/javascript": { "source": "iana", "charset": "UTF-8", "compressible": true, "extensions": ["js","mjs"] }, "application/jf2feed+json": { "source": "iana", "compressible": true }, "application/jose": { "source": "iana" }, "application/jose+json": { "source": "iana", "compressible": true }, "application/jrd+json": { "source": "iana", "compressible": true }, "application/json": { "source": "iana", "charset": "UTF-8", "compressible": true, "extensions": ["json","map"] }, "application/json-patch+json": { "source": "iana", "compressible": true }, "application/json-seq": { "source": "iana" }, "application/json5": { "extensions": ["json5"] }, "application/jsonml+json": { "source": "apache", "compressible": true, "extensions": ["jsonml"] }, "application/jwk+json": { "source": "iana", "compressible": true }, "application/jwk-set+json": { "source": "iana", "compressible": true }, "application/jwt": { "source": "iana" }, "application/kpml-request+xml": { "source": "iana", "compressible": true }, "application/kpml-response+xml": { "source": "iana", "compressible": true }, "application/ld+json": { "source": "iana", "compressible": true, "extensions": ["jsonld"] }, "application/lgr+xml": { "source": "iana", "compressible": true, "extensions": ["lgr"] }, "application/link-format": { "source": "iana" }, "application/load-control+xml": { "source": "iana", "compressible": true }, "application/lost+xml": { "source": "iana", "compressible": true, "extensions": ["lostxml"] }, "application/lostsync+xml": { "source": "iana", "compressible": true }, "application/lpf+zip": { "source": "iana", "compressible": false }, "application/lxf": { "source": "iana" }, "application/mac-binhex40": { "source": "iana", "extensions": ["hqx"] }, "application/mac-compactpro": { "source": "apache", "extensions": ["cpt"] }, "application/macwriteii": { "source": "iana" }, "application/mads+xml": { "source": "iana", "compressible": true, "extensions": ["mads"] }, "application/manifest+json": { "charset": "UTF-8", "compressible": true, "extensions": ["webmanifest"] }, "application/marc": { "source": "iana", "extensions": ["mrc"] }, "application/marcxml+xml": { "source": "iana", "compressible": true, "extensions": ["mrcx"] }, "application/mathematica": { "source": "iana", "extensions": ["ma","nb","mb"] }, "application/mathml+xml": { "source": "iana", "compressible": true, "extensions": ["mathml"] }, "application/mathml-content+xml": { "source": "iana", "compressible": true }, "application/mathml-presentation+xml": { "source": "iana", "compressible": true }, "application/mbms-associated-procedure-description+xml": { "source": "iana", "compressible": true }, "application/mbms-deregister+xml": { "source": "iana", "compressible": true }, "application/mbms-envelope+xml": { "source": "iana", "compressible": true }, "application/mbms-msk+xml": { "source": "iana", "compressible": true }, "application/mbms-msk-response+xml": { "source": "iana", "compressible": true }, "application/mbms-protection-description+xml": { "source": "iana", "compressible": true }, "application/mbms-reception-report+xml": { "source": "iana", "compressible": true }, "application/mbms-register+xml": { "source": "iana", "compressible": true }, "application/mbms-register-response+xml": { "source": "iana", "compressible": true }, "application/mbms-schedule+xml": { "source": "iana", "compressible": true }, "application/mbms-user-service-description+xml": { "source": "iana", "compressible": true }, "application/mbox": { "source": "iana", "extensions": ["mbox"] }, "application/media-policy-dataset+xml": { "source": "iana", "compressible": true }, "application/media_control+xml": { "source": "iana", "compressible": true }, "application/mediaservercontrol+xml": { "source": "iana", "compressible": true, "extensions": ["mscml"] }, "application/merge-patch+json": { "source": "iana", "compressible": true }, "application/metalink+xml": { "source": "apache", "compressible": true, "extensions": ["metalink"] }, "application/metalink4+xml": { "source": "iana", "compressible": true, "extensions": ["meta4"] }, "application/mets+xml": { "source": "iana", "compressible": true, "extensions": ["mets"] }, "application/mf4": { "source": "iana" }, "application/mikey": { "source": "iana" }, "application/mipc": { "source": "iana" }, "application/mmt-aei+xml": { "source": "iana", "compressible": true, "extensions": ["maei"] }, "application/mmt-usd+xml": { "source": "iana", "compressible": true, "extensions": ["musd"] }, "application/mods+xml": { "source": "iana", "compressible": true, "extensions": ["mods"] }, "application/moss-keys": { "source": "iana" }, "application/moss-signature": { "source": "iana" }, "application/mosskey-data": { "source": "iana" }, "application/mosskey-request": { "source": "iana" }, "application/mp21": { "source": "iana", "extensions": ["m21","mp21"] }, "application/mp4": { "source": "iana", "extensions": ["mp4s","m4p"] }, "application/mpeg4-generic": { "source": "iana" }, "application/mpeg4-iod": { "source": "iana" }, "application/mpeg4-iod-xmt": { "source": "iana" }, "application/mrb-consumer+xml": { "source": "iana", "compressible": true, "extensions": ["xdf"] }, "application/mrb-publish+xml": { "source": "iana", "compressible": true, "extensions": ["xdf"] }, "application/msc-ivr+xml": { "source": "iana", "charset": "UTF-8", "compressible": true }, "application/msc-mixer+xml": { "source": "iana", "charset": "UTF-8", "compressible": true }, "application/msword": { "source": "iana", "compressible": false, "extensions": ["doc","dot"] }, "application/mud+json": { "source": "iana", "compressible": true }, "application/multipart-core": { "source": "iana" }, "application/mxf": { "source": "iana", "extensions": ["mxf"] }, "application/n-quads": { "source": "iana", "extensions": ["nq"] }, "application/n-triples": { "source": "iana", "extensions": ["nt"] }, "application/nasdata": { "source": "iana" }, "application/news-checkgroups": { "source": "iana", "charset": "US-ASCII" }, "application/news-groupinfo": { "source": "iana", "charset": "US-ASCII" }, "application/news-transmission": { "source": "iana" }, "application/nlsml+xml": { "source": "iana", "compressible": true }, "application/node": { "source": "iana", "extensions": ["cjs"] }, "application/nss": { "source": "iana" }, "application/ocsp-request": { "source": "iana" }, "application/ocsp-response": { "source": "iana" }, "application/octet-stream": { "source": "iana", "compressible": false, "extensions": ["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"] }, "application/oda": { "source": "iana", "extensions": ["oda"] }, "application/odm+xml": { "source": "iana", "compressible": true }, "application/odx": { "source": "iana" }, "application/oebps-package+xml": { "source": "iana", "compressible": true, "extensions": ["opf"] }, "application/ogg": { "source": "iana", "compressible": false, "extensions": ["ogx"] }, "application/omdoc+xml": { "source": "apache", "compressible": true, "extensions": ["omdoc"] }, "application/onenote": { "source": "apache", "extensions": ["onetoc","onetoc2","onetmp","onepkg"] }, "application/oscore": { "source": "iana" }, "application/oxps": { "source": "iana", "extensions": ["oxps"] }, "application/p2p-overlay+xml": { "source": "iana", "compressible": true, "extensions": ["relo"] }, "application/parityfec": { "source": "iana" }, "application/passport": { "source": "iana" }, "application/patch-ops-error+xml": { "source": "iana", "compressible": true, "extensions": ["xer"] }, "application/pdf": { "source": "iana", "compressible": false, "extensions": ["pdf"] }, "application/pdx": { "source": "iana" }, "application/pem-certificate-chain": { "source": "iana" }, "application/pgp-encrypted": { "source": "iana", "compressible": false, "extensions": ["pgp"] }, "application/pgp-keys": { "source": "iana" }, "application/pgp-signature": { "source": "iana", "extensions": ["asc","s