Fix bugs identified by the testsuites
* Handle resolving . and .. in path properly * Make the subtle distinction between empty and unset for the authority, fragment and query.
This commit is contained in:
@@ -30,7 +30,8 @@ BUrl::BUrl(const char* url)
|
|||||||
fPort(0),
|
fPort(0),
|
||||||
fPath(),
|
fPath(),
|
||||||
fRequest(),
|
fRequest(),
|
||||||
fHasAuthority(false)
|
fHasAuthority(false),
|
||||||
|
fHasFragment(false)
|
||||||
{
|
{
|
||||||
SetUrlString(url);
|
SetUrlString(url);
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,8 @@ BUrl::BUrl(BMessage* archive)
|
|||||||
fPort(0),
|
fPort(0),
|
||||||
fPath(),
|
fPath(),
|
||||||
fRequest(),
|
fRequest(),
|
||||||
fHasAuthority(false)
|
fHasAuthority(false),
|
||||||
|
fHasFragment(false)
|
||||||
{
|
{
|
||||||
BString url;
|
BString url;
|
||||||
|
|
||||||
@@ -68,7 +70,8 @@ BUrl::BUrl(const BUrl& other)
|
|||||||
fPort(0),
|
fPort(0),
|
||||||
fPath(),
|
fPath(),
|
||||||
fRequest(),
|
fRequest(),
|
||||||
fHasAuthority(false)
|
fHasAuthority(false),
|
||||||
|
fHasFragment(false)
|
||||||
{
|
{
|
||||||
*this = other;
|
*this = other;
|
||||||
}
|
}
|
||||||
@@ -84,24 +87,26 @@ BUrl::BUrl(const BUrl& base, const BString& location)
|
|||||||
fPort(0),
|
fPort(0),
|
||||||
fPath(),
|
fPath(),
|
||||||
fRequest(),
|
fRequest(),
|
||||||
fHasAuthority(false)
|
fHasAuthority(false),
|
||||||
|
fHasFragment(false)
|
||||||
{
|
{
|
||||||
// This implements the algorithm in RFC3986, Section 5.2.
|
// This implements the algorithm in RFC3986, Section 5.2.
|
||||||
|
|
||||||
BUrl relative(location);
|
BUrl relative(location);
|
||||||
if (relative.HasProtocol()) {
|
if (relative.HasProtocol()) {
|
||||||
SetProtocol(relative.Protocol());
|
SetProtocol(relative.Protocol());
|
||||||
SetAuthority(relative.Authority());
|
if (relative.HasAuthority())
|
||||||
SetPath(relative.Path()); // TODO _RemoveDotSegments()
|
SetAuthority(relative.Authority());
|
||||||
|
SetPath(relative.Path());
|
||||||
SetRequest(relative.Request());
|
SetRequest(relative.Request());
|
||||||
} else {
|
} else {
|
||||||
if (relative.HasAuthority()) {
|
if (relative.HasAuthority()) {
|
||||||
SetAuthority(relative.Authority());
|
SetAuthority(relative.Authority());
|
||||||
SetPath(relative.Path()); // TODO _RemoveDotSegments()
|
SetPath(relative.Path());
|
||||||
SetRequest(relative.Request());
|
SetRequest(relative.Request());
|
||||||
} else {
|
} else {
|
||||||
if (relative.Path().IsEmpty()) {
|
if (relative.Path().IsEmpty()) {
|
||||||
SetPath(base.Path());
|
_SetPathUnsafe(base.Path());
|
||||||
if (relative.HasRequest())
|
if (relative.HasRequest())
|
||||||
SetRequest(relative.Request());
|
SetRequest(relative.Request());
|
||||||
else
|
else
|
||||||
@@ -110,22 +115,20 @@ BUrl::BUrl(const BUrl& base, const BString& location)
|
|||||||
if (relative.Path()[0] == '/')
|
if (relative.Path()[0] == '/')
|
||||||
SetPath(relative.Path());
|
SetPath(relative.Path());
|
||||||
else {
|
else {
|
||||||
BString path = base.Path();
|
BString path = base._MergePath(relative.Path());
|
||||||
// Remove last part of path (the file, if any) so we get the
|
|
||||||
// "current directory"
|
|
||||||
path.Truncate(path.FindLast('/') + 1);
|
|
||||||
path += relative.Path();
|
|
||||||
// TODO _RemoveDotSegments()
|
|
||||||
SetPath(path);
|
SetPath(path);
|
||||||
}
|
}
|
||||||
SetRequest(relative.Request());
|
SetRequest(relative.Request());
|
||||||
}
|
}
|
||||||
SetAuthority(base.Authority());
|
|
||||||
|
if (base.HasAuthority())
|
||||||
|
SetAuthority(base.Authority());
|
||||||
}
|
}
|
||||||
SetProtocol(base.Protocol());
|
SetProtocol(base.Protocol());
|
||||||
}
|
}
|
||||||
|
|
||||||
SetFragment(relative.Fragment());
|
if (relative.HasFragment())
|
||||||
|
SetFragment(relative.Fragment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -139,7 +142,8 @@ BUrl::BUrl()
|
|||||||
fPort(0),
|
fPort(0),
|
||||||
fPath(),
|
fPath(),
|
||||||
fRequest(),
|
fRequest(),
|
||||||
fHasAuthority(false)
|
fHasAuthority(false),
|
||||||
|
fHasFragment(false)
|
||||||
{
|
{
|
||||||
_ResetFields();
|
_ResetFields();
|
||||||
}
|
}
|
||||||
@@ -220,9 +224,80 @@ BUrl::SetPort(int port)
|
|||||||
BUrl&
|
BUrl&
|
||||||
BUrl::SetPath(const BString& path)
|
BUrl::SetPath(const BString& path)
|
||||||
{
|
{
|
||||||
fPath = path;
|
// Implements RFC3986 section 5.2.4, "Remove dot segments"
|
||||||
fHasPath = true; // RFC says an empty path is still a path
|
|
||||||
fUrlStringValid = false;
|
// 1.
|
||||||
|
BString output;
|
||||||
|
BString input(path);
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
while(!input.IsEmpty())
|
||||||
|
{
|
||||||
|
// 2.A.
|
||||||
|
if (input.StartsWith("./"))
|
||||||
|
{
|
||||||
|
input.Remove(0, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.StartsWith("../"))
|
||||||
|
{
|
||||||
|
input.Remove(0, 3);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.B.
|
||||||
|
if (input.StartsWith("/./"))
|
||||||
|
{
|
||||||
|
input.Remove(0, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input == "/.")
|
||||||
|
{
|
||||||
|
input.Remove(1, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.C.
|
||||||
|
if (input.StartsWith("/../"))
|
||||||
|
{
|
||||||
|
input.Remove(0, 3);
|
||||||
|
output.Truncate(output.FindLast('/'));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input == "/..")
|
||||||
|
{
|
||||||
|
input.Remove(1, 2);
|
||||||
|
output.Truncate(output.FindLast('/'));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.D.
|
||||||
|
if (input == "." || input == "..")
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input == "/.")
|
||||||
|
{
|
||||||
|
input.Remove(1, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.E.
|
||||||
|
int slashpos = input.FindFirst('/', 1);
|
||||||
|
if (slashpos > 0) {
|
||||||
|
output.Append(input, slashpos);
|
||||||
|
input.Remove(0, slashpos);
|
||||||
|
} else {
|
||||||
|
output.Append(input);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_SetPathUnsafe(output);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +316,7 @@ BUrl&
|
|||||||
BUrl::SetFragment(const BString& fragment)
|
BUrl::SetFragment(const BString& fragment)
|
||||||
{
|
{
|
||||||
fFragment = fragment;
|
fFragment = fragment;
|
||||||
fHasFragment = !fFragment.IsEmpty();
|
fHasFragment = true;
|
||||||
fUrlStringValid = false;
|
fUrlStringValid = false;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -657,9 +732,13 @@ BUrl::_ExplodeUrlString(const BString& url)
|
|||||||
fHasProtocol = true;
|
fHasProtocol = true;
|
||||||
|
|
||||||
// Authority (including user credentials, host, and port
|
// Authority (including user credentials, host, and port
|
||||||
url.CopyInto(fAuthority, match.GroupStartOffsetAt(3),
|
if (match.GroupEndOffsetAt(2) - match.GroupStartOffsetAt(2) > 0)
|
||||||
match.GroupEndOffsetAt(3) - match.GroupStartOffsetAt(3));
|
{
|
||||||
SetAuthority(fAuthority);
|
url.CopyInto(fAuthority, match.GroupStartOffsetAt(3),
|
||||||
|
match.GroupEndOffsetAt(3) - match.GroupStartOffsetAt(3));
|
||||||
|
SetAuthority(fAuthority);
|
||||||
|
} else
|
||||||
|
fHasAuthority = false;
|
||||||
|
|
||||||
// Path
|
// Path
|
||||||
url.CopyInto(fPath, match.GroupStartOffsetAt(4),
|
url.CopyInto(fPath, match.GroupStartOffsetAt(4),
|
||||||
@@ -668,16 +747,56 @@ BUrl::_ExplodeUrlString(const BString& url)
|
|||||||
fHasPath = true;
|
fHasPath = true;
|
||||||
|
|
||||||
// Query
|
// Query
|
||||||
url.CopyInto(fRequest, match.GroupStartOffsetAt(6),
|
if (match.GroupEndOffsetAt(5) - match.GroupStartOffsetAt(5) > 0)
|
||||||
match.GroupEndOffsetAt(6) - match.GroupStartOffsetAt(6));
|
{
|
||||||
if (!fRequest.IsEmpty())
|
url.CopyInto(fRequest, match.GroupStartOffsetAt(6),
|
||||||
|
match.GroupEndOffsetAt(6) - match.GroupStartOffsetAt(6));
|
||||||
fHasRequest = true;
|
fHasRequest = true;
|
||||||
|
} else {
|
||||||
|
fRequest = "";
|
||||||
|
fHasRequest = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Fragment
|
// Fragment
|
||||||
url.CopyInto(fFragment, match.GroupStartOffsetAt(8),
|
if (match.GroupEndOffsetAt(7) - match.GroupStartOffsetAt(7) > 0)
|
||||||
match.GroupEndOffsetAt(8) - match.GroupStartOffsetAt(8));
|
{
|
||||||
if (!fFragment.IsEmpty())
|
url.CopyInto(fFragment, match.GroupStartOffsetAt(8),
|
||||||
|
match.GroupEndOffsetAt(8) - match.GroupStartOffsetAt(8));
|
||||||
fHasFragment = true;
|
fHasFragment = true;
|
||||||
|
} else {
|
||||||
|
fFragment = "";
|
||||||
|
fHasFragment = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
BUrl::_MergePath(const BString& relative) const
|
||||||
|
{
|
||||||
|
// This implements RFC3986, Section 5.2.3.
|
||||||
|
if (HasAuthority() && fPath == "")
|
||||||
|
{
|
||||||
|
BString result("/");
|
||||||
|
result << relative;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BString result(fPath);
|
||||||
|
result.Truncate(result.FindLast("/") + 1);
|
||||||
|
result << relative;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This sets the path without normalizing it. If fed with a path that has . or
|
||||||
|
// .. segments, this would make the URL invalid.
|
||||||
|
void
|
||||||
|
BUrl::_SetPathUnsafe(const BString& path)
|
||||||
|
{
|
||||||
|
fPath = path;
|
||||||
|
fHasPath = true; // RFC says an empty path is still a path
|
||||||
|
fUrlStringValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -690,10 +809,14 @@ BUrl::SetAuthority(const BString& authority)
|
|||||||
fHasUserInfo = false;
|
fHasUserInfo = false;
|
||||||
fHasHost = false;
|
fHasHost = false;
|
||||||
|
|
||||||
|
// An empty authority is still an authority, making it possible to have
|
||||||
|
// URLs such as file:///path/to/file.
|
||||||
|
// TODO however, there is no way to unset the authority once it is set...
|
||||||
|
// We may want to take a const char* parameter and allow NULL.
|
||||||
|
fHasAuthority = true;
|
||||||
if (fAuthority.IsEmpty())
|
if (fAuthority.IsEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fHasAuthority = true;
|
|
||||||
int32 userInfoEnd = fAuthority.FindFirst('@');
|
int32 userInfoEnd = fAuthority.FindFirst('@');
|
||||||
|
|
||||||
// URL contains userinfo field
|
// URL contains userinfo field
|
||||||
|
|||||||
Reference in New Issue
Block a user