mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 09:47:54 +00:00
Travis seems to be having issues pulling deps, so we'll have to check in the vendor directory and prevent the makefile from trying to regenerate it normally.
180 lines
4.7 KiB
Go
180 lines
4.7 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package urlesc implements query escaping as per RFC 3986.
|
|
// It contains some parts of the net/url package, modified so as to allow
|
|
// some reserved characters incorrectly escaped by net/url.
|
|
// See https://github.com/golang/go/issues/5684
|
|
package urlesc
|
|
|
|
import (
|
|
"bytes"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type encoding int
|
|
|
|
const (
|
|
encodePath encoding = 1 + iota
|
|
encodeUserPassword
|
|
encodeQueryComponent
|
|
encodeFragment
|
|
)
|
|
|
|
// Return true if the specified character should be escaped when
|
|
// appearing in a URL string, according to RFC 3986.
|
|
func shouldEscape(c byte, mode encoding) bool {
|
|
// §2.3 Unreserved characters (alphanum)
|
|
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
|
|
return false
|
|
}
|
|
|
|
switch c {
|
|
case '-', '.', '_', '~': // §2.3 Unreserved characters (mark)
|
|
return false
|
|
|
|
// §2.2 Reserved characters (reserved)
|
|
case ':', '/', '?', '#', '[', ']', '@', // gen-delims
|
|
'!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims
|
|
// Different sections of the URL allow a few of
|
|
// the reserved characters to appear unescaped.
|
|
switch mode {
|
|
case encodePath: // §3.3
|
|
// The RFC allows sub-delims and : @.
|
|
// '/', '[' and ']' can be used to assign meaning to individual path
|
|
// segments. This package only manipulates the path as a whole,
|
|
// so we allow those as well. That leaves only ? and # to escape.
|
|
return c == '?' || c == '#'
|
|
|
|
case encodeUserPassword: // §3.2.1
|
|
// The RFC allows : and sub-delims in
|
|
// userinfo. The parsing of userinfo treats ':' as special so we must escape
|
|
// all the gen-delims.
|
|
return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'
|
|
|
|
case encodeQueryComponent: // §3.4
|
|
// The RFC allows / and ?.
|
|
return c != '/' && c != '?'
|
|
|
|
case encodeFragment: // §4.1
|
|
// The RFC text is silent but the grammar allows
|
|
// everything, so escape nothing but #
|
|
return c == '#'
|
|
}
|
|
}
|
|
|
|
// Everything else must be escaped.
|
|
return true
|
|
}
|
|
|
|
// QueryEscape escapes the string so it can be safely placed
|
|
// inside a URL query.
|
|
func QueryEscape(s string) string {
|
|
return escape(s, encodeQueryComponent)
|
|
}
|
|
|
|
func escape(s string, mode encoding) string {
|
|
spaceCount, hexCount := 0, 0
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
if shouldEscape(c, mode) {
|
|
if c == ' ' && mode == encodeQueryComponent {
|
|
spaceCount++
|
|
} else {
|
|
hexCount++
|
|
}
|
|
}
|
|
}
|
|
|
|
if spaceCount == 0 && hexCount == 0 {
|
|
return s
|
|
}
|
|
|
|
t := make([]byte, len(s)+2*hexCount)
|
|
j := 0
|
|
for i := 0; i < len(s); i++ {
|
|
switch c := s[i]; {
|
|
case c == ' ' && mode == encodeQueryComponent:
|
|
t[j] = '+'
|
|
j++
|
|
case shouldEscape(c, mode):
|
|
t[j] = '%'
|
|
t[j+1] = "0123456789ABCDEF"[c>>4]
|
|
t[j+2] = "0123456789ABCDEF"[c&15]
|
|
j += 3
|
|
default:
|
|
t[j] = s[i]
|
|
j++
|
|
}
|
|
}
|
|
return string(t)
|
|
}
|
|
|
|
var uiReplacer = strings.NewReplacer(
|
|
"%21", "!",
|
|
"%27", "'",
|
|
"%28", "(",
|
|
"%29", ")",
|
|
"%2A", "*",
|
|
)
|
|
|
|
// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986.
|
|
func unescapeUserinfo(s string) string {
|
|
return uiReplacer.Replace(s)
|
|
}
|
|
|
|
// Escape reassembles the URL into a valid URL string.
|
|
// The general form of the result is one of:
|
|
//
|
|
// scheme:opaque
|
|
// scheme://userinfo@host/path?query#fragment
|
|
//
|
|
// If u.Opaque is non-empty, String uses the first form;
|
|
// otherwise it uses the second form.
|
|
//
|
|
// In the second form, the following rules apply:
|
|
// - if u.Scheme is empty, scheme: is omitted.
|
|
// - if u.User is nil, userinfo@ is omitted.
|
|
// - if u.Host is empty, host/ is omitted.
|
|
// - if u.Scheme and u.Host are empty and u.User is nil,
|
|
// the entire scheme://userinfo@host/ is omitted.
|
|
// - if u.Host is non-empty and u.Path begins with a /,
|
|
// the form host/path does not add its own /.
|
|
// - if u.RawQuery is empty, ?query is omitted.
|
|
// - if u.Fragment is empty, #fragment is omitted.
|
|
func Escape(u *url.URL) string {
|
|
var buf bytes.Buffer
|
|
if u.Scheme != "" {
|
|
buf.WriteString(u.Scheme)
|
|
buf.WriteByte(':')
|
|
}
|
|
if u.Opaque != "" {
|
|
buf.WriteString(u.Opaque)
|
|
} else {
|
|
if u.Scheme != "" || u.Host != "" || u.User != nil {
|
|
buf.WriteString("//")
|
|
if ui := u.User; ui != nil {
|
|
buf.WriteString(unescapeUserinfo(ui.String()))
|
|
buf.WriteByte('@')
|
|
}
|
|
if h := u.Host; h != "" {
|
|
buf.WriteString(h)
|
|
}
|
|
}
|
|
if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
|
|
buf.WriteByte('/')
|
|
}
|
|
buf.WriteString(escape(u.Path, encodePath))
|
|
}
|
|
if u.RawQuery != "" {
|
|
buf.WriteByte('?')
|
|
buf.WriteString(u.RawQuery)
|
|
}
|
|
if u.Fragment != "" {
|
|
buf.WriteByte('#')
|
|
buf.WriteString(escape(u.Fragment, encodeFragment))
|
|
}
|
|
return buf.String()
|
|
}
|