'use strict' const UUID_REG = /^[\da-f]{8}\b-[\da-f]{4}\b-[\da-f]{4}\b-[\da-f]{4}\b-[\da-f]{12}$/iu const URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu function isSecure (wsComponents) { return typeof wsComponents.secure === 'boolean' ? wsComponents.secure : String(wsComponents.scheme).toLowerCase() === 'wss' } function httpParse (components) { if (!components.host) { components.error = components.error || 'HTTP URIs must have a host.' } return components } function httpSerialize (components) { const secure = String(components.scheme).toLowerCase() === 'https' // normalize the default port if (components.port === (secure ? 443 : 80) || components.port === '') { components.port = undefined } // normalize the empty path if (!components.path) { components.path = '/' } // NOTE: We do not parse query strings for HTTP URIs // as WWW Form Url Encoded query strings are part of the HTML4+ spec, // and not the HTTP spec. return components } function wsParse (wsComponents) { // indicate if the secure flag is set wsComponents.secure = isSecure(wsComponents) // construct resouce name wsComponents.resourceName = (wsComponents.path || '/') + (wsComponents.query ? '?' + wsComponents.query : '') wsComponents.path = undefined wsComponents.query = undefined return wsComponents } function wsSerialize (wsComponents) { // normalize the default port if (wsComponents.port === (isSecure(wsComponents) ? 443 : 80) || wsComponents.port === '') { wsComponents.port = undefined } // ensure scheme matches secure flag if (typeof wsComponents.secure === 'boolean') { wsComponents.scheme = (wsComponents.secure ? 'wss' : 'ws') wsComponents.secure = undefined } // reconstruct path from resource name if (wsComponents.resourceName) { const [path, query] = wsComponents.resourceName.split('?') wsComponents.path = (path && path !== '/' ? path : undefined) wsComponents.query = query wsComponents.resourceName = undefined } // forbid fragment component wsComponents.fragment = undefined return wsComponents } function urnParse (urnComponents, options) { if (!urnComponents.path) { urnComponents.error = 'URN can not be parsed' return urnComponents } const matches = urnComponents.path.match(URN_REG) if (matches) { const scheme = options.scheme || urnComponents.scheme || 'urn' urnComponents.nid = matches[1].toLowerCase() urnComponents.nss = matches[2] const urnScheme = `${scheme}:${options.nid || urnComponents.nid}` const schemeHandler = SCHEMES[urnScheme] urnComponents.path = undefined if (schemeHandler) { urnComponents = schemeHandler.parse(urnComponents, options) } } else { urnComponents.error = urnComponents.error || 'URN can not be parsed.' } return urnComponents } function urnSerialize (urnComponents, options) { const scheme = options.scheme || urnComponents.scheme || 'urn' const nid = urnComponents.nid.toLowerCase() const urnScheme = `${scheme}:${options.nid || nid}` const schemeHandler = SCHEMES[urnScheme] if (schemeHandler) { urnComponents = schemeHandler.serialize(urnComponents, options) } const uriComponents = urnComponents const nss = urnComponents.nss uriComponents.path = `${nid || options.nid}:${nss}` options.skipEscape = true return uriComponents } function urnuuidParse (urnComponents, options) { const uuidComponents = urnComponents uuidComponents.uuid = uuidComponents.nss uuidComponents.nss = undefined if (!options.tolerant && (!uuidComponents.uuid || !UUID_REG.test(uuidComponents.uuid))) { uuidComponents.error = uuidComponents.error || 'UUID is not valid.' } return uuidComponents } function urnuuidSerialize (uuidComponents) { const urnComponents = uuidComponents // normalize UUID urnComponents.nss = (uuidComponents.uuid || '').toLowerCase() return urnComponents } const http = { scheme: 'http', domainHost: true, parse: httpParse, serialize: httpSerialize } const https = { scheme: 'https', domainHost: http.domainHost, parse: httpParse, serialize: httpSerialize } const ws = { scheme: 'ws', domainHost: true, parse: wsParse, serialize: wsSerialize } const wss = { scheme: 'wss', domainHost: ws.domainHost, parse: ws.parse, serialize: ws.serialize } const urn = { scheme: 'urn', parse: urnParse, serialize: urnSerialize, skipNormalize: true } const urnuuid = { scheme: 'urn:uuid', parse: urnuuidParse, serialize: urnuuidSerialize, skipNormalize: true } const SCHEMES = { http, https, ws, wss, urn, 'urn:uuid': urnuuid } module.exports = SCHEMES