forked from TheThingsNetwork/lorawan-stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathredirect.go
More file actions
105 lines (99 loc) · 2.91 KB
/
redirect.go
File metadata and controls
105 lines (99 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Copyright © 2020 The Things Network Foundation, The Things Industries B.V.
//
// 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 webmiddleware
import (
"net"
"net/http"
"net/url"
"strconv"
)
// RedirectConfiguration is the configuration for the Redirect middleware.
// If any of Scheme, HostName, Port, Path returns a different value than the argument
// passed to it, the middleware will redirect with the configured Code.
type RedirectConfiguration struct {
Scheme func(string) string
HostName func(string) string
Port func(uint) uint
Path func(string) string
Code int
}
func (c RedirectConfiguration) isZero() bool {
return c.Scheme == nil && c.HostName == nil && c.Port == nil && c.Path == nil
}
func (c RedirectConfiguration) build(url *url.URL) (*url.URL, bool) {
var (
target = *url
redirect bool
)
if c.Scheme != nil {
if s := c.Scheme(url.Scheme); s != url.Scheme {
target.Scheme, redirect = s, true
}
}
if c.HostName != nil || c.Port != nil {
hostname, portStr, err := net.SplitHostPort(url.Host)
if err != nil {
hostname, portStr = url.Host, ""
}
if c.HostName != nil {
hostname = c.HostName(hostname)
}
if c.Port != nil {
port, _ := strconv.ParseUint(portStr, 10, 0)
port = uint64(c.Port(uint(port)))
portStr = strconv.FormatUint(port, 10)
}
host := hostname
if portStr != "" {
switch {
case portStr == "0":
// Just use the hostname.
case target.Scheme == "http" && portStr == "80":
// This is the default. Just use the hostame.
case target.Scheme == "https" && portStr == "443":
// This is the default. Just use the hostame.
default:
host = net.JoinHostPort(host, portStr)
}
}
if host != url.Host {
target.Host, redirect = host, true
}
}
if c.Path != nil {
if p := c.Path(url.Path); p != url.Path {
target.Path, redirect = p, true
}
}
return &target, redirect
}
// Redirect returns a middleware that redirects requests if they don't already match the configuration.
func Redirect(config RedirectConfiguration) MiddlewareFunc {
return func(next http.Handler) http.Handler {
if config.isZero() {
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if url, redirect := config.build(r.URL); redirect {
code := config.Code
if code == 0 {
code = http.StatusFound
}
http.Redirect(w, r, url.String(), code)
return
}
next.ServeHTTP(w, r)
})
}
}