github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/utils/earl/url.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package earl 16 17 import ( 18 "net/url" 19 "regexp" 20 "strconv" 21 "strings" 22 23 "github.com/dolthub/dolt/go/libraries/utils/osutil" 24 ) 25 26 var validHostRegex = regexp.MustCompile("^[-.a-zA-z0-9]*$") 27 var validHostWithPortRegex = regexp.MustCompile("^[-.a-zA-z0-9]*:[0-9]*$") 28 29 func isValidHost(hostAndPortStr string) bool { 30 hostStr := hostAndPortStr 31 portStr := "" 32 33 if idx := strings.IndexRune(hostAndPortStr, ':'); idx != -1 { 34 hostStr = hostAndPortStr[:idx] 35 portStr = strings.TrimSpace(hostAndPortStr[idx+1:]) 36 } 37 38 if len(portStr) > 0 { 39 if _, err := strconv.ParseUint(portStr, 10, 16); err != nil { 40 return false 41 } 42 } 43 44 if hostStr == "" { 45 return false 46 } else if hostStr == "localhost" { 47 return true 48 } else if strings.Index(hostStr, ".") == -1 { 49 return false 50 } 51 52 return validHostRegex.MatchString(hostStr) || validHostWithPortRegex.MatchString(hostStr) 53 } 54 55 func Parse(urlStr string) (*url.URL, error) { 56 u, err := parse(urlStr) 57 58 if err != nil { 59 return nil, err 60 } 61 62 // url.parse doesn't handle file paths that begin with . correctly 63 if u.Scheme == "file" && strings.HasPrefix(u.Host, ".") { 64 u.Path = u.Host + u.Path 65 u.Host = "" 66 } 67 68 // if Path is e.g. "/C$/" for a network location, it should instead be "C:/" 69 if len(u.Path) >= 3 && u.Path[0] == '/' && u.Path[1] >= 'A' && u.Path[1] <= 'Z' && u.Path[2] == '$' { 70 u.Path = u.Path[1:2] + ":" + u.Path[3:] 71 } else if !osutil.StartsWithWindowsVolume(u.Path) { // normalize some 72 if len(u.Path) == 0 || (u.Path[0] != '/' && u.Path[0] != '.') { 73 u.Path = "/" + u.Path 74 } 75 } 76 u.Path = strings.ReplaceAll(u.Path, `\`, "/") 77 78 return u, nil 79 } 80 81 func parse(urlStr string) (*url.URL, error) { 82 if strIdx := strings.Index(urlStr, ":///"); strIdx != -1 && osutil.StartsWithWindowsVolume(urlStr[strIdx+4:]) { 83 return &url.URL{ 84 Scheme: urlStr[:strIdx], 85 Path: urlStr[strIdx+4:], 86 }, nil 87 } 88 if strings.Index(urlStr, "://") == -1 { 89 u, err := url.Parse("http://" + urlStr) 90 91 if err == nil && isValidHost(u.Host) { 92 u.Scheme = "" 93 return u, nil 94 } else if err != nil { 95 return nil, err 96 } 97 } 98 99 return url.Parse(urlStr) 100 } 101 102 // FileUrlFromPath returns a url for the given path with the "file" scheme i.e. file://... 103 func FileUrlFromPath(path string, separator rune) string { 104 if osutil.StartsWithWindowsVolume(path) { 105 path = "/" + path 106 } 107 108 if separator != '/' { 109 path = strings.ReplaceAll(path, string(separator), "/") 110 } 111 112 u := &url.URL{Scheme: "file", Path: path} 113 urlStr := u.String() 114 return urlStr 115 }