knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/apis/url.go (about) 1 /* 2 Copyright 2019 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package apis 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "net/url" 23 24 "k8s.io/apimachinery/pkg/api/equality" 25 ) 26 27 // URL is an alias of url.URL. 28 // It has custom json marshal methods that enable it to be used in K8s CRDs 29 // such that the CRD resource will have the URL but operator code can can work with url.URL struct 30 // +kubebuilder:validation:Type=string 31 type URL url.URL 32 33 // ParseURL attempts to parse the given string as a URL. 34 // Compatible with net/url.Parse except in the case of an empty string, where 35 // the resulting *URL will be nil with no error. 36 func ParseURL(u string) (*URL, error) { 37 if u == "" { 38 return nil, nil 39 } 40 pu, err := url.Parse(u) 41 if err != nil { 42 return nil, err 43 } 44 return (*URL)(pu), nil 45 } 46 47 // HTTP creates an http:// URL pointing to a known domain. 48 func HTTP(domain string) *URL { 49 return &URL{ 50 Scheme: "http", 51 Host: domain, 52 } 53 } 54 55 // HTTPS creates an https:// URL pointing to a known domain. 56 func HTTPS(domain string) *URL { 57 return &URL{ 58 Scheme: "https", 59 Host: domain, 60 } 61 } 62 63 // IsEmpty returns true if the URL is `nil` or represents an empty URL. 64 func (u *URL) IsEmpty() bool { 65 if u == nil { 66 return true 67 } 68 return *u == URL{} 69 } 70 71 // MarshalJSON implements a custom json marshal method used when this type is 72 // marshaled using json.Marshal. 73 // json.Marshaler impl 74 func (u URL) MarshalJSON() ([]byte, error) { 75 b := fmt.Sprintf("%q", u.String()) 76 return []byte(b), nil 77 } 78 79 // UnmarshalJSON implements the json unmarshal method used when this type is 80 // unmarsheled using json.Unmarshal. 81 // json.Unmarshaler impl 82 func (u *URL) UnmarshalJSON(b []byte) error { 83 var ref string 84 if err := json.Unmarshal(b, &ref); err != nil { 85 return err 86 } 87 if r, err := ParseURL(ref); err != nil { 88 return err 89 } else if r != nil { 90 *u = *r 91 } else { 92 *u = URL{} 93 } 94 95 return nil 96 } 97 98 // String returns the full string representation of the URL. 99 func (u *URL) String() string { 100 if u == nil { 101 return "" 102 } 103 uu := url.URL(*u) 104 return uu.String() 105 } 106 107 // URL returns the URL as a url.URL. 108 func (u *URL) URL() *url.URL { 109 if u == nil { 110 return &url.URL{} 111 } 112 url := url.URL(*u) 113 return &url 114 } 115 116 // ResolveReference calls the underlying ResolveReference method 117 // and returns an apis.URL 118 func (u *URL) ResolveReference(ref *URL) *URL { 119 if ref == nil { 120 return u 121 } 122 // Turn both u / ref to url.URL 123 uRef := url.URL(*ref) 124 uu := url.URL(*u) 125 126 newU := uu.ResolveReference(&uRef) 127 128 // Turn new back to apis.URL 129 ret := URL(*newU) 130 return &ret 131 } 132 133 func init() { 134 equality.Semantic.AddFunc( 135 // url.URL has an unexported type (UserInfo) which causes semantic 136 // equality to panic unless we add a custom equality function 137 func(a, b URL) bool { 138 return a.String() == b.String() 139 }, 140 ) 141 }