2020-12-24 09:56:10 +01:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
2022-06-22 19:38:43 +02:00
|
|
|
"net/http"
|
2020-12-24 09:56:10 +01:00
|
|
|
"net/url"
|
|
|
|
|
2021-03-12 02:35:49 +01:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
2021-03-09 17:54:09 +01:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth"
|
2020-12-24 09:56:10 +01:00
|
|
|
)
|
|
|
|
|
2023-08-12 14:03:08 +02:00
|
|
|
var validURLSchemes = []string{"http", "https", "socks5", "tls+socks5"}
|
|
|
|
|
|
|
|
func isURLSchemeValid(scheme string) bool {
|
|
|
|
for _, vs := range validURLSchemes {
|
|
|
|
if scheme == vs {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-12-24 09:56:10 +01:00
|
|
|
// URL implements YAML.Marshaler and yaml.Unmarshaler interfaces for url.URL.
|
|
|
|
type URL struct {
|
2022-05-06 23:02:54 +02:00
|
|
|
URL *url.URL
|
2020-12-24 09:56:10 +01:00
|
|
|
}
|
|
|
|
|
2021-03-12 02:35:49 +01:00
|
|
|
// MustNewURL returns new URL for the given u.
|
2021-10-26 20:21:08 +02:00
|
|
|
func MustNewURL(u string) *URL {
|
2021-03-12 02:35:49 +01:00
|
|
|
pu, err := url.Parse(u)
|
|
|
|
if err != nil {
|
|
|
|
logger.Panicf("BUG: cannot parse u=%q: %s", u, err)
|
|
|
|
}
|
2021-10-26 20:21:08 +02:00
|
|
|
return &URL{
|
2022-05-06 23:02:54 +02:00
|
|
|
URL: pu,
|
2021-03-12 02:35:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-06 23:02:54 +02:00
|
|
|
// GetURL return the underlying url.
|
|
|
|
func (u *URL) GetURL() *url.URL {
|
|
|
|
if u == nil || u.URL == nil {
|
2020-12-24 09:56:10 +01:00
|
|
|
return nil
|
|
|
|
}
|
2022-05-06 23:02:54 +02:00
|
|
|
return u.URL
|
2020-12-24 09:56:10 +01:00
|
|
|
}
|
|
|
|
|
2021-04-03 23:40:08 +02:00
|
|
|
// IsHTTPOrHTTPS returns true if u is http or https
|
|
|
|
func (u *URL) IsHTTPOrHTTPS() bool {
|
2022-05-06 23:02:54 +02:00
|
|
|
pu := u.GetURL()
|
2021-04-03 23:40:08 +02:00
|
|
|
if pu == nil {
|
|
|
|
return false
|
|
|
|
}
|
2022-05-06 23:02:54 +02:00
|
|
|
scheme := u.URL.Scheme
|
2021-04-03 23:40:08 +02:00
|
|
|
return scheme == "http" || scheme == "https"
|
|
|
|
}
|
|
|
|
|
2021-03-12 02:35:49 +01:00
|
|
|
// String returns string representation of u.
|
|
|
|
func (u *URL) String() string {
|
2022-05-06 23:02:54 +02:00
|
|
|
pu := u.GetURL()
|
2021-03-12 02:35:49 +01:00
|
|
|
if pu == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return pu.String()
|
|
|
|
}
|
|
|
|
|
2022-06-22 19:38:43 +02:00
|
|
|
// SetHeaders sets headers to req according to u and ac configs.
|
2023-10-17 11:58:19 +02:00
|
|
|
func (u *URL) SetHeaders(ac *promauth.Config, req *http.Request) error {
|
|
|
|
ah, err := u.getAuthHeader(ac)
|
|
|
|
if err != nil {
|
2023-10-25 23:19:33 +02:00
|
|
|
return fmt.Errorf("cannot obtain Proxy-Authorization headers: %w", err)
|
2023-10-17 11:58:19 +02:00
|
|
|
}
|
2022-06-22 19:38:43 +02:00
|
|
|
if ah != "" {
|
|
|
|
req.Header.Set("Proxy-Authorization", ah)
|
|
|
|
}
|
2023-10-17 11:58:19 +02:00
|
|
|
return ac.SetHeaders(req, false)
|
2022-06-22 19:38:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// getAuthHeader returns Proxy-Authorization auth header for the given u and ac.
|
2023-10-17 11:58:19 +02:00
|
|
|
func (u *URL) getAuthHeader(ac *promauth.Config) (string, error) {
|
2021-04-03 23:40:08 +02:00
|
|
|
authHeader := ""
|
|
|
|
if ac != nil {
|
2023-10-17 11:58:19 +02:00
|
|
|
var err error
|
|
|
|
authHeader, err = ac.GetAuthHeader()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2021-04-03 23:40:08 +02:00
|
|
|
}
|
2022-05-06 23:02:54 +02:00
|
|
|
if u == nil || u.URL == nil {
|
2023-10-17 11:58:19 +02:00
|
|
|
return authHeader, nil
|
2021-04-03 23:40:08 +02:00
|
|
|
}
|
2022-05-06 23:02:54 +02:00
|
|
|
pu := u.URL
|
2021-04-03 23:40:08 +02:00
|
|
|
if pu.User != nil && len(pu.User.Username()) > 0 {
|
|
|
|
userPasswordEncoded := base64.StdEncoding.EncodeToString([]byte(pu.User.String()))
|
|
|
|
authHeader = "Basic " + userPasswordEncoded
|
|
|
|
}
|
2023-10-17 11:58:19 +02:00
|
|
|
return authHeader, nil
|
2021-04-03 23:40:08 +02:00
|
|
|
}
|
|
|
|
|
2020-12-24 09:56:10 +01:00
|
|
|
// MarshalYAML implements yaml.Marshaler interface.
|
2024-07-10 00:14:15 +02:00
|
|
|
func (u *URL) MarshalYAML() (any, error) {
|
2022-05-06 23:02:54 +02:00
|
|
|
if u.URL == nil {
|
2020-12-24 09:56:10 +01:00
|
|
|
return nil, nil
|
|
|
|
}
|
2022-05-06 23:02:54 +02:00
|
|
|
return u.URL.String(), nil
|
2020-12-24 09:56:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalYAML implements yaml.Unmarshaler interface.
|
2024-07-10 00:14:15 +02:00
|
|
|
func (u *URL) UnmarshalYAML(unmarshal func(any) error) error {
|
2020-12-24 09:56:10 +01:00
|
|
|
var s string
|
|
|
|
if err := unmarshal(&s); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
parsedURL, err := url.Parse(s)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot parse proxy_url=%q as *url.URL: %w", s, err)
|
|
|
|
}
|
2023-08-12 14:03:08 +02:00
|
|
|
if !isURLSchemeValid(parsedURL.Scheme) {
|
|
|
|
return fmt.Errorf("cannot parse proxy_url=%q unsupported scheme format=%q, valid schemes: %s", s, parsedURL.Scheme, validURLSchemes)
|
|
|
|
}
|
2022-05-06 23:02:54 +02:00
|
|
|
u.URL = parsedURL
|
2020-12-24 09:56:10 +01:00
|
|
|
return nil
|
|
|
|
}
|