Skip to content

Commit dbd9fef

Browse files
committed
protocol utilities
1 parent 210b91e commit dbd9fef

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

proto/escape.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package proto
2+
3+
func fromhex(ch byte) int8 {
4+
if ch >= '0' && ch <= '9' {
5+
return int8(ch - '0')
6+
}
7+
if ch >= 'a' && ch <= 'f' {
8+
return int8(ch - 'a' + 10)
9+
}
10+
if ch >= 'A' && ch <= 'F' {
11+
return int8(ch - 'A' + 10)
12+
}
13+
return -1
14+
}
15+
16+
// RFC1738Unescape is a port of Squid rfc1738_unescape function.
17+
// It does unescaping in-place and returns slice pointing to boundaries of
18+
// unescaped bytes.
19+
func RFC1738Unescape(s []byte) []byte {
20+
var i, j int /* i is write, j is read */
21+
for ; j < len(s); i, j = i+1, j+1 {
22+
s[i] = s[j]
23+
if s[j] != '%' {
24+
/* normal case, nothing more to do */
25+
} else if j+1 < len(s) && s[j+1] == '%' { /* %% case */
26+
j++ /* Skip % */
27+
} else {
28+
if j+2 >= len(s) {
29+
continue
30+
}
31+
/* decode */
32+
var v1, v2 int8
33+
var x byte
34+
v1 = fromhex(s[j+1])
35+
if v1 < 0 {
36+
continue /* non-hex */
37+
}
38+
v2 = fromhex(s[j+2])
39+
if v2 < 0 {
40+
continue /* non-hex */
41+
}
42+
x = byte(v1)<<4 | byte(v2)
43+
if x > 0 && x <= 255 {
44+
s[i] = x
45+
j += 2
46+
}
47+
}
48+
}
49+
return s[:i]
50+
}

proto/escape_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package proto
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"testing"
7+
)
8+
9+
func TestRFC1738Unescape(t *testing.T) {
10+
testcases := []struct {
11+
input string
12+
output string
13+
}{
14+
{
15+
"%2Fdata%2Fsource%2Fpath",
16+
"/data/source/path",
17+
},
18+
{
19+
"http://foo.invalid%2Fdata%2Fsource%2Fpath",
20+
"http://foo.invalid/data/source/path",
21+
},
22+
{
23+
"w%0Ard",
24+
"w\nrd",
25+
},
26+
{
27+
"w%rd",
28+
"w%rd",
29+
},
30+
{
31+
"w%%rd",
32+
"w%rd",
33+
},
34+
{
35+
"w%%%rd",
36+
"w%%rd",
37+
},
38+
{
39+
"Bad String %1",
40+
"Bad String %1",
41+
},
42+
{
43+
"Bad String %1A%3",
44+
"Bad String \032%3",
45+
},
46+
{
47+
"Good String %1A",
48+
"Good String \032",
49+
},
50+
{
51+
"w%00rd",
52+
"w%00rd",
53+
},
54+
{
55+
"w%0rd",
56+
"w%0rd",
57+
},
58+
{
59+
"w%%00%rd",
60+
"w%00%rd",
61+
},
62+
{
63+
"w%%%00%rd",
64+
"w%%00%rd",
65+
},
66+
}
67+
for i, tc := range testcases {
68+
t.Run(fmt.Sprintf("Testcase#%d", i+1), func(t *testing.T) {
69+
output := RFC1738Unescape([]byte(tc.input))
70+
if bytes.Compare(output, []byte(tc.output)) != 0 {
71+
t.Errorf("got: %x, want: %x", output, []byte(tc.output))
72+
}
73+
})
74+
}
75+
}

0 commit comments

Comments
 (0)