| | 'use strict'; |
| |
|
| | const Assert = require('@hapi/hoek/lib/assert'); |
| | const EscapeRegex = require('@hapi/hoek/lib/escapeRegex'); |
| |
|
| |
|
| | const internals = {}; |
| |
|
| |
|
| | internals.generate = function () { |
| |
|
| | const rfc3986 = {}; |
| |
|
| | const hexDigit = '\\dA-Fa-f'; |
| | const hexDigitOnly = '[' + hexDigit + ']'; |
| |
|
| | const unreserved = '\\w-\\.~'; |
| | const subDelims = '!\\$&\'\\(\\)\\*\\+,;='; |
| | const pctEncoded = '%' + hexDigit; |
| | const pchar = unreserved + pctEncoded + subDelims + ':@'; |
| | const pcharOnly = '[' + pchar + ']'; |
| | const decOctect = '(?:0{0,2}\\d|0?[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])'; |
| |
|
| | rfc3986.ipv4address = '(?:' + decOctect + '\\.){3}' + decOctect; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | const h16 = hexDigitOnly + '{1,4}'; |
| | const ls32 = '(?:' + h16 + ':' + h16 + '|' + rfc3986.ipv4address + ')'; |
| | const IPv6SixHex = '(?:' + h16 + ':){6}' + ls32; |
| | const IPv6FiveHex = '::(?:' + h16 + ':){5}' + ls32; |
| | const IPv6FourHex = '(?:' + h16 + ')?::(?:' + h16 + ':){4}' + ls32; |
| | const IPv6ThreeHex = '(?:(?:' + h16 + ':){0,1}' + h16 + ')?::(?:' + h16 + ':){3}' + ls32; |
| | const IPv6TwoHex = '(?:(?:' + h16 + ':){0,2}' + h16 + ')?::(?:' + h16 + ':){2}' + ls32; |
| | const IPv6OneHex = '(?:(?:' + h16 + ':){0,3}' + h16 + ')?::' + h16 + ':' + ls32; |
| | const IPv6NoneHex = '(?:(?:' + h16 + ':){0,4}' + h16 + ')?::' + ls32; |
| | const IPv6NoneHex2 = '(?:(?:' + h16 + ':){0,5}' + h16 + ')?::' + h16; |
| | const IPv6NoneHex3 = '(?:(?:' + h16 + ':){0,6}' + h16 + ')?::'; |
| |
|
| | rfc3986.ipv4Cidr = '(?:\\d|[1-2]\\d|3[0-2])'; |
| | rfc3986.ipv6Cidr = '(?:0{0,2}\\d|0?[1-9]\\d|1[01]\\d|12[0-8])'; |
| | rfc3986.ipv6address = '(?:' + IPv6SixHex + '|' + IPv6FiveHex + '|' + IPv6FourHex + '|' + IPv6ThreeHex + '|' + IPv6TwoHex + '|' + IPv6OneHex + '|' + IPv6NoneHex + '|' + IPv6NoneHex2 + '|' + IPv6NoneHex3 + ')'; |
| | rfc3986.ipvFuture = 'v' + hexDigitOnly + '+\\.[' + unreserved + subDelims + ':]+'; |
| |
|
| | rfc3986.scheme = '[a-zA-Z][a-zA-Z\\d+-\\.]*'; |
| | rfc3986.schemeRegex = new RegExp(rfc3986.scheme); |
| |
|
| | const userinfo = '[' + unreserved + pctEncoded + subDelims + ':]*'; |
| | const IPLiteral = '\\[(?:' + rfc3986.ipv6address + '|' + rfc3986.ipvFuture + ')\\]'; |
| | const regName = '[' + unreserved + pctEncoded + subDelims + ']{1,255}'; |
| | const host = '(?:' + IPLiteral + '|' + rfc3986.ipv4address + '|' + regName + ')'; |
| | const port = '\\d*'; |
| | const authority = '(?:' + userinfo + '@)?' + host + '(?::' + port + ')?'; |
| | const authorityCapture = '(?:' + userinfo + '@)?(' + host + ')(?::' + port + ')?'; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | const segment = pcharOnly + '*'; |
| | const segmentNz = pcharOnly + '+'; |
| | const segmentNzNc = '[' + unreserved + pctEncoded + subDelims + '@' + ']+'; |
| | const pathEmpty = ''; |
| | const pathAbEmpty = '(?:\\/' + segment + ')*'; |
| | const pathAbsolute = '\\/(?:' + segmentNz + pathAbEmpty + ')?'; |
| | const pathRootless = segmentNz + pathAbEmpty; |
| | const pathNoScheme = segmentNzNc + pathAbEmpty; |
| | const pathAbNoAuthority = '(?:\\/\\/\\/' + segment + pathAbEmpty + ')'; |
| |
|
| | |
| |
|
| | rfc3986.hierPart = '(?:' + '(?:\\/\\/' + authority + pathAbEmpty + ')' + '|' + pathAbsolute + '|' + pathRootless + '|' + pathAbNoAuthority + ')'; |
| | rfc3986.hierPartCapture = '(?:' + '(?:\\/\\/' + authorityCapture + pathAbEmpty + ')' + '|' + pathAbsolute + '|' + pathRootless + ')'; |
| |
|
| | |
| |
|
| | rfc3986.relativeRef = '(?:' + '(?:\\/\\/' + authority + pathAbEmpty + ')' + '|' + pathAbsolute + '|' + pathNoScheme + '|' + pathEmpty + ')'; |
| | rfc3986.relativeRefCapture = '(?:' + '(?:\\/\\/' + authorityCapture + pathAbEmpty + ')' + '|' + pathAbsolute + '|' + pathNoScheme + '|' + pathEmpty + ')'; |
| |
|
| | |
| | |
| |
|
| | rfc3986.query = '[' + pchar + '\\/\\?]*(?=#|$)'; |
| | rfc3986.queryWithSquareBrackets = '[' + pchar + '\\[\\]\\/\\?]*(?=#|$)'; |
| |
|
| | |
| |
|
| | rfc3986.fragment = '[' + pchar + '\\/\\?]*'; |
| |
|
| | return rfc3986; |
| | }; |
| |
|
| | internals.rfc3986 = internals.generate(); |
| |
|
| |
|
| | exports.ip = { |
| | v4Cidr: internals.rfc3986.ipv4Cidr, |
| | v6Cidr: internals.rfc3986.ipv6Cidr, |
| | ipv4: internals.rfc3986.ipv4address, |
| | ipv6: internals.rfc3986.ipv6address, |
| | ipvfuture: internals.rfc3986.ipvFuture |
| | }; |
| |
|
| |
|
| | internals.createRegex = function (options) { |
| |
|
| | const rfc = internals.rfc3986; |
| |
|
| | |
| |
|
| | const query = options.allowQuerySquareBrackets ? rfc.queryWithSquareBrackets : rfc.query; |
| | const suffix = '(?:\\?' + query + ')?' + '(?:#' + rfc.fragment + ')?'; |
| |
|
| | |
| |
|
| | const relative = options.domain ? rfc.relativeRefCapture : rfc.relativeRef; |
| |
|
| | if (options.relativeOnly) { |
| | return internals.wrap(relative + suffix); |
| | } |
| |
|
| | |
| |
|
| | let customScheme = ''; |
| | if (options.scheme) { |
| | Assert(options.scheme instanceof RegExp || typeof options.scheme === 'string' || Array.isArray(options.scheme), 'scheme must be a RegExp, String, or Array'); |
| |
|
| | const schemes = [].concat(options.scheme); |
| | Assert(schemes.length >= 1, 'scheme must have at least 1 scheme specified'); |
| |
|
| | |
| |
|
| | const selections = []; |
| | for (let i = 0; i < schemes.length; ++i) { |
| | const scheme = schemes[i]; |
| | Assert(scheme instanceof RegExp || typeof scheme === 'string', 'scheme at position ' + i + ' must be a RegExp or String'); |
| |
|
| | if (scheme instanceof RegExp) { |
| | selections.push(scheme.source.toString()); |
| | } |
| | else { |
| | Assert(rfc.schemeRegex.test(scheme), 'scheme at position ' + i + ' must be a valid scheme'); |
| | selections.push(EscapeRegex(scheme)); |
| | } |
| | } |
| |
|
| | customScheme = selections.join('|'); |
| | } |
| |
|
| | |
| |
|
| | const scheme = customScheme ? '(?:' + customScheme + ')' : rfc.scheme; |
| | const absolute = '(?:' + scheme + ':' + (options.domain ? rfc.hierPartCapture : rfc.hierPart) + ')'; |
| | const prefix = options.allowRelative ? '(?:' + absolute + '|' + relative + ')' : absolute; |
| | return internals.wrap(prefix + suffix, customScheme); |
| | }; |
| |
|
| |
|
| | internals.wrap = function (raw, scheme) { |
| |
|
| | raw = `(?=.)(?!https?\:/(?:$|[^/]))(?!https?\:///)(?!https?\:[^/])${raw}`; |
| |
|
| | return { |
| | raw, |
| | regex: new RegExp(`^${raw}$`), |
| | scheme |
| | }; |
| | }; |
| |
|
| |
|
| | internals.uriRegex = internals.createRegex({}); |
| |
|
| |
|
| | exports.regex = function (options = {}) { |
| |
|
| | if (options.scheme || |
| | options.allowRelative || |
| | options.relativeOnly || |
| | options.allowQuerySquareBrackets || |
| | options.domain) { |
| |
|
| | return internals.createRegex(options); |
| | } |
| |
|
| | return internals.uriRegex; |
| | }; |
| |
|