pkg/ingress/kube/annotations/redirect.go (109 lines of code) (raw):

// Copyright (c) 2022 Alibaba Group Holding Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package annotations import ( "fmt" "net/http" "net/url" "strings" networking "istio.io/api/networking/v1alpha3" ) const ( appRoot = "app-root" temporalRedirect = "temporal-redirect" permanentRedirect = "permanent-redirect" permanentRedirectCode = "permanent-redirect-code" sslRedirect = "ssl-redirect" forceSSLRedirect = "force-ssl-redirect" defaultPermanentRedirectCode = 301 defaultTemporalRedirectCode = 302 ) var ( _ Parser = &redirect{} _ RouteHandler = &redirect{} ) type RedirectConfig struct { AppRoot string URL string Code int httpsRedirect bool } type redirect struct{} func (r redirect) Parse(annotations Annotations, config *Ingress, _ *GlobalContext) error { if !needRedirectConfig(annotations) { return nil } redirectConfig := &RedirectConfig{ Code: defaultPermanentRedirectCode, } config.Redirect = redirectConfig redirectConfig.AppRoot, _ = annotations.ParseStringASAP(appRoot) httpsRedirect, _ := annotations.ParseBoolASAP(sslRedirect) forceHTTPSRedirect, _ := annotations.ParseBoolASAP(forceSSLRedirect) if httpsRedirect || forceHTTPSRedirect { redirectConfig.httpsRedirect = true } // temporal redirect is firstly applied. tr, err := annotations.ParseStringASAP(temporalRedirect) if err != nil && !IsMissingAnnotations(err) { return nil } if tr != "" && isValidURL(tr) == nil { redirectConfig.URL = tr redirectConfig.Code = defaultTemporalRedirectCode return nil } // permanent redirect // url pr, err := annotations.ParseStringASAP(permanentRedirect) if err != nil && !IsMissingAnnotations(err) { return nil } if pr != "" && isValidURL(pr) == nil { redirectConfig.URL = pr } // code if prc, err := annotations.ParseIntASAP(permanentRedirectCode); err == nil { if prc < http.StatusMultipleChoices || prc > http.StatusPermanentRedirect { prc = defaultPermanentRedirectCode } redirectConfig.Code = prc } return nil } func (r redirect) ApplyRoute(route *networking.HTTPRoute, config *Ingress) { redirectConfig := config.Redirect if redirectConfig == nil { return } var redirectPolicy *networking.HTTPRedirect if redirectConfig.URL != "" { parseURL, err := url.Parse(redirectConfig.URL) if err != nil { return } redirectPolicy = &networking.HTTPRedirect{ Scheme: parseURL.Scheme, Authority: parseURL.Host, Uri: parseURL.Path, RedirectCode: uint32(redirectConfig.Code), } } else if redirectConfig.httpsRedirect { redirectPolicy = &networking.HTTPRedirect{ Scheme: "https", // 308 is the default code for ssl redirect RedirectCode: 308, } } route.Redirect = redirectPolicy } func needRedirectConfig(annotations Annotations) bool { return annotations.HasASAP(temporalRedirect) || annotations.HasASAP(permanentRedirect) || annotations.HasASAP(sslRedirect) || annotations.HasASAP(forceSSLRedirect) || annotations.HasASAP(appRoot) } func isValidURL(s string) error { u, err := url.Parse(s) if err != nil { return err } if !strings.HasPrefix(u.Scheme, "http") { return fmt.Errorf("only http and https are valid protocols (%v)", u.Scheme) } return nil }