Properly handle hashes and single dots in URI.Update (see https://github.com/kataras/iris/issues/173)

This commit is contained in:
Aliaksandr Valialkin
2016-06-07 13:29:51 +03:00
parent 3c5ba2c98d
commit 033bb40f06
3 changed files with 50 additions and 26 deletions
+15
View File
@@ -8,6 +8,7 @@ import (
"io"
"math"
"net"
"reflect"
"sync"
"time"
"unsafe"
@@ -360,6 +361,20 @@ func b2s(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// s2b converts string to a byte slice without memory allocation.
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func s2b(s string) []byte {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: sh.Data,
Len: sh.Len,
Cap: sh.Len,
}
return *(*[]byte)(unsafe.Pointer(&bh))
}
// AppendQuotedArg appends url-encoded src to dst and returns appended dst.
func AppendQuotedArg(dst, src []byte) []byte {
for _, c := range src {
+31 -26
View File
@@ -286,8 +286,19 @@ func normalizePath(dst, src []byte) []byte {
}
dst = dst[:bSize]
// remove /foo/../ parts
// remove /./ parts
b = dst
for {
n := bytes.Index(b, strSlashDotSlash)
if n < 0 {
break
}
nn := n + len(strSlashDotSlash) - 1
copy(b[n:], b[nn:])
b = b[:len(b)-nn+n]
}
// remove /foo/../ parts
for {
n := bytes.Index(b, strSlashDotDotSlash)
if n < 0 {
@@ -302,17 +313,6 @@ func normalizePath(dst, src []byte) []byte {
b = b[:len(b)-n+nn]
}
// remove /./ parts
for {
n := bytes.Index(b, strSlashDotSlash)
if n < 0 {
break
}
nn := n + len(strSlashDotSlash) - 1
copy(b[n:], b[nn:])
b = b[:len(b)-nn+n]
}
// remove trailing /foo/..
n := bytes.LastIndex(b, strSlashDotDot)
if n >= 0 && n+len(strSlashDotDot) == len(b) {
@@ -371,8 +371,7 @@ func (u *URI) LastPathSegment() []byte {
// * Relative path, i.e. xx?yy=abc . In this case the original RequestURI
// is updated according to the new relative path.
func (u *URI) Update(newURI string) {
u.fullURI = append(u.fullURI[:0], newURI...)
u.UpdateBytes(u.fullURI)
u.UpdateBytes(s2b(newURI))
}
// UpdateBytes updates uri.
@@ -409,22 +408,28 @@ func (u *URI) updateBytes(newURI, buf []byte) []byte {
}
// relative path
if newURI[0] == '?' {
switch newURI[0] {
case '?':
// query string only update
u.SetQueryStringBytes(newURI[1:])
return append(buf[:0], u.FullURI()...)
case '#':
// update only hash
u.SetHashBytes(newURI[1:])
return append(buf[:0], u.FullURI()...)
default:
// update the last path part after the slash
path := u.Path()
n = bytes.LastIndexByte(path, '/')
if n < 0 {
panic("BUG: path must contain at least one slash")
}
buf = u.appendSchemeHost(buf[:0])
buf = appendQuotedPath(buf, path[:n+1])
buf = append(buf, newURI...)
u.Parse(nil, buf)
return buf
}
path := u.Path()
n = bytes.LastIndexByte(path, '/')
if n < 0 {
panic("BUG: path must contain at least one slash")
}
buf = u.appendSchemeHost(buf[:0])
buf = appendQuotedPath(buf, path[:n+1])
buf = append(buf, newURI...)
u.Parse(nil, buf)
return buf
}
// FullURI returns full uri in the form {Scheme}://{Host}{RequestURI}#{Hash}.
+4
View File
@@ -109,6 +109,9 @@ func TestURIUpdate(t *testing.T) {
testURIUpdate(t, "http://foo.bar/baz", "~a/%20b=c,тест?йцу=ке", "http://foo.bar/~a/%20b=c,%D1%82%D0%B5%D1%81%D1%82?йцу=ке")
testURIUpdate(t, "http://foo.bar/baz", "/qwe#fragment", "http://foo.bar/qwe#fragment")
testURIUpdate(t, "http://foobar/baz/xxx", "aaa.html#bb?cc=dd&ee=dfd", "http://foobar/baz/aaa.html#bb?cc=dd&ee=dfd")
// hash
testURIUpdate(t, "http://foo.bar/baz#aaa", "#fragment", "http://foo.bar/baz#fragment")
}
func testURIUpdate(t *testing.T, base, update, result string) {
@@ -166,6 +169,7 @@ func TestURIPathNormalize(t *testing.T) {
testURIPathNormalize(t, &u, "/a/./b/././c/./d.html", "/a/b/c/d.html")
testURIPathNormalize(t, &u, "./foo/", "/foo/")
testURIPathNormalize(t, &u, "./../.././../../aaa/bbb/../../../././../", "/")
testURIPathNormalize(t, &u, "./a/./.././../b/./foo.html", "/b/foo.html")
}
func testURIPathNormalize(t *testing.T, u *URI, requestURI, expectedPath string) {