github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/urlx/parse.go (about) 1 package urlx 2 3 import ( 4 "net/url" 5 "regexp" 6 "strings" 7 8 "github.com/ory/x/logrusx" 9 ) 10 11 // winPathRegex is a regex for [DRIVE-LETTER]: 12 var winPathRegex = regexp.MustCompile("^[A-Za-z]:.*") 13 14 // Parse parses rawURL into a URL structure with special handling for file:// URLs 15 // 16 // File URLs with relative paths (file://../file, ../file) will be returned as a 17 // url.URL object without the Scheme set to "file". This is because the file 18 // scheme does not support relative paths. Make sure to check for 19 // both "file" or "" (an empty string) in URL.Scheme if you are looking for 20 // a file path. 21 // 22 // Use the companion function GetURLFilePath() to get a file path suitable 23 // for the current operating system. 24 func Parse(rawURL string) (*url.URL, error) { 25 lcRawURL := strings.ToLower(rawURL) 26 if strings.HasPrefix(lcRawURL, "file:///") { 27 return url.Parse(rawURL) 28 } 29 30 // Normally the first part after file:// is a hostname, but since 31 // this is often misused we interpret the URL like a normal path 32 // by removing the "file://" from the beginning (if it exists) 33 rawURL = trimPrefixIC(rawURL, "file://") 34 35 if winPathRegex.MatchString(rawURL) { 36 // Windows path 37 return url.Parse("file:///" + rawURL) 38 } 39 40 if strings.HasPrefix(lcRawURL, "\\\\") { 41 // Windows UNC path 42 // We extract the hostname and create an appropriate file:// URL 43 // based on the hostname and the path 44 host, path := extractUNCPathParts(rawURL) 45 // It is safe to replace the \ with / here because this is POSIX style path 46 return url.Parse("file://" + host + strings.ReplaceAll(path, "\\", "/")) 47 } 48 49 return url.Parse(rawURL) 50 } 51 52 // ParseOrPanic parses a url or panics. 53 func ParseOrPanic(in string) *url.URL { 54 out, err := url.Parse(in) 55 if err != nil { 56 panic(err.Error()) 57 } 58 return out 59 } 60 61 // ParseOrFatal parses a url or fatals. 62 func ParseOrFatal(l *logrusx.Logger, in string) *url.URL { 63 out, err := url.Parse(in) 64 if err != nil { 65 l.WithError(err).Fatalf("Unable to parse url: %s", in) 66 } 67 return out 68 } 69 70 // ParseRequestURIOrPanic parses a request uri or panics. 71 func ParseRequestURIOrPanic(in string) *url.URL { 72 out, err := url.ParseRequestURI(in) 73 if err != nil { 74 panic(err.Error()) 75 } 76 return out 77 } 78 79 // ParseRequestURIOrFatal parses a request uri or fatals. 80 func ParseRequestURIOrFatal(l *logrusx.Logger, in string) *url.URL { 81 out, err := url.ParseRequestURI(in) 82 if err != nil { 83 l.WithError(err).Fatalf("Unable to parse url: %s", in) 84 } 85 return out 86 } 87 88 func extractUNCPathParts(uncPath string) (host, path string) { 89 parts := strings.Split(strings.TrimPrefix(uncPath, "\\\\"), "\\") 90 host = parts[0] 91 if len(parts) > 0 { 92 path = "\\" + strings.Join(parts[1:], "\\") 93 } 94 return host, path 95 } 96 97 // trimPrefixIC returns s without the provided leading prefix string using 98 // case insensitive matching. 99 // If s doesn't start with prefix, s is returned unchanged. 100 func trimPrefixIC(s, prefix string) string { 101 if strings.HasPrefix(strings.ToLower(s), prefix) { 102 return s[len(prefix):] 103 } 104 return s 105 }