summaryrefslogtreecommitdiff
path: root/node_modules/mdurl/lib/parse.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/mdurl/lib/parse.mjs')
-rw-r--r--node_modules/mdurl/lib/parse.mjs308
1 files changed, 308 insertions, 0 deletions
diff --git a/node_modules/mdurl/lib/parse.mjs b/node_modules/mdurl/lib/parse.mjs
new file mode 100644
index 0000000..fac5065
--- /dev/null
+++ b/node_modules/mdurl/lib/parse.mjs
@@ -0,0 +1,308 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+//
+// Changes from joyent/node:
+//
+// 1. No leading slash in paths,
+// e.g. in `url.parse('http://foo?bar')` pathname is ``, not `/`
+//
+// 2. Backslashes are not replaced with slashes,
+// so `http:\\example.org\` is treated like a relative path
+//
+// 3. Trailing colon is treated like a part of the path,
+// i.e. in `http://example.org:foo` pathname is `:foo`
+//
+// 4. Nothing is URL-encoded in the resulting object,
+// (in joyent/node some chars in auth and paths are encoded)
+//
+// 5. `url.parse()` does not have `parseQueryString` argument
+//
+// 6. Removed extraneous result properties: `host`, `path`, `query`, etc.,
+// which can be constructed using other parts of the url.
+//
+
+function Url () {
+ this.protocol = null
+ this.slashes = null
+ this.auth = null
+ this.port = null
+ this.hostname = null
+ this.hash = null
+ this.search = null
+ this.pathname = null
+}
+
+// Reference: RFC 3986, RFC 1808, RFC 2396
+
+// define these here so at least they only have to be
+// compiled once on the first module load.
+const protocolPattern = /^([a-z0-9.+-]+:)/i
+const portPattern = /:[0-9]*$/
+
+// Special case for a simple path URL
+/* eslint-disable-next-line no-useless-escape */
+const simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/
+
+// RFC 2396: characters reserved for delimiting URLs.
+// We actually just auto-escape these.
+const delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t']
+
+// RFC 2396: characters not allowed for various reasons.
+const unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims)
+
+// Allowed by RFCs, but cause of XSS attacks. Always escape these.
+const autoEscape = ['\''].concat(unwise)
+// Characters that are never ever allowed in a hostname.
+// Note that any invalid chars are also handled, but these
+// are the ones that are *expected* to be seen, so we fast-path
+// them.
+const nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape)
+const hostEndingChars = ['/', '?', '#']
+const hostnameMaxLen = 255
+const hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/
+const hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/
+// protocols that can allow "unsafe" and "unwise" chars.
+// protocols that never have a hostname.
+const hostlessProtocol = {
+ javascript: true,
+ 'javascript:': true
+}
+// protocols that always contain a // bit.
+const slashedProtocol = {
+ http: true,
+ https: true,
+ ftp: true,
+ gopher: true,
+ file: true,
+ 'http:': true,
+ 'https:': true,
+ 'ftp:': true,
+ 'gopher:': true,
+ 'file:': true
+}
+
+function urlParse (url, slashesDenoteHost) {
+ if (url && url instanceof Url) return url
+
+ const u = new Url()
+ u.parse(url, slashesDenoteHost)
+ return u
+}
+
+Url.prototype.parse = function (url, slashesDenoteHost) {
+ let lowerProto, hec, slashes
+ let rest = url
+
+ // trim before proceeding.
+ // This is to support parse stuff like " http://foo.com \n"
+ rest = rest.trim()
+
+ if (!slashesDenoteHost && url.split('#').length === 1) {
+ // Try fast path regexp
+ const simplePath = simplePathPattern.exec(rest)
+ if (simplePath) {
+ this.pathname = simplePath[1]
+ if (simplePath[2]) {
+ this.search = simplePath[2]
+ }
+ return this
+ }
+ }
+
+ let proto = protocolPattern.exec(rest)
+ if (proto) {
+ proto = proto[0]
+ lowerProto = proto.toLowerCase()
+ this.protocol = proto
+ rest = rest.substr(proto.length)
+ }
+
+ // figure out if it's got a host
+ // user@server is *always* interpreted as a hostname, and url
+ // resolution will treat //foo/bar as host=foo,path=bar because that's
+ // how the browser resolves relative URLs.
+ /* eslint-disable-next-line no-useless-escape */
+ if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
+ slashes = rest.substr(0, 2) === '//'
+ if (slashes && !(proto && hostlessProtocol[proto])) {
+ rest = rest.substr(2)
+ this.slashes = true
+ }
+ }
+
+ if (!hostlessProtocol[proto] &&
+ (slashes || (proto && !slashedProtocol[proto]))) {
+ // there's a hostname.
+ // the first instance of /, ?, ;, or # ends the host.
+ //
+ // If there is an @ in the hostname, then non-host chars *are* allowed
+ // to the left of the last @ sign, unless some host-ending character
+ // comes *before* the @-sign.
+ // URLs are obnoxious.
+ //
+ // ex:
+ // http://a@b@c/ => user:a@b host:c
+ // http://a@b?@c => user:a host:c path:/?@c
+
+ // v0.12 TODO(isaacs): This is not quite how Chrome does things.
+ // Review our test case against browsers more comprehensively.
+
+ // find the first instance of any hostEndingChars
+ let hostEnd = -1
+ for (let i = 0; i < hostEndingChars.length; i++) {
+ hec = rest.indexOf(hostEndingChars[i])
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) {
+ hostEnd = hec
+ }
+ }
+
+ // at this point, either we have an explicit point where the
+ // auth portion cannot go past, or the last @ char is the decider.
+ let auth, atSign
+ if (hostEnd === -1) {
+ // atSign can be anywhere.
+ atSign = rest.lastIndexOf('@')
+ } else {
+ // atSign must be in auth portion.
+ // http://a@b/c@d => host:b auth:a path:/c@d
+ atSign = rest.lastIndexOf('@', hostEnd)
+ }
+
+ // Now we have a portion which is definitely the auth.
+ // Pull that off.
+ if (atSign !== -1) {
+ auth = rest.slice(0, atSign)
+ rest = rest.slice(atSign + 1)
+ this.auth = auth
+ }
+
+ // the host is the remaining to the left of the first non-host char
+ hostEnd = -1
+ for (let i = 0; i < nonHostChars.length; i++) {
+ hec = rest.indexOf(nonHostChars[i])
+ if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) {
+ hostEnd = hec
+ }
+ }
+ // if we still have not hit it, then the entire thing is a host.
+ if (hostEnd === -1) {
+ hostEnd = rest.length
+ }
+
+ if (rest[hostEnd - 1] === ':') { hostEnd-- }
+ const host = rest.slice(0, hostEnd)
+ rest = rest.slice(hostEnd)
+
+ // pull out port.
+ this.parseHost(host)
+
+ // we've indicated that there is a hostname,
+ // so even if it's empty, it has to be present.
+ this.hostname = this.hostname || ''
+
+ // if hostname begins with [ and ends with ]
+ // assume that it's an IPv6 address.
+ const ipv6Hostname = this.hostname[0] === '[' &&
+ this.hostname[this.hostname.length - 1] === ']'
+
+ // validate a little.
+ if (!ipv6Hostname) {
+ const hostparts = this.hostname.split(/\./)
+ for (let i = 0, l = hostparts.length; i < l; i++) {
+ const part = hostparts[i]
+ if (!part) { continue }
+ if (!part.match(hostnamePartPattern)) {
+ let newpart = ''
+ for (let j = 0, k = part.length; j < k; j++) {
+ if (part.charCodeAt(j) > 127) {
+ // we replace non-ASCII char with a temporary placeholder
+ // we need this to make sure size of hostname is not
+ // broken by replacing non-ASCII by nothing
+ newpart += 'x'
+ } else {
+ newpart += part[j]
+ }
+ }
+ // we test again with ASCII char only
+ if (!newpart.match(hostnamePartPattern)) {
+ const validParts = hostparts.slice(0, i)
+ const notHost = hostparts.slice(i + 1)
+ const bit = part.match(hostnamePartStart)
+ if (bit) {
+ validParts.push(bit[1])
+ notHost.unshift(bit[2])
+ }
+ if (notHost.length) {
+ rest = notHost.join('.') + rest
+ }
+ this.hostname = validParts.join('.')
+ break
+ }
+ }
+ }
+ }
+
+ if (this.hostname.length > hostnameMaxLen) {
+ this.hostname = ''
+ }
+
+ // strip [ and ] from the hostname
+ // the host field still retains them, though
+ if (ipv6Hostname) {
+ this.hostname = this.hostname.substr(1, this.hostname.length - 2)
+ }
+ }
+
+ // chop off from the tail first.
+ const hash = rest.indexOf('#')
+ if (hash !== -1) {
+ // got a fragment string.
+ this.hash = rest.substr(hash)
+ rest = rest.slice(0, hash)
+ }
+ const qm = rest.indexOf('?')
+ if (qm !== -1) {
+ this.search = rest.substr(qm)
+ rest = rest.slice(0, qm)
+ }
+ if (rest) { this.pathname = rest }
+ if (slashedProtocol[lowerProto] &&
+ this.hostname && !this.pathname) {
+ this.pathname = ''
+ }
+
+ return this
+}
+
+Url.prototype.parseHost = function (host) {
+ let port = portPattern.exec(host)
+ if (port) {
+ port = port[0]
+ if (port !== ':') {
+ this.port = port.substr(1)
+ }
+ host = host.substr(0, host.length - port.length)
+ }
+ if (host) { this.hostname = host }
+}
+
+export default urlParse