You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					293 lines
				
				9.1 KiB
			
		
		
			
		
	
	
					293 lines
				
				9.1 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const URL = require('url') | ||
|  | const { normalizeIPv6, normalizeIPv4, removeDotSegments, recomposeAuthority, normalizeComponentEncoding } = require('./lib/utils') | ||
|  | const SCHEMES = require('./lib/schemes') | ||
|  | 
 | ||
|  | function normalize (uri, options) { | ||
|  |   if (typeof uri === 'string') { | ||
|  |     uri = serialize(parse(uri, options), options) | ||
|  |   } else if (typeof uri === 'object') { | ||
|  |     uri = parse(serialize(uri, options), options) | ||
|  |   } | ||
|  |   return uri | ||
|  | } | ||
|  | 
 | ||
|  | function resolve (baseURI, relativeURI, options) { | ||
|  |   const schemelessOptions = Object.assign({ scheme: 'null' }, options) | ||
|  |   const resolved = resolveComponents(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true) | ||
|  |   return serialize(resolved, { ...schemelessOptions, skipEscape: true }) | ||
|  | } | ||
|  | 
 | ||
|  | function resolveComponents (base, relative, options, skipNormalization) { | ||
|  |   const target = {} | ||
|  |   if (!skipNormalization) { | ||
|  |     base = parse(serialize(base, options), options) // normalize base components
 | ||
|  |     relative = parse(serialize(relative, options), options) // normalize relative components
 | ||
|  |   } | ||
|  |   options = options || {} | ||
|  | 
 | ||
|  |   if (!options.tolerant && relative.scheme) { | ||
|  |     target.scheme = relative.scheme | ||
|  |     // target.authority = relative.authority;
 | ||
|  |     target.userinfo = relative.userinfo | ||
|  |     target.host = relative.host | ||
|  |     target.port = relative.port | ||
|  |     target.path = removeDotSegments(relative.path || '') | ||
|  |     target.query = relative.query | ||
|  |   } else { | ||
|  |     if (relative.userinfo !== undefined || relative.host !== undefined || relative.port !== undefined) { | ||
|  |       // target.authority = relative.authority;
 | ||
|  |       target.userinfo = relative.userinfo | ||
|  |       target.host = relative.host | ||
|  |       target.port = relative.port | ||
|  |       target.path = removeDotSegments(relative.path || '') | ||
|  |       target.query = relative.query | ||
|  |     } else { | ||
|  |       if (!relative.path) { | ||
|  |         target.path = base.path | ||
|  |         if (relative.query !== undefined) { | ||
|  |           target.query = relative.query | ||
|  |         } else { | ||
|  |           target.query = base.query | ||
|  |         } | ||
|  |       } else { | ||
|  |         if (relative.path.charAt(0) === '/') { | ||
|  |           target.path = removeDotSegments(relative.path) | ||
|  |         } else { | ||
|  |           if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) { | ||
|  |             target.path = '/' + relative.path | ||
|  |           } else if (!base.path) { | ||
|  |             target.path = relative.path | ||
|  |           } else { | ||
|  |             target.path = base.path.slice(0, base.path.lastIndexOf('/') + 1) + relative.path | ||
|  |           } | ||
|  |           target.path = removeDotSegments(target.path) | ||
|  |         } | ||
|  |         target.query = relative.query | ||
|  |       } | ||
|  |       // target.authority = base.authority;
 | ||
|  |       target.userinfo = base.userinfo | ||
|  |       target.host = base.host | ||
|  |       target.port = base.port | ||
|  |     } | ||
|  |     target.scheme = base.scheme | ||
|  |   } | ||
|  | 
 | ||
|  |   target.fragment = relative.fragment | ||
|  | 
 | ||
|  |   return target | ||
|  | } | ||
|  | 
 | ||
|  | function equal (uriA, uriB, options) { | ||
|  |   if (typeof uriA === 'string') { | ||
|  |     uriA = unescape(uriA) | ||
|  |     uriA = serialize(normalizeComponentEncoding(parse(uriA, options), true), { ...options, skipEscape: true }) | ||
|  |   } else if (typeof uriA === 'object') { | ||
|  |     uriA = serialize(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true }) | ||
|  |   } | ||
|  | 
 | ||
|  |   if (typeof uriB === 'string') { | ||
|  |     uriB = unescape(uriB) | ||
|  |     uriB = serialize(normalizeComponentEncoding(parse(uriB, options), true), { ...options, skipEscape: true }) | ||
|  |   } else if (typeof uriB === 'object') { | ||
|  |     uriB = serialize(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true }) | ||
|  |   } | ||
|  | 
 | ||
|  |   return uriA.toLowerCase() === uriB.toLowerCase() | ||
|  | } | ||
|  | 
 | ||
|  | function serialize (cmpts, opts) { | ||
|  |   const components = { | ||
|  |     host: cmpts.host, | ||
|  |     scheme: cmpts.scheme, | ||
|  |     userinfo: cmpts.userinfo, | ||
|  |     port: cmpts.port, | ||
|  |     path: cmpts.path, | ||
|  |     query: cmpts.query, | ||
|  |     nid: cmpts.nid, | ||
|  |     nss: cmpts.nss, | ||
|  |     uuid: cmpts.uuid, | ||
|  |     fragment: cmpts.fragment, | ||
|  |     reference: cmpts.reference, | ||
|  |     resourceName: cmpts.resourceName, | ||
|  |     secure: cmpts.secure, | ||
|  |     error: '' | ||
|  |   } | ||
|  |   const options = Object.assign({}, opts) | ||
|  |   const uriTokens = [] | ||
|  | 
 | ||
|  |   // find scheme handler
 | ||
|  |   const schemeHandler = SCHEMES[(options.scheme || components.scheme || '').toLowerCase()] | ||
|  | 
 | ||
|  |   // perform scheme specific serialization
 | ||
|  |   if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(components, options) | ||
|  | 
 | ||
|  |   if (components.path !== undefined) { | ||
|  |     if (!options.skipEscape) { | ||
|  |       components.path = escape(components.path) | ||
|  | 
 | ||
|  |       if (components.scheme !== undefined) { | ||
|  |         components.path = components.path.split('%3A').join(':') | ||
|  |       } | ||
|  |     } else { | ||
|  |       components.path = unescape(components.path) | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (options.reference !== 'suffix' && components.scheme) { | ||
|  |     uriTokens.push(components.scheme) | ||
|  |     uriTokens.push(':') | ||
|  |   } | ||
|  | 
 | ||
|  |   const authority = recomposeAuthority(components, options) | ||
|  |   if (authority !== undefined) { | ||
|  |     if (options.reference !== 'suffix') { | ||
|  |       uriTokens.push('//') | ||
|  |     } | ||
|  | 
 | ||
|  |     uriTokens.push(authority) | ||
|  | 
 | ||
|  |     if (components.path && components.path.charAt(0) !== '/') { | ||
|  |       uriTokens.push('/') | ||
|  |     } | ||
|  |   } | ||
|  |   if (components.path !== undefined) { | ||
|  |     let s = components.path | ||
|  | 
 | ||
|  |     if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) { | ||
|  |       s = removeDotSegments(s) | ||
|  |     } | ||
|  | 
 | ||
|  |     if (authority === undefined) { | ||
|  |       s = s.replace(/^\/\//, '/%2F') // don't allow the path to start with "//"
 | ||
|  |     } | ||
|  | 
 | ||
|  |     uriTokens.push(s) | ||
|  |   } | ||
|  | 
 | ||
|  |   if (components.query !== undefined) { | ||
|  |     uriTokens.push('?') | ||
|  |     uriTokens.push(components.query) | ||
|  |   } | ||
|  | 
 | ||
|  |   if (components.fragment !== undefined) { | ||
|  |     uriTokens.push('#') | ||
|  |     uriTokens.push(components.fragment) | ||
|  |   } | ||
|  |   return uriTokens.join('') | ||
|  | } | ||
|  | 
 | ||
|  | const URI_PARSE = /^(?:([^:/?#]+):)?(?:\/\/((?:([^/?#@]*)@)?(\[[^/?#\]]+\]|[^/?#:]*)(?::(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n|\r)*))?/i | ||
|  | 
 | ||
|  | function parse (uri, opts) { | ||
|  |   const options = Object.assign({}, opts) | ||
|  |   const parsed = { | ||
|  |     scheme: undefined, | ||
|  |     userinfo: undefined, | ||
|  |     host: '', | ||
|  |     port: undefined, | ||
|  |     path: '', | ||
|  |     query: undefined, | ||
|  |     fragment: undefined | ||
|  |   } | ||
|  |   const gotEncoding = uri.indexOf('%') !== -1 | ||
|  |   if (options.reference === 'suffix') uri = (options.scheme ? options.scheme + ':' : '') + '//' + uri | ||
|  | 
 | ||
|  |   const matches = uri.match(URI_PARSE) | ||
|  | 
 | ||
|  |   if (matches) { | ||
|  |     // store each component
 | ||
|  |     parsed.scheme = matches[1] | ||
|  |     parsed.userinfo = matches[3] | ||
|  |     parsed.host = matches[4] | ||
|  |     parsed.port = parseInt(matches[5], 10) | ||
|  |     parsed.path = matches[6] || '' | ||
|  |     parsed.query = matches[7] | ||
|  |     parsed.fragment = matches[8] | ||
|  | 
 | ||
|  |     // fix port number
 | ||
|  |     if (isNaN(parsed.port)) { | ||
|  |       parsed.port = matches[5] | ||
|  |     } | ||
|  |     if (parsed.host) { | ||
|  |       const ipv4result = normalizeIPv4(parsed.host) | ||
|  |       if (ipv4result.isIPV4 === false) { | ||
|  |         parsed.host = normalizeIPv6(ipv4result.host, { isIPV4: false }).host.toLowerCase() | ||
|  |       } else { | ||
|  |         parsed.host = ipv4result.host | ||
|  |       } | ||
|  |     } | ||
|  |     if (parsed.scheme === undefined && parsed.userinfo === undefined && parsed.host === undefined && parsed.port === undefined && !parsed.path && parsed.query === undefined) { | ||
|  |       parsed.reference = 'same-document' | ||
|  |     } else if (parsed.scheme === undefined) { | ||
|  |       parsed.reference = 'relative' | ||
|  |     } else if (parsed.fragment === undefined) { | ||
|  |       parsed.reference = 'absolute' | ||
|  |     } else { | ||
|  |       parsed.reference = 'uri' | ||
|  |     } | ||
|  | 
 | ||
|  |     // check for reference errors
 | ||
|  |     if (options.reference && options.reference !== 'suffix' && options.reference !== parsed.reference) { | ||
|  |       parsed.error = parsed.error || 'URI is not a ' + options.reference + ' reference.' | ||
|  |     } | ||
|  | 
 | ||
|  |     // find scheme handler
 | ||
|  |     const schemeHandler = SCHEMES[(options.scheme || parsed.scheme || '').toLowerCase()] | ||
|  | 
 | ||
|  |     // check if scheme can't handle IRIs
 | ||
|  |     if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) { | ||
|  |       // if host component is a domain name
 | ||
|  |       if (parsed.host && (options.domainHost || (schemeHandler && schemeHandler.domainHost))) { | ||
|  |         // convert Unicode IDN -> ASCII IDN
 | ||
|  |         try { | ||
|  |           parsed.host = URL.domainToASCII(parsed.host.toLowerCase()) | ||
|  |         } catch (e) { | ||
|  |           parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e | ||
|  |         } | ||
|  |       } | ||
|  |       // convert IRI -> URI
 | ||
|  |     } | ||
|  | 
 | ||
|  |     if (!schemeHandler || (schemeHandler && !schemeHandler.skipNormalize)) { | ||
|  |       if (gotEncoding && parsed.scheme !== undefined) { | ||
|  |         parsed.scheme = unescape(parsed.scheme) | ||
|  |       } | ||
|  |       if (gotEncoding && parsed.userinfo !== undefined) { | ||
|  |         parsed.userinfo = unescape(parsed.userinfo) | ||
|  |       } | ||
|  |       if (gotEncoding && parsed.host !== undefined) { | ||
|  |         parsed.host = unescape(parsed.host) | ||
|  |       } | ||
|  |       if (parsed.path !== undefined && parsed.path.length) { | ||
|  |         parsed.path = encodeURI(parsed.path) | ||
|  |       } | ||
|  |       if (parsed.fragment !== undefined && parsed.fragment.length) { | ||
|  |         parsed.fragment = encodeURI(decodeURI(parsed.fragment)) | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     // perform scheme specific parsing
 | ||
|  |     if (schemeHandler && schemeHandler.parse) { | ||
|  |       schemeHandler.parse(parsed, options) | ||
|  |     } | ||
|  |   } else { | ||
|  |     parsed.error = parsed.error || 'URI can not be parsed.' | ||
|  |   } | ||
|  |   return parsed | ||
|  | } | ||
|  | 
 | ||
|  | const fastUri = { | ||
|  |   normalize, | ||
|  |   resolve, | ||
|  |   resolveComponents, | ||
|  |   equal, | ||
|  |   serialize, | ||
|  |   parse | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = fastUri | ||
|  | module.exports.default = fastUri | ||
|  | module.exports.fastUri = fastUri |