2020-05-07 09:36:32 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-03-06 19:56:32 +00:00
|
|
|
"net/http"
|
2020-05-07 09:36:32 +00:00
|
|
|
"net/url"
|
|
|
|
"path"
|
2024-03-06 18:52:23 +00:00
|
|
|
"slices"
|
2020-05-07 09:36:32 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2023-11-13 21:30:39 +00:00
|
|
|
func mergeURLs(uiURL, requestURI *url.URL, dropSrcPathPrefixParts int) *url.URL {
|
2021-04-21 07:55:29 +00:00
|
|
|
targetURL := *uiURL
|
2023-11-13 21:30:39 +00:00
|
|
|
srcPath := dropPrefixParts(requestURI.Path, dropSrcPathPrefixParts)
|
|
|
|
if strings.HasPrefix(srcPath, "/") {
|
2023-09-07 22:46:34 +00:00
|
|
|
targetURL.Path = strings.TrimSuffix(targetURL.Path, "/")
|
|
|
|
}
|
2023-11-13 21:30:39 +00:00
|
|
|
targetURL.Path += srcPath
|
2021-04-20 07:51:03 +00:00
|
|
|
requestParams := requestURI.Query()
|
|
|
|
// fast path
|
|
|
|
if len(requestParams) == 0 {
|
2021-04-21 07:55:29 +00:00
|
|
|
return &targetURL
|
2021-04-20 07:51:03 +00:00
|
|
|
}
|
|
|
|
// merge query parameters from requests.
|
2021-04-21 07:55:29 +00:00
|
|
|
uiParams := targetURL.Query()
|
2021-04-20 07:51:03 +00:00
|
|
|
for k, v := range requestParams {
|
|
|
|
// skip clashed query params from original request
|
2021-04-21 07:55:29 +00:00
|
|
|
if exist := uiParams.Get(k); len(exist) > 0 {
|
2021-04-20 07:51:03 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
for i := range v {
|
2021-04-21 07:55:29 +00:00
|
|
|
uiParams.Add(k, v[i])
|
2021-04-20 07:51:03 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-21 07:55:29 +00:00
|
|
|
targetURL.RawQuery = uiParams.Encode()
|
|
|
|
return &targetURL
|
2021-04-20 07:51:03 +00:00
|
|
|
}
|
|
|
|
|
2023-11-13 21:30:39 +00:00
|
|
|
func dropPrefixParts(path string, parts int) string {
|
|
|
|
if parts <= 0 {
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
for parts > 0 {
|
|
|
|
path = strings.TrimPrefix(path, "/")
|
|
|
|
n := strings.IndexByte(path, '/')
|
|
|
|
if n < 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
path = path[n:]
|
|
|
|
parts--
|
|
|
|
}
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
|
2024-03-06 19:56:32 +00:00
|
|
|
func (ui *UserInfo) getURLPrefixAndHeaders(u *url.URL, h http.Header) (*URLPrefix, HeadersConf) {
|
2023-02-10 05:05:13 +00:00
|
|
|
for _, e := range ui.URLMaps {
|
2024-03-06 19:56:32 +00:00
|
|
|
if !matchAnyRegex(e.SrcHosts, u.Host) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !matchAnyRegex(e.SrcPaths, u.Path) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !matchAnyQueryArg(e.SrcQueryArgs, u.Query()) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !matchAnyHeader(e.SrcHeaders, h) {
|
|
|
|
continue
|
2023-02-10 05:05:13 +00:00
|
|
|
}
|
2024-03-06 19:56:32 +00:00
|
|
|
|
|
|
|
return e.URLPrefix, e.HeadersConf
|
2023-02-10 05:05:13 +00:00
|
|
|
}
|
|
|
|
if ui.URLPrefix != nil {
|
2023-12-13 23:04:46 +00:00
|
|
|
return ui.URLPrefix, ui.HeadersConf
|
2023-02-10 05:05:13 +00:00
|
|
|
}
|
2023-12-13 23:04:46 +00:00
|
|
|
return nil, HeadersConf{}
|
2023-02-10 05:05:13 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 22:46:36 +00:00
|
|
|
func matchAnyRegex(rs []*Regex, s string) bool {
|
|
|
|
if len(rs) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, r := range rs {
|
|
|
|
if r.match(s) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-03-06 18:52:23 +00:00
|
|
|
func matchAnyQueryArg(qas []QueryArg, args url.Values) bool {
|
|
|
|
if len(qas) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, qa := range qas {
|
2024-04-17 07:54:43 +00:00
|
|
|
vs, ok := args[qa.Name]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, v := range vs {
|
|
|
|
if qa.Value.match(v) {
|
|
|
|
return true
|
|
|
|
}
|
2024-03-06 18:52:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-03-06 19:56:32 +00:00
|
|
|
func matchAnyHeader(headers []Header, h http.Header) bool {
|
|
|
|
if len(headers) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, header := range headers {
|
|
|
|
if slices.Contains(h.Values(header.Name), header.Value) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-02-10 05:05:13 +00:00
|
|
|
func normalizeURL(uOrig *url.URL) *url.URL {
|
2021-04-21 07:55:29 +00:00
|
|
|
u := *uOrig
|
2020-05-07 09:36:32 +00:00
|
|
|
// Prevent from attacks with using `..` in r.URL.Path
|
|
|
|
u.Path = path.Clean(u.Path)
|
2022-10-01 13:52:27 +00:00
|
|
|
if !strings.HasSuffix(u.Path, "/") && strings.HasSuffix(uOrig.Path, "/") {
|
2023-02-13 12:27:13 +00:00
|
|
|
// The path.Clean() removes trailing slash.
|
2022-10-01 13:52:27 +00:00
|
|
|
// Return it back if needed.
|
|
|
|
// This should fix https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1752
|
|
|
|
u.Path += "/"
|
|
|
|
}
|
2020-05-07 09:36:32 +00:00
|
|
|
if !strings.HasPrefix(u.Path, "/") {
|
|
|
|
u.Path = "/" + u.Path
|
|
|
|
}
|
2022-10-01 13:52:27 +00:00
|
|
|
if u.Path == "/" {
|
|
|
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/pull/1554
|
|
|
|
u.Path = ""
|
|
|
|
}
|
2023-02-10 05:05:13 +00:00
|
|
|
return &u
|
2020-05-07 09:36:32 +00:00
|
|
|
}
|