mirror of
https://github.com/datarhei/core.git
synced 2025-09-26 20:11:29 +08:00
137 lines
2.8 KiB
Go
137 lines
2.8 KiB
Go
package url
|
|
|
|
import (
|
|
"net"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
type URL struct {
|
|
Scheme string
|
|
Opaque string // encoded opaque data
|
|
User *url.Userinfo // username and password information
|
|
Host string // host or host:port
|
|
RawPath string // path (relative paths may omit leading slash)
|
|
RawQuery string // encoded query values, without '?'
|
|
RawFragment string // fragment for references, without '#'
|
|
}
|
|
|
|
func (u *URL) Hostname() string {
|
|
if !strings.Contains(u.Host, ":") {
|
|
return u.Host
|
|
}
|
|
|
|
hostname, _, _ := net.SplitHostPort(u.Host)
|
|
|
|
return hostname
|
|
}
|
|
|
|
func (u *URL) Port() string {
|
|
if !strings.Contains(u.Host, ":") {
|
|
return ""
|
|
}
|
|
|
|
_, port, _ := net.SplitHostPort(u.Host)
|
|
|
|
return port
|
|
}
|
|
|
|
var reScheme = regexp.MustCompile(`(?i)^([a-z][a-z0-9.+-:]*):/{1,3}`)
|
|
|
|
// Validate checks whether the given address is a valid URL, based on the
|
|
// relaxed version of Parse in this package.
|
|
func Validate(address string) error {
|
|
_, err := Parse(address)
|
|
|
|
return err
|
|
}
|
|
|
|
// Parse parses an URL into its components. It is a more relaxed version of
|
|
// url.Parse as it's not checking the escaping of the path, query, and fragment.
|
|
func Parse(address string) (*URL, error) {
|
|
address, frag, _ := strings.Cut(address, "#")
|
|
|
|
u := &URL{
|
|
RawFragment: frag,
|
|
}
|
|
|
|
matches := reScheme.FindStringSubmatch(address)
|
|
if matches != nil {
|
|
u.Scheme = matches[1]
|
|
address = strings.Replace(address, u.Scheme+":", "", 1)
|
|
}
|
|
|
|
address, query, _ := strings.Cut(address, "?")
|
|
u.RawQuery = query
|
|
|
|
if strings.HasPrefix(address, "///") {
|
|
u.RawPath = strings.TrimPrefix(address, "//")
|
|
return u, nil
|
|
}
|
|
|
|
if strings.HasPrefix(address, "//") {
|
|
host, path, _ := strings.Cut(address[2:], "/")
|
|
u.RawPath = "/" + path
|
|
|
|
parsedHost, err := url.Parse("//" + host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
u.User = parsedHost.User
|
|
u.Host = parsedHost.Host
|
|
|
|
return u, nil
|
|
}
|
|
|
|
if strings.HasPrefix(address, "/") {
|
|
u.RawPath = address
|
|
|
|
return u, nil
|
|
}
|
|
|
|
scheme, address, _ := strings.Cut(address, ":")
|
|
|
|
u.Scheme = scheme
|
|
u.Opaque = address
|
|
|
|
return u, nil
|
|
}
|
|
|
|
// HasScheme returns whether the address has an URL scheme prefix
|
|
func HasScheme(address string) bool {
|
|
if ok := reScheme.MatchString(address); ok {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Lookup returns the first resolved IP address of the host in the address.
|
|
// If the address doesn't have an URL scheme prefix, an empty string and no
|
|
// error is returned. If the address is an URL and the lookup fails, an
|
|
// error is returned.
|
|
func Lookup(address string) (string, error) {
|
|
if !HasScheme(address) {
|
|
return "", nil
|
|
}
|
|
|
|
u, err := Parse(address)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
host := u.Hostname()
|
|
if len(host) == 0 {
|
|
return "", nil
|
|
}
|
|
|
|
addrs, err := net.LookupHost(host)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return addrs[0], nil
|
|
}
|