Skip to content

Commit a9be3ec

Browse files
committed
feat(serverHandler/log): output unescaped url path
1 parent 19b843f commit a9be3ec

File tree

5 files changed

+160
-3
lines changed

5 files changed

+160
-3
lines changed

src/serverHandler/log.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
11
package serverHandler
22

33
import (
4+
"../util"
45
"net/http"
6+
"net/url"
57
)
68

79
func (h *handler) logRequest(r *http.Request) {
810
if !h.logger.CanLogAccess() {
911
return
1012
}
1113

12-
buf := make([]byte, 0, 2+len(r.RemoteAddr)+len(r.Method)+len(r.RequestURI))
14+
var unescapedUri []byte
15+
unescapedLen := 0
16+
unescapedStr, err := url.QueryUnescape(r.RequestURI)
17+
if err == nil && unescapedStr != r.RequestURI {
18+
unescapedUri = util.EscapeControllingRune(unescapedStr)
19+
if len(unescapedUri) > 0 {
20+
unescapedLen = len(unescapedUri) + 5 // " <=> "
21+
}
22+
}
23+
24+
uri := util.EscapeControllingRune(r.RequestURI)
25+
26+
buf := make([]byte, 0, 2+len(r.RemoteAddr)+len(r.Method)+unescapedLen+len(uri))
1327

1428
buf = append(buf, []byte(r.RemoteAddr)...) // ~ 9-47 bytes, mainly 21 bytes
1529
buf = append(buf, ' ') // 1 byte
1630
buf = append(buf, []byte(r.Method)...) // ~ 3-4 bytes
1731
buf = append(buf, ' ') // 1 byte
18-
buf = append(buf, []byte(r.RequestURI)...)
32+
if unescapedLen > 0 {
33+
buf = append(buf, unescapedUri...)
34+
buf = append(buf, ' ', '<', '=', '>', ' ') // 5 bytes
35+
}
36+
buf = append(buf, uri...)
1937

2038
go h.logger.LogAccess(buf)
2139
}

src/util/conv.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package util
2+
3+
var hexSeq = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}
4+
5+
func ByteToHex(b byte) (high, low byte) {
6+
high = hexSeq[b>>4]
7+
low = hexSeq[b&15]
8+
return
9+
}

src/util/conv_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package util
2+
3+
import "testing"
4+
5+
func TestByteToHex(t *testing.T) {
6+
var h, l byte
7+
8+
h, l = ByteToHex(0)
9+
if h != '0' && l != '0' {
10+
t.Error(h, l)
11+
}
12+
13+
h, l = ByteToHex(8)
14+
if h != '0' && l != '8' {
15+
t.Error(h, l)
16+
}
17+
18+
h, l = ByteToHex(15)
19+
if h != '0' && l != 'f' {
20+
t.Error(h, l)
21+
}
22+
23+
h, l = ByteToHex(16)
24+
if h != '1' && l != '0' {
25+
t.Error(h, l)
26+
}
27+
28+
h, l = ByteToHex(24)
29+
if h != '1' && l != '8' {
30+
t.Error(h, l)
31+
}
32+
33+
h, l = ByteToHex(240)
34+
if h != 'f' && l != '0' {
35+
t.Error(h, l)
36+
}
37+
38+
h, l = ByteToHex(248)
39+
if h != 'f' && l != '8' {
40+
t.Error(h, l)
41+
}
42+
43+
h, l = ByteToHex(255)
44+
if h != 'f' && l != 'f' {
45+
t.Error(h, l)
46+
}
47+
}

src/util/str.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package util
22

3-
import "strings"
3+
import (
4+
"strings"
5+
"unicode"
6+
"unicode/utf8"
7+
)
48

59
type StrEqualFunc func(a, b string) bool
610

@@ -11,3 +15,47 @@ func IsStrEqualAccurate(a, b string) bool {
1115
func IsStrEqualNoCase(a, b string) bool {
1216
return strings.EqualFold(a, b)
1317
}
18+
19+
func EscapeControllingRune(str string) []byte {
20+
runeBytes := make([]byte, 4)
21+
buf := make([]byte, 0, len(str))
22+
23+
for _, r := range str {
24+
if uint32(r) < 32 { // non-printable chars
25+
b := byte(r)
26+
if b == 0 {
27+
buf = append(buf, '\\', '0')
28+
} else if b == '\a' {
29+
buf = append(buf, '\\', 'a')
30+
} else if b == '\b' {
31+
buf = append(buf, '\\', 'b')
32+
} else if b == '\f' {
33+
buf = append(buf, '\\', 'f')
34+
} else if b == '\n' {
35+
buf = append(buf, '\\', 'n')
36+
} else if b == '\r' {
37+
buf = append(buf, '\\', 'r')
38+
} else if b == '\t' {
39+
buf = append(buf, '\\', 't')
40+
} else if b == '\v' {
41+
buf = append(buf, '\\', 'v')
42+
} else {
43+
h, l := ByteToHex(b)
44+
buf = append(buf, '\\', 'x', h, l)
45+
}
46+
continue
47+
}
48+
49+
if unicode.IsControl(r) {
50+
nBytes := utf8.EncodeRune(runeBytes, r)
51+
for i := 0; i < nBytes; i++ {
52+
h, l := ByteToHex(runeBytes[i])
53+
buf = append(buf, '\\', 'x', h, l)
54+
}
55+
} else {
56+
buf = utf8.AppendRune(buf, r)
57+
}
58+
}
59+
60+
return buf
61+
}

src/util/str_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package util
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestReplaceControllingRune(t *testing.T) {
9+
var str string
10+
buf := make([]byte, 0, 64)
11+
12+
str = "abcdefg"
13+
buf = EscapeControllingRune(str)
14+
if !bytes.Equal(buf, []byte(str)) {
15+
t.Error(string(buf))
16+
}
17+
18+
str = "abc\tdef"
19+
buf = EscapeControllingRune(str)
20+
if !bytes.Equal(buf, []byte("abc\\tdef")) {
21+
t.Error(string(buf))
22+
}
23+
24+
str = "<\000\a\b\f\n\r\t\v>"
25+
buf = EscapeControllingRune(str)
26+
if !bytes.Equal(buf, []byte("<\\0\\a\\b\\f\\n\\r\\t\\v>")) {
27+
t.Error(string(buf))
28+
}
29+
30+
str = string([]byte{'[', 0x0e, 127, ']'})
31+
buf = EscapeControllingRune(str)
32+
if !bytes.Equal(buf, []byte("[\\x0e\\x7f]")) {
33+
t.Error(string(buf))
34+
}
35+
}

0 commit comments

Comments
 (0)