├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── cmd └── domainparser │ └── domainparser.go ├── domainutil ├── tlds.go ├── tlds_generator.go ├── util.go └── util_test.go ├── go.mod └── go.sum /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [push] 3 | jobs: 4 | 5 | test: 6 | name: Generate & Test 7 | runs-on: ubuntu-latest 8 | steps: 9 | 10 | - name: Set up Go 11 | uses: actions/setup-go@v1 12 | with: 13 | go-version: 1.13 14 | id: go 15 | 16 | - name: Check out code into the Go module directory 17 | uses: actions/checkout@v1 18 | 19 | - name: Get dependencies 20 | run: go mod download 21 | 22 | - name: Generate 23 | run: go generate github.com/bobesa/go-domain-util/domainutil 24 | 25 | - name: Test 26 | run: go test -v ./domainutil 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | vendor/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Vlastimil Šenfeld 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-domain-tools 2 | [![Build Status](https://travis-ci.org/bobesa/go-domain-util.svg?branch=master)](https://travis-ci.org/bobesa/go-domain-util) 3 | 4 | GOlang package for checking if url contains subdomain, what that subdomain is, what is a top level domain in url etc. 5 | 6 | # Installation 7 | 8 | ```bash 9 | go get github.com/bobesa/go-domain-util/domainutil 10 | ``` 11 | 12 | # Rebuild the TLD database from publicsuffix.org 13 | 14 | ``` 15 | # (Re)build Parser 16 | go build -o $GOPATH/bin/domainparser github.com/bobesa/go-domain-util/cmd/domainparser 17 | 18 | # Go to domainutil pkg & generate tlds 19 | go generate github.com/bobesa/go-domain-util/domainutil 20 | ``` 21 | 22 | # Example code 23 | 24 | ```go 25 | package main 26 | 27 | import ( 28 | "fmt" 29 | ) 30 | 31 | import "github.com/bobesa/go-domain-util/domainutil" 32 | 33 | func main(){ 34 | fmt.Println(domainutil.Domain("keep.google.com")) 35 | } 36 | ``` 37 | 38 | # Functions 39 | 40 | ## Get the top level domain from url 41 | ```go 42 | func Domain(url string) string 43 | ``` 44 | Domain returns top level domain from url string. If no domain is found in provided url, this function returns empty string. If no TLD is found in provided url, this function returns empty string. 45 | 46 | ## Get the domain suffix from url 47 | ```go 48 | func DomainSuffix(url string) string 49 | ``` 50 | DomainSuffix returns domain suffix from provided url. If no TLD is found in provided url, this function returns empty string. 51 | 52 | ## Check if url has subdomain 53 | ```go 54 | func HasSubdomain(domain string) bool 55 | ``` 56 | HasSubdomain reports whether domain contains any subdomain. 57 | 58 | ## Get subdomain from url 59 | ```go 60 | func Subdomain(url string) string 61 | ``` 62 | Subdomain returns subdomain from provided url. If subdomain is not found in provided url, this function returns empty string. 63 | 64 | ## Get protocol from url 65 | ```go 66 | func Protocol(url string) string 67 | ``` 68 | Protocol returns protocol from provided url. If protocol is not found in provided url, this function returns empty string. 69 | 70 | ## Get username from credentials of url 71 | ```go 72 | func Username(url string) string 73 | ``` 74 | Username returns username from provided url. If username is not found in provided url, this function returns empty string. 75 | 76 | ## Get password from credentials of url 77 | ```go 78 | func Password(url string) string 79 | ``` 80 | Password returns password from provided url. If password is not found in provided url, this function returns empty string. 81 | -------------------------------------------------------------------------------- /cmd/domainparser/domainparser.go: -------------------------------------------------------------------------------- 1 | package main // import "github.com/bobesa/go-domain-util/cmd/domainparser" 2 | 3 | import ( 4 | "flag" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "sort" 12 | "strings" 13 | ) 14 | 15 | // tld contains single tld info 16 | type tld map[string]*tld 17 | 18 | func (t *tld) Source() string { 19 | // Report nil if nothing is present 20 | if len(*t) == 0 { 21 | return "nil" 22 | } 23 | 24 | // Create set of keys (for sorting) 25 | keys, i := make([]string, len(*t)), 0 26 | for key := range *t { 27 | keys[i] = key 28 | i++ 29 | } 30 | sort.Strings(keys) 31 | 32 | // Create source code based on sorted keys 33 | str := "&tld{\n" 34 | for _, key := range keys { 35 | str += `"` + key + `": ` 36 | str += (*t)[key].Source() 37 | str += ",\n" 38 | } 39 | return str + "}" 40 | } 41 | 42 | func checkError(err error) { 43 | if err != nil { 44 | log.Fatal(err.Error()) 45 | os.Exit(1) 46 | } 47 | } 48 | 49 | func main() { 50 | // Get path as argument 51 | args := flag.Args() 52 | if len(args) == 0 { 53 | // Default: process whole package in current directory. 54 | args = []string{"."} 55 | } 56 | 57 | // Do the http request 58 | resp, err := http.Get("https://publicsuffix.org/list/public_suffix_list.dat") 59 | checkError(err) 60 | 61 | // Read the listing from request body 62 | b, err := ioutil.ReadAll(resp.Body) 63 | checkError(err) 64 | 65 | // Generate basic tree 66 | tlds := &tld{} 67 | 68 | // Parse text as separate lines 69 | lines := strings.Split(string(b), "\n") 70 | for _, line := range lines { 71 | if line != "" && (len(line) < 2 || line[:2] != "//") { 72 | currentTLD := tlds 73 | parts := strings.Split(line, ".") 74 | for p := len(parts) - 1; p >= 0; p-- { 75 | part := parts[p] 76 | if nextTLD, exists := (*currentTLD)[part]; !exists { 77 | nextTLD = &tld{} 78 | (*currentTLD)[part] = nextTLD 79 | currentTLD = nextTLD 80 | } else { 81 | currentTLD = nextTLD 82 | } 83 | } 84 | } 85 | } 86 | 87 | // Create tlds file 88 | source := `// Code generated by github.com/bobesa/go-domain-util, DO NOT EDIT. 89 | 90 | package domainutil 91 | 92 | // tld contains single tld info 93 | type tld map[string]*tld 94 | 95 | // tlds holds all informations about correct tlds 96 | var tlds = ` + tlds.Source() 97 | 98 | // Run gofmt to format the code 99 | cmd := exec.Command("gofmt") 100 | cmd.Stdin = strings.NewReader(source) 101 | out, err := cmd.Output() 102 | if err != nil { 103 | log.Fatal(err) 104 | } 105 | 106 | // Write results 107 | dir := filepath.Dir(args[0]) 108 | outputName := filepath.Join(dir, "tlds.go") 109 | err = ioutil.WriteFile(outputName, out, 0644) 110 | checkError(err) 111 | } 112 | -------------------------------------------------------------------------------- /domainutil/tlds_generator.go: -------------------------------------------------------------------------------- 1 | package domainutil 2 | 3 | //go:generate go run github.com/bobesa/go-domain-util/cmd/domainparser 4 | -------------------------------------------------------------------------------- /domainutil/util.go: -------------------------------------------------------------------------------- 1 | package domainutil 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/net/idna" 7 | ) 8 | 9 | // HasSubdomainQuantity checks the amount of subdomains in domain. 10 | // If quantity matches the number of subdomains in domain, this function returns true. 11 | func HasSubdomainQuantity(domain string, quantity int) bool { 12 | domainSplit := SplitDomain(domain) 13 | if len(domainSplit) - 2 == quantity { 14 | return true 15 | } 16 | return false 17 | } 18 | 19 | // HasSubdomain reports whether domain contains any subdomain. 20 | func HasSubdomain(domain string) bool { 21 | domain, top := stripURLParts(domain), Domain(domain) 22 | return domain != top && top != "" 23 | } 24 | 25 | // Subdomain returns subdomain from provided url. 26 | // If subdomain is not found in provided url, this function returns empty string. 27 | func Subdomain(url string) string { 28 | domain, top := stripURLParts(url), Domain(url) 29 | lt, ld := len(top), len(domain) 30 | if lt < ld && top != "" { 31 | return domain[:(ld-lt)-1] 32 | } 33 | return "" 34 | } 35 | 36 | // SplitDomain split domain into string array 37 | // for example, zh.wikipedia.org will split into {"zh", "wikipedia", "org"} 38 | func SplitDomain(url string) []string { 39 | domain, second, top := Subdomain(url), DomainPrefix(url), DomainSuffix(url) 40 | if len(top) == 0 { 41 | return nil 42 | } 43 | 44 | if len(second) == 0 { 45 | return []string{top} 46 | } 47 | 48 | if len(domain) == 0 { 49 | return []string{second, top} 50 | } 51 | 52 | array := strings.Split(domain, ".") 53 | res := append(array, second, top) 54 | return res 55 | } 56 | 57 | // DomainPrefix returns second-level domain from provided url. 58 | // If no SLD is found in provided url, this function returns empty string. 59 | func DomainPrefix(url string) string { 60 | domain := Domain(url) 61 | if len(domain) != 0 { 62 | return domain[:strings.Index(domain, ".")] 63 | } 64 | return "" 65 | } 66 | 67 | // DomainSuffix returns domain suffix from provided url. 68 | // If no TLD is found in provided url, this function returns empty string. 69 | func DomainSuffix(url string) string { 70 | domain := Domain(url) 71 | if len(domain) != 0 { 72 | return domain[strings.Index(domain, ".")+1:] 73 | } 74 | return "" 75 | } 76 | 77 | // Domain returns top level domain from url string. 78 | // If no domain is found in provided url, this function returns empty string. 79 | // If no TLD is found in provided url, this function returns empty string. 80 | func Domain(url string) string { 81 | domain, top := stripURLParts(url), "" 82 | parts := strings.Split(domain, ".") 83 | currentTld := *tlds 84 | foundTld := false 85 | 86 | // Cycle trough parts in reverse 87 | if len(parts) > 1 { 88 | for i := len(parts) - 1; i >= 0; i-- { 89 | // Generate top domain output 90 | if top != "" { 91 | top = "." + top 92 | } 93 | top = parts[i] + top 94 | 95 | // Check for TLD 96 | if currentTld == nil { 97 | return top // Return current output because we no longer have the TLD 98 | } else if tldEntry, found := currentTld[parts[i]]; found { 99 | if tldEntry != nil { 100 | currentTld = *tldEntry 101 | } else { 102 | currentTld = nil 103 | } 104 | foundTld = true 105 | continue 106 | } else if foundTld { 107 | return top // Return current output if tld was found before 108 | } 109 | 110 | // Return empty string if no tld was found ever 111 | return "" 112 | } 113 | } 114 | 115 | return "" 116 | } 117 | 118 | // stripURLParts removes path, protocol & query from url and returns it. 119 | func stripURLParts(url string) string { 120 | // Lower case the url 121 | url = strings.ToLower(url) 122 | 123 | // Strip protocol 124 | if index := strings.Index(url, "://"); index > -1 { 125 | url = url[index+3:] 126 | } 127 | 128 | // Strip path (and query with it) 129 | if index := strings.Index(url, "/"); index > -1 { 130 | url = url[:index] 131 | } else if index := strings.Index(url, "?"); index > -1 { // Strip query if path is not found 132 | url = url[:index] 133 | } 134 | 135 | // Now, if the url looks like this: username:password@www.example.com/path?query=? 136 | // we remove the content before the '@' symbol 137 | if index := strings.Index(url, "@"); index > -1 { 138 | url = url[index+1:] 139 | } 140 | 141 | // Convert domain to unicode 142 | if strings.Index(url, "xn--") != -1 { 143 | var err error 144 | url, err = idna.ToUnicode(url) 145 | if err != nil { 146 | return "" 147 | } 148 | } 149 | 150 | // Return domain 151 | return url 152 | } 153 | 154 | // Protocol returns protocol from given url 155 | // 156 | // If protocol is not present - return empty string 157 | func Protocol(url string) string { 158 | if index := strings.Index(url, "://"); index > -1 { 159 | return url[:index] 160 | } 161 | return "" 162 | } 163 | 164 | // credentials returns credentials (user:pass) from given url 165 | func credentials(url string) string { 166 | index := strings.IndexRune(url, '@') 167 | if index == -1 { 168 | return "" 169 | } 170 | if protocol := Protocol(url); protocol != "" { 171 | return url[len(protocol)+3 : index] 172 | } 173 | return url[:index] 174 | } 175 | 176 | // Username returns username from given url 177 | // 178 | // If username is not present - return empty string 179 | func Username(url string) string { 180 | auth := strings.SplitN(credentials(url), ":", 2) 181 | if len(auth) == 0 { 182 | return "" 183 | } 184 | return auth[0] 185 | } 186 | 187 | // Password returns password from given url 188 | // 189 | // If password is not present - return empty string 190 | func Password(url string) string { 191 | auth := strings.SplitN(credentials(url), ":", 2) 192 | if len(auth) < 2 { 193 | return "" 194 | } 195 | return auth[1] 196 | } 197 | -------------------------------------------------------------------------------- /domainutil/util_test.go: -------------------------------------------------------------------------------- 1 | package domainutil 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func ExampleHasSubdomain() { 10 | fmt.Println(HasSubdomain("google.com")) 11 | fmt.Println(HasSubdomain("keep.google.com")) 12 | // Output: false 13 | // true 14 | } 15 | 16 | func TestSplitDomain(t *testing.T) { 17 | cases := map[string][]string{ 18 | "http://zh.wikipedia.org": {"zh", "wikipedia", "org"}, 19 | "zh.wikipedia.org": {"zh", "wikipedia", "org"}, 20 | "https://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D": {"zh", "wikipedia", "org"}, 21 | "wikipedia.org": {"wikipedia", "org"}, 22 | ".org": {"org"}, 23 | "org": nil, 24 | "a.b.c.d.wikipedia.org": {"a", "b", "c", "d", "wikipedia", "org"}, 25 | } 26 | 27 | for url, array := range cases { 28 | arrVal := SplitDomain(url) 29 | if !reflect.DeepEqual(array, arrVal) { 30 | t.Errorf("Url (%q) return %v for SplitDomain, bug %v was expected", url, arrVal, array) 31 | } 32 | } 33 | } 34 | 35 | // TestHasSubdomain tests HasSubdomain() function 36 | func TestHasSubdomain(t *testing.T) { 37 | //Test cases 38 | cases := map[string]bool{ 39 | "http://google.com": false, 40 | "http://google.com/ding?true": false, 41 | "google.com/?ding=false": false, 42 | "google.com?ding=false": false, 43 | "nonexist.***": false, 44 | "google.com": false, 45 | "google.co.uk": false, 46 | "gama.google.com": true, 47 | "gama.google.co.uk": true, 48 | "beta.gama.google.co.uk": true, 49 | } 50 | 51 | //Test each domain, some should fail (expected) 52 | for url, shouldHaveSubdomain := range cases { 53 | hasSubdomain := HasSubdomain(url) 54 | if hasSubdomain != shouldHaveSubdomain { 55 | t.Errorf("Url (%q) returned %v for HasSubdomain(), but %v was expected", url, hasSubdomain, shouldHaveSubdomain) 56 | } 57 | } 58 | } 59 | 60 | // BenchmarkHasSubdomain benchmarks HasSubdomain() function 61 | func BenchmarkHasSubdomain(b *testing.B) { 62 | for i := 0; i < b.N; i++ { 63 | HasSubdomain("https://beta.gama.google.co.uk?test=true") 64 | } 65 | } 66 | 67 | func ExampleSubdomain() { 68 | fmt.Printf("%q %q", Subdomain("google.com"), Subdomain("keep.google.com")) 69 | // Output: "" "keep" 70 | } 71 | 72 | // TestSubdomain tests Subdomain() function 73 | func TestSubdomain(t *testing.T) { 74 | //Test cases 75 | cases := map[string]string{ 76 | "http://google.com": "", 77 | "http://google.com/ding?true": "", 78 | "google.com/?ding=false": "", 79 | "google.com?ding=false": "", 80 | "nonexist.***": "", 81 | "google.com": "", 82 | "google.co.uk": "", 83 | "gama.google.com": "gama", 84 | "gama.google.co.uk": "gama", 85 | "beta.gama.google.co.uk": "beta.gama", 86 | "": "", 87 | } 88 | 89 | //Test each domain, some should fail (expected) 90 | for url, expectedSubdomain := range cases { 91 | subdomain := Subdomain(url) 92 | if subdomain != expectedSubdomain { 93 | t.Errorf("Url (%q) returned %q for Subdomain(), but %q was expected", url, subdomain, expectedSubdomain) 94 | } 95 | } 96 | } 97 | 98 | // BenchmarkSubdomain benchmarks Subdomain() function 99 | func BenchmarkSubdomain(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | Subdomain("https://beta.gama.google.co.uk?test=true") 102 | } 103 | } 104 | 105 | func ExampleDomainSuffix() { 106 | fmt.Println(DomainSuffix("google.co.uk")) 107 | fmt.Println(DomainSuffix("keep.google.com")) 108 | // Output: co.uk 109 | // com 110 | } 111 | 112 | // TestDomainPrefix tests DomainPrefix function 113 | func TestDomainPrefix(t *testing.T) { 114 | //Test cases 115 | cases := map[string]string{ 116 | "http://google.com": "google", 117 | "http://google.com/ding?true": "google", 118 | "google.com/?ding=false": "google", 119 | "google.com?ding=false": "google", 120 | "google.com": "google", 121 | "google.co.uk": "google", 122 | "gama.google.com": "google", 123 | "gama.google.co.uk": "google", 124 | "beta.gama.google.co.uk": "google", 125 | } 126 | 127 | for url, expectedPrefix := range cases { 128 | domainPrefix := DomainPrefix(url) 129 | if domainPrefix != expectedPrefix { 130 | t.Errorf("Url (%q) returned %q for DomainPrefix(), but %q was expected", url, domainPrefix, expectedPrefix) 131 | } 132 | } 133 | } 134 | 135 | func BenchmarkDomainPrefix(b *testing.B) { 136 | for i := 0; i < b.N; i++ { 137 | DomainPrefix("https://beta.gama.google.co.uk?test=true") 138 | } 139 | } 140 | 141 | // TestDomainSuffix tests DomainSuffix() function 142 | func TestDomainSuffix(t *testing.T) { 143 | //Test cases 144 | cases := map[string]string{ 145 | "http://google.com": "com", 146 | "http://google.com/ding?true": "com", 147 | "google.com/?ding=false": "com", 148 | "google.com?ding=false": "com", 149 | "nonexist.***": "", 150 | "google.com": "com", 151 | "google.co.uk": "co.uk", 152 | "gama.google.com": "com", 153 | "gama.google.co.uk": "co.uk", 154 | "beta.gama.google.co.uk": "co.uk", 155 | } 156 | 157 | //Test each domain, some should fail (expected) 158 | for url, expectedSuffix := range cases { 159 | domainSuffix := DomainSuffix(url) 160 | if domainSuffix != expectedSuffix { 161 | t.Errorf("Url (%q) returned %q for DomainSuffix(), but %q was expected", url, domainSuffix, expectedSuffix) 162 | } 163 | } 164 | } 165 | 166 | // BenchmarkDomainSuffix benchmarks DomainSuffix() function 167 | func BenchmarkDomainSuffix(b *testing.B) { 168 | for i := 0; i < b.N; i++ { 169 | DomainSuffix("https://beta.gama.google.co.uk?test=true") 170 | } 171 | } 172 | 173 | func ExampleDomain() { 174 | fmt.Println(Domain("google.co.uk")) 175 | fmt.Println(Domain("keep.google.com")) 176 | // Output: google.co.uk 177 | // google.com 178 | } 179 | 180 | // TestDomain tests Domain() function 181 | func TestDomain(t *testing.T) { 182 | //Test cases 183 | cases := map[string]bool{ 184 | "http://google.com": true, 185 | "http://google.com/ding?true": true, 186 | "google.com/?ding=false": true, 187 | "google.com?ding=false": true, 188 | "nonexist.***": false, 189 | "google.com": true, 190 | "google.co.uk": true, 191 | "gama.google.com": true, 192 | "gama.google.co.uk": true, 193 | "beta.gama.google.co.uk": true, 194 | "something.blogspot.com": true, 195 | "something.blogspot.co.uk": true, 196 | } 197 | 198 | //Test each domain, some should fail (expected) 199 | for url, shouldNotBeEmpty := range cases { 200 | domain := Domain(url) 201 | if domain == "" && shouldNotBeEmpty { 202 | t.Errorf("Url (%q) returned empty string as domain, but expected non-empty string", url) 203 | } else if domain != "" && !shouldNotBeEmpty { 204 | t.Errorf("Url (%q) returned (%s) as domain, but expected empty string", url, domain) 205 | } 206 | } 207 | } 208 | 209 | // BenchmarkDomain benchmarks Domain() function 210 | func BenchmarkDomain(b *testing.B) { 211 | for i := 0; i < b.N; i++ { 212 | Domain("https://beta.gama.google.co.uk?test=true") 213 | } 214 | } 215 | 216 | // TestStripURLParts tests stripURLParts() function 217 | func TestStripURLParts(t *testing.T) { 218 | //Test cases 219 | cases := map[string]string{ 220 | "http://google.com": "google.com", 221 | "http://google.com/ding?true": "google.com", 222 | "google.com/?ding=false": "google.com", 223 | "google.com?ding=false": "google.com", 224 | "nonexist.***": "nonexist.***", 225 | "google.com": "google.com", 226 | "google.co.uk": "google.co.uk", 227 | "gama.google.com": "gama.google.com", 228 | "gama.google.co.uk": "gama.google.co.uk", 229 | "beta.gama.google.co.uk": "beta.gama.google.co.uk", 230 | "https://beta.gama.google.co.uk": "beta.gama.google.co.uk", 231 | "xn--n3h.example": "☃.example", 232 | "xn--äää": "", 233 | "http://admin:adminpw@google.com": "google.com", 234 | "admin:adminpw@gama.google.com": "gama.google.com", 235 | "https://admin:adminpw@gama.google.com/path?key=value": "gama.google.com", 236 | "http://medium.com/@example/example": "medium.com", 237 | } 238 | 239 | //Test each domain, some should fail (expected) 240 | for url, expectedStripped := range cases { 241 | stripped := stripURLParts(url) 242 | if stripped != expectedStripped { 243 | t.Errorf("Url (%q) returned %q for StripURLParts(), but %q was expected", url, stripped, expectedStripped) 244 | } 245 | } 246 | } 247 | 248 | // BenchmarkStripURLParts benchmarks StripURLParts() function 249 | func BenchmarkStripURLParts(b *testing.B) { 250 | for i := 0; i < b.N; i++ { 251 | stripURLParts("https://beta.gama.google.co.uk?test=true") 252 | } 253 | } 254 | 255 | func ExampleProtocol() { 256 | fmt.Printf("%q\n", Protocol("google.com")) 257 | fmt.Printf("%q\n", Protocol("ftp://google.com")) 258 | fmt.Printf("%q\n", Protocol("http://google.com")) 259 | fmt.Printf("%q\n", Protocol("https://google.com")) 260 | fmt.Printf("%q\n", Protocol("https://user@google.com")) 261 | fmt.Printf("%q\n", Protocol("https://user:pass@google.com")) 262 | // Output: "" 263 | // "ftp" 264 | // "http" 265 | // "https" 266 | // "https" 267 | // "https" 268 | } 269 | 270 | // TestProtocol tests Protocol() function 271 | func TestProtocol(t *testing.T) { 272 | for _, testCase := range []struct{ URL, Expected string }{ 273 | {"google.com", ""}, 274 | {"ftp://google.com", "ftp"}, 275 | {"http://google.com", "http"}, 276 | {"https://google.com", "https"}, 277 | {"https://user@google.com", "https"}, 278 | {"https://user:pass@google.com", "https"}, 279 | } { 280 | if result := Protocol(testCase.URL); result != testCase.Expected { 281 | t.Errorf(`Url (%q) returned %q for Protocol(), but %q was expected`, testCase.URL, result, testCase.Expected) 282 | } 283 | } 284 | } 285 | 286 | // BenchmarkProtocol benchmarks Protocol() function 287 | func BenchmarkProtocol(b *testing.B) { 288 | for i := 0; i < b.N; i++ { 289 | Protocol("https://user:pass@beta.gama.google.co.uk?test=true") 290 | } 291 | } 292 | 293 | func ExampleUsername() { 294 | fmt.Printf("%q\n", Username("user:pass@google.com")) 295 | fmt.Printf("%q\n", Username("https://user:pass@google.com")) 296 | fmt.Printf("%q\n", Username("https://user@google.com")) 297 | fmt.Printf("%q\n", Username("https://google.com")) 298 | fmt.Printf("%q\n", Username("google.com")) 299 | // Output: "user" 300 | // "user" 301 | // "user" 302 | // "" 303 | // "" 304 | } 305 | 306 | // TestUsername tests Username() function 307 | func TestUsername(t *testing.T) { 308 | for _, testCase := range []struct{ URL, Expected string }{ 309 | {"user:pass@google.com", "user"}, 310 | {"https://user:pass@google.com", "user"}, 311 | {"https://user@google.com", "user"}, 312 | {"https://google.com", ""}, 313 | {"google.com", ""}, 314 | } { 315 | if result := Username(testCase.URL); result != testCase.Expected { 316 | t.Errorf(`Url (%q) returned %q for Username(), but %q was expected`, testCase.URL, result, testCase.Expected) 317 | } 318 | } 319 | } 320 | 321 | // BenchmarkUsername benchmarks Username() function 322 | func BenchmarkUsername(b *testing.B) { 323 | for i := 0; i < b.N; i++ { 324 | Username("https://user:pass@beta.gama.google.co.uk?test=true") 325 | } 326 | } 327 | 328 | func ExamplePassword() { 329 | fmt.Printf("%q\n", Password("user:pass@google.com")) 330 | fmt.Printf("%q\n", Password("https://user:pass@google.com")) 331 | fmt.Printf("%q\n", Password("https://user@google.com")) 332 | fmt.Printf("%q\n", Password("https://google.com")) 333 | fmt.Printf("%q\n", Password("google.com")) 334 | // Output: "pass" 335 | // "pass" 336 | // "" 337 | // "" 338 | // "" 339 | } 340 | 341 | // TestPassword tests Password() function 342 | func TestPassword(t *testing.T) { 343 | for _, testCase := range []struct{ URL, Expected string }{ 344 | {"user:pass@google.com", "pass"}, 345 | {"https://user:pass@google.com", "pass"}, 346 | {"https://user@google.com", ""}, 347 | {"https://google.com", ""}, 348 | {"google.com", ""}, 349 | } { 350 | if result := Password(testCase.URL); result != testCase.Expected { 351 | t.Errorf(`Url (%q) returned %q for Password(), but %q was expected`, testCase.URL, result, testCase.Expected) 352 | } 353 | } 354 | } 355 | 356 | // BenchmarkPassword benchmarks Password() function 357 | func BenchmarkPassword(b *testing.B) { 358 | for i := 0; i < b.N; i++ { 359 | Password("https://user:pass@beta.gama.google.co.uk?test=true") 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bobesa/go-domain-util 2 | 3 | go 1.13 4 | 5 | require ( 6 | golang.org/x/net v0.0.0-20180811021610-c39426892332 7 | golang.org/x/text v0.3.0 // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E= 2 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 3 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 4 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 5 | --------------------------------------------------------------------------------