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  }