| '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 |