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
}