generated from okp4/template-oss
-
Notifications
You must be signed in to change notification settings - Fork 119
/
uri.go
109 lines (95 loc) 路 3.07 KB
/
uri.go
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
106
107
108
109
package util
import (
"net/url"
)
type URIComponent int
const (
PathComponent URIComponent = iota
SegmentComponent
QueryValueComponent
FragmentComponent
)
const upperhex = "0123456789ABCDEF"
// Return true if the specified character should be escaped when
// appearing in a URL string depending on the targeted URI component, according
// to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986).
//
// This is a re-implementation of url.shouldEscape of net/url. Needed since the native implementation doesn't follow
// exactly the [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) and also because the implementation of component
// escaping is only public for Path component (who in reality is SegmentPath component) and Query component. Otherwise,
// escaping doesn't fit to the SWI-Prolog escaping due to RFC discrepancy between those two implementations.
//
// Another discrepancy is on the query component that escape the space character ' ' to a '+' (plus sign) on the
// golang library and to '%20' escaping on the
// [SWI-Prolog implementation](https://www.swi-prolog.org/pldoc/doc/_SWI_/library/uri.pl?show=src#uri_encoded/3).
//
// Here some reported issues on golang about the RFC non-compliance.
// - golang.org/issue/5684.
// - https://github.com/golang/go/issues/27559
func (c URIComponent) shouldEscape(b byte) bool {
// 搂2.3 Unreserved characters (alphanum)
if 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' || '0' <= b && b <= '9' {
return false
}
switch b {
case '-', '.', '_', '~': // 搂2.3 Unreserved characters (mark)
return false
case '!', '$', '&', '\'', '(', ')', '*', '+', ',', '/', ':', ';', '=', '?', '@': // 搂2.2 Reserved characters (reserved)
// Different sections of the URL allow a few of
// the reserved characters to appear unescaped.
switch c {
case PathComponent: // 搂3.3
return b == '?' || b == ':'
case SegmentComponent: // 搂3.3
// The RFC allows : @ & = + $
// meaning to individual path segments.
return b == '/' || b == '?' || b == ':'
case QueryValueComponent: // 搂3.4
return b == '&' || b == '+' || b == ':' || b == ';' || b == '='
case FragmentComponent: // 搂4.1
return false
}
}
// Everything else must be escaped.
return true
}
// Escape return the given input string by adding percent encoding depending on the current component where it's
// supposed to be put.
// This is a re-implementation of native url.escape. See shouldEscape() comment's for more details.
func (c URIComponent) Escape(v string) string {
hexCount := 0
for i := 0; i < len(v); i++ {
ch := v[i]
if c.shouldEscape(ch) {
hexCount++
}
}
if hexCount == 0 {
return v
}
var buf [64]byte
var t []byte
required := len(v) + 2*hexCount
if required <= len(buf) {
t = buf[:required]
} else {
t = make([]byte, required)
}
j := 0
for i := 0; i < len(v); i++ {
switch ch := v[i]; {
case c.shouldEscape(ch):
t[j] = '%'
t[j+1] = upperhex[ch>>4]
t[j+2] = upperhex[ch&15]
j += 3
default:
t[j] = v[i]
j++
}
}
return string(t)
}
func (c URIComponent) Unescape(v string) (string, error) {
return url.PathUnescape(v)
}