make std::string optional
This commit is contained in:
parent
494447b731
commit
28bcbd3f35
@ -45,6 +45,20 @@ inline SizeType StrLen(const wchar_t* s) {
|
||||
return SizeType(std::wcslen(s));
|
||||
}
|
||||
|
||||
//! Custom strcmpn() which works on different character types.
|
||||
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
||||
\param s1 Null-terminated input string.
|
||||
\param s2 Null-terminated input string.
|
||||
\return 0 if equal
|
||||
*/
|
||||
template<typename Ch>
|
||||
inline int StrCmp(const Ch* s1, const Ch* s2) {
|
||||
RAPIDJSON_ASSERT(s1 != 0);
|
||||
RAPIDJSON_ASSERT(s2 != 0);
|
||||
while(*s1 && (*s1 == *s2)) { s1++; s2++; }
|
||||
return static_cast<unsigned>(*s1) < static_cast<unsigned>(*s2) ? -1 : static_cast<unsigned>(*s1) > static_cast<unsigned>(*s2);
|
||||
}
|
||||
|
||||
//! Returns number of code points in a encoded string.
|
||||
template<typename Encoding>
|
||||
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
|
||||
|
@ -554,8 +554,7 @@ public:
|
||||
// See if we have an id, and if so resolve with the current base
|
||||
typename ValueType::MemberIterator m = v->FindMember(kIdValue);
|
||||
if (m != v->MemberEnd() && (m->value).IsString()) {
|
||||
UriType here = UriType(m->value);
|
||||
here.Resolve(base);
|
||||
UriType here = UriType(m->value, allocator_).Resolve(base, allocator_);
|
||||
base = here;
|
||||
}
|
||||
m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
|
||||
@ -576,7 +575,7 @@ public:
|
||||
// Error: unresolved token
|
||||
if (unresolvedTokenIndex)
|
||||
*unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
|
||||
return UriType();
|
||||
return UriType(allocator_);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
@ -494,9 +494,8 @@ public:
|
||||
// If we have an id property, resolve it with the in-scope id
|
||||
if (const ValueType* v = GetMember(value, GetIdString())) {
|
||||
if (v->IsString()) {
|
||||
UriType local = UriType(*v);
|
||||
local.Resolve(id_);
|
||||
id_ = local;
|
||||
UriType local(*v, allocator);
|
||||
id_ = local.Resolve(id_, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1659,7 +1658,7 @@ public:
|
||||
|
||||
Ch noUri[1] = {0};
|
||||
uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
|
||||
docId_ = UriType(uri_);
|
||||
docId_ = UriType(uri_, allocator_);
|
||||
|
||||
typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
|
||||
new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
|
||||
@ -1792,8 +1791,7 @@ private:
|
||||
if (len > 0) {
|
||||
// First resolve $ref against the in-scope id
|
||||
UriType scopeId = id;
|
||||
UriType ref = UriType(itr->value);
|
||||
ref.Resolve(scopeId);
|
||||
UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
|
||||
// See if the resolved $ref minus the fragment matches a resolved id in this document
|
||||
// Search from the root. Returns the subschema in the document and its absolute JSON pointer.
|
||||
PointerType basePointer = PointerType();
|
||||
@ -1851,7 +1849,7 @@ private:
|
||||
// See if the fragment matches an id in this document.
|
||||
// Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer.
|
||||
PointerType pointer = PointerType();
|
||||
if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBase()), true, basePointer)) {
|
||||
if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
|
||||
if (!IsCyclicRef(pointer)) {
|
||||
//GenericStringBuffer<EncodingType> sb;
|
||||
//pointer.StringifyUriFragment(sb);
|
||||
@ -1887,8 +1885,7 @@ private:
|
||||
// Establish the base URI of this object
|
||||
typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString());
|
||||
if (m != doc.MemberEnd() && m->value.GetType() == kStringType) {
|
||||
localuri = UriType(m->value);
|
||||
localuri.Resolve(baseuri);
|
||||
localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_);
|
||||
}
|
||||
// See if it matches
|
||||
if (localuri.Match(finduri, full)) {
|
||||
|
@ -1,20 +1,22 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// (C) Copyright IBM Corporation 2021
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_URI_H_
|
||||
#define RAPIDJSON_URI_H_
|
||||
|
||||
#include "internal/strfunc.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
@ -27,222 +29,462 @@ RAPIDJSON_NAMESPACE_BEGIN
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericUri
|
||||
|
||||
template <typename ValueType, typename Allocator=CrtAllocator>
|
||||
class GenericUri {
|
||||
public:
|
||||
template <typename ValueType, typename Allocator=CrtAllocator>
|
||||
class GenericUri {
|
||||
public:
|
||||
typedef typename ValueType::Ch Ch;
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
typedef std::basic_string<Ch> String;
|
||||
#endif
|
||||
|
||||
// Constructors
|
||||
GenericUri() : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {}
|
||||
|
||||
GenericUri(const String& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {
|
||||
Parse(uri);
|
||||
//! Constructors
|
||||
GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
//std::cout << "Default constructor" << std::endl;
|
||||
}
|
||||
|
||||
GenericUri(const Ch* uri, SizeType len) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {
|
||||
Parse(String(uri, len));
|
||||
GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
Parse(uri, len);
|
||||
}
|
||||
|
||||
GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
Parse(uri, internal::StrLen<Ch>(uri));
|
||||
}
|
||||
|
||||
// Use with specializations of GenericValue
|
||||
template<typename T> GenericUri(const T& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {
|
||||
Parse(uri.template Get<String>());
|
||||
template<typename T> GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
const Ch* u = uri.template Get<const Ch*>(); // TypeHelper from document.h
|
||||
Parse(u, internal::StrLen<Ch>(u));
|
||||
}
|
||||
|
||||
// Getters
|
||||
const String& Get() const { return uri_; }
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
Parse(uri.c_str(), internal::StrLen<Ch>(uri.c_str()));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use with specializations of GenericValue
|
||||
template<typename T> void Get(T& uri, Allocator& allocator) {
|
||||
uri.template Set<String>(this->Get(), allocator);
|
||||
//! Copy constructor
|
||||
GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(rhs.allocator_), ownAllocator_() {
|
||||
//std::cout << "Copy constructor" << std::endl;
|
||||
*this = rhs;
|
||||
}
|
||||
|
||||
const String& GetBase() const { return base_; }
|
||||
const String& GetScheme() const { return scheme_; }
|
||||
const String& GetAuth() const { return auth_; }
|
||||
const String& GetPath() const { return path_; }
|
||||
const String& GetQuery() const { return query_; }
|
||||
const String& GetFrag() const { return frag_; }
|
||||
//! Copy constructor
|
||||
GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
//std::cout << "Copy constructor" << std::endl;
|
||||
*this = rhs;
|
||||
}
|
||||
|
||||
const Ch* GetString() const { return uri_.c_str(); }
|
||||
SizeType GetStringLength() const { return static_cast<SizeType>(uri_.length()); }
|
||||
//! Destructor.
|
||||
~GenericUri() {
|
||||
//std::cout << "Destructor" << std::endl;
|
||||
Free();
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
const Ch* GetBaseString() const { return base_.c_str(); }
|
||||
SizeType GetBaseStringLength() const { return static_cast<SizeType>(base_.length()); }
|
||||
|
||||
const Ch* GetFragString() const { return frag_.c_str(); }
|
||||
SizeType GetFragStringLength() const { return static_cast<SizeType>(frag_.length()); }
|
||||
|
||||
// Resolve this URI against another URI in accordance with URI resolution rules at
|
||||
// https://tools.ietf.org/html/rfc3986
|
||||
// Use for resolving an id or $ref with an in-scope id.
|
||||
// This URI is updated in place where needed from the base URI.
|
||||
GenericUri& Resolve(const GenericUri& uri) {
|
||||
if (!scheme_.empty()) {
|
||||
// Use all of this URI
|
||||
RemoveDotSegments(path_);
|
||||
} else {
|
||||
if (!auth_.empty()) {
|
||||
RemoveDotSegments(path_);
|
||||
} else {
|
||||
if (path_.empty()) {
|
||||
path_ = uri.GetPath();
|
||||
if (query_.empty()) {
|
||||
query_ = uri.GetQuery();
|
||||
}
|
||||
} else {
|
||||
static const String slash = GetSlashString().GetString();
|
||||
if (path_.find(slash) == 0) {
|
||||
// Absolute path - replace all the path
|
||||
RemoveDotSegments(path_);
|
||||
} else {
|
||||
// Relative path - append to path after last slash
|
||||
String p;
|
||||
if (!uri.GetAuth().empty() && uri.GetPath().empty()) p = slash;
|
||||
std::size_t lastslashpos = uri.GetPath().find_last_of(slash);
|
||||
path_ = p + uri.GetPath().substr(0, lastslashpos + 1) + path_;
|
||||
RemoveDotSegments(path_);
|
||||
}
|
||||
}
|
||||
auth_ = uri.GetAuth();
|
||||
}
|
||||
scheme_ = uri.GetScheme();
|
||||
//! Assignment operator
|
||||
GenericUri& operator=(const GenericUri& rhs) {
|
||||
//std::cout << "Operator=" << std::endl;
|
||||
if (this != &rhs) {
|
||||
// Do not delete ownAllocator
|
||||
Free();
|
||||
Allocate(rhs.GetStringLength());
|
||||
auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength());
|
||||
path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength());
|
||||
query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength());
|
||||
frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength());
|
||||
base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength());
|
||||
uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength());
|
||||
CopyPart(uri_, rhs.uri_, rhs.GetStringLength());
|
||||
//std::wcout << L" Assignment uri: " << uri_ << ", length: " << GetStringLength() << std::endl;
|
||||
}
|
||||
base_ = scheme_ + auth_ + path_ + query_;
|
||||
uri_ = base_ + frag_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Getters
|
||||
// Use with specializations of GenericValue
|
||||
template<typename T> void Get(T& uri, Allocator& allocator) {
|
||||
uri.template Set<const Ch*>(this->GetString(), allocator); // TypeHelper from document.h
|
||||
}
|
||||
|
||||
const Ch* GetString() const { return uri_; }
|
||||
SizeType GetStringLength() const { return internal::StrLen<Ch>(uri_); }
|
||||
const Ch* GetBaseString() const { return base_; }
|
||||
SizeType GetBaseStringLength() const { return internal::StrLen<Ch>(base_); }
|
||||
const Ch* GetSchemeString() const { return scheme_; }
|
||||
SizeType GetSchemeStringLength() const { return internal::StrLen<Ch>(scheme_); }
|
||||
const Ch* GetAuthString() const { return auth_; }
|
||||
SizeType GetAuthStringLength() const { return internal::StrLen<Ch>(auth_); }
|
||||
const Ch* GetPathString() const { return path_; }
|
||||
SizeType GetPathStringLength() const { return internal::StrLen<Ch>(path_); }
|
||||
const Ch* GetQueryString() const { return query_; }
|
||||
SizeType GetQueryStringLength() const { return internal::StrLen<Ch>(query_); }
|
||||
const Ch* GetFragString() const { return frag_; }
|
||||
SizeType GetFragStringLength() const { return internal::StrLen<Ch>(frag_); }
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); }
|
||||
static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); }
|
||||
static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); }
|
||||
static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); }
|
||||
static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); }
|
||||
static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); }
|
||||
static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); }
|
||||
#endif
|
||||
|
||||
//! Equality operators
|
||||
bool operator==(const GenericUri& rhs) const {
|
||||
return Match(rhs, true);
|
||||
}
|
||||
|
||||
bool operator!=(const GenericUri& rhs) const {
|
||||
return !Match(rhs, true);
|
||||
}
|
||||
|
||||
bool Match(const GenericUri& uri, bool full) const {
|
||||
if (full)
|
||||
return uri_ == uri.Get();
|
||||
else
|
||||
return base_ == uri.GetBase();
|
||||
Ch* s1;
|
||||
Ch* s2;
|
||||
if (full) {
|
||||
s1 = uri_;
|
||||
s2 = uri.uri_;
|
||||
} else {
|
||||
s1 = base_;
|
||||
s2 = uri.base_;
|
||||
}
|
||||
if (s1 == s2) return true;
|
||||
if (s1 == 0 || s2 == 0) return false;
|
||||
return internal::StrCmp<Ch>(s1, s2) == 0;
|
||||
}
|
||||
|
||||
// Generate functions for string literal according to Ch
|
||||
#define RAPIDJSON_STRING_(name, ...) \
|
||||
static const ValueType& Get##name##String() {\
|
||||
static const Ch s[] = { __VA_ARGS__, '\0' };\
|
||||
static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
|
||||
return v;\
|
||||
//! Resolve this URI against another (base) URI in accordance with URI resolution rules.
|
||||
// See https://tools.ietf.org/html/rfc3986
|
||||
// Use for resolving an id or $ref with an in-scope id.
|
||||
// Returns a new GenericUri for the resolved URI.
|
||||
GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) {
|
||||
//std::cout << "Resolve" << std::endl;
|
||||
GenericUri resuri;
|
||||
resuri.allocator_ = allocator;
|
||||
// Ensure enough space for combining paths
|
||||
resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash
|
||||
|
||||
if (!(GetSchemeStringLength() == 0)) {
|
||||
// Use all of this URI
|
||||
resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength());
|
||||
resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
|
||||
resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
||||
resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
||||
resuri.RemoveDotSegments();
|
||||
} else {
|
||||
// Use the base scheme
|
||||
resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength());
|
||||
if (!(GetAuthStringLength() == 0)) {
|
||||
// Use this auth, path, query
|
||||
resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
|
||||
resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
||||
resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
||||
resuri.RemoveDotSegments();
|
||||
} else {
|
||||
// Use the base auth
|
||||
resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength());
|
||||
if (GetPathStringLength() == 0) {
|
||||
// Use the base path
|
||||
resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength());
|
||||
if (GetQueryStringLength() == 0) {
|
||||
// Use the base query
|
||||
resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength());
|
||||
} else {
|
||||
// Use this query
|
||||
resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
||||
}
|
||||
} else {
|
||||
if (path_[0] == '/') {
|
||||
// Absolute path - use all of this path
|
||||
resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
||||
resuri.RemoveDotSegments();
|
||||
} else {
|
||||
// Relative path - append this path to base path after base path's last slash
|
||||
size_t pos = 0;
|
||||
if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) {
|
||||
resuri.path_[pos] = '/';
|
||||
pos++;
|
||||
}
|
||||
size_t lastslashpos = baseuri.GetPathStringLength();
|
||||
while (lastslashpos > 0) {
|
||||
if (baseuri.path_[lastslashpos - 1] == '/') break;
|
||||
lastslashpos--;
|
||||
}
|
||||
std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch));
|
||||
pos += lastslashpos;
|
||||
resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength());
|
||||
resuri.RemoveDotSegments();
|
||||
}
|
||||
// Use this query
|
||||
resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Always use this frag
|
||||
resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength());
|
||||
//std::wcout << L" Resolved uri: " << L"s: " << resuri.scheme_ << L" a: " << resuri.auth_ << L" p: " << resuri.path_ << L" q: " << resuri.query_ << L" f: " << resuri.frag_ << std::endl;
|
||||
|
||||
// Re-constitute base_ and uri_
|
||||
resuri.SetBase();
|
||||
resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1;
|
||||
resuri.SetUri();
|
||||
//std::wcout << L" Resolved rebuilt uri: " << resuri.uri_ << L", length: " << resuri.GetStringLength() << std::endl;
|
||||
return resuri;
|
||||
}
|
||||
|
||||
RAPIDJSON_STRING_(SchemeEnd, ':')
|
||||
RAPIDJSON_STRING_(AuthStart, '/', '/')
|
||||
RAPIDJSON_STRING_(QueryStart, '?')
|
||||
RAPIDJSON_STRING_(FragStart, '#')
|
||||
RAPIDJSON_STRING_(Slash, '/')
|
||||
RAPIDJSON_STRING_(Dot, '.')
|
||||
|
||||
#undef RAPIDJSON_STRING_
|
||||
//! Get the allocator of this GenericUri.
|
||||
Allocator& GetAllocator() { return *allocator_; }
|
||||
|
||||
private:
|
||||
// Parse a URI into constituent scheme, authority, path, query, fragment
|
||||
// Allocate memory for a URI
|
||||
// Returns total amount allocated
|
||||
std::size_t Allocate(std::size_t len) {
|
||||
//std::cout << "Allocate" << std::endl;
|
||||
// Create own allocator if user did not supply.
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||
|
||||
// Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated.
|
||||
// Order: scheme, auth, path, query, frag, base, uri
|
||||
size_t total = (3 * len + 7) * sizeof(Ch);
|
||||
scheme_ = static_cast<Ch*>(allocator_->Malloc(total));
|
||||
*scheme_ = '\0';
|
||||
auth_ = scheme_ + 1;
|
||||
*auth_ = '\0';
|
||||
path_ = auth_ + 1;
|
||||
*path_ = '\0';
|
||||
query_ = path_ + 1;
|
||||
*query_ = '\0';
|
||||
frag_ = query_ + 1;
|
||||
*frag_ = '\0';
|
||||
base_ = frag_ + 1;
|
||||
*base_ = '\0';
|
||||
uri_ = base_ + 1;
|
||||
*uri_ = '\0';
|
||||
//std::cout << " Allocating " << total << std::endl;
|
||||
return total;
|
||||
}
|
||||
|
||||
// Free memory for a URI
|
||||
void Free() {
|
||||
//std::cout << "Free" << std::endl;
|
||||
if (scheme_) {
|
||||
//std::cout << " Freeing" << std::endl;
|
||||
Allocator::Free(scheme_);
|
||||
scheme_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a URI into constituent scheme, authority, path, query, & fragment parts
|
||||
// Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per
|
||||
// https://tools.ietf.org/html/rfc3986
|
||||
void Parse(const String& uri) {
|
||||
void Parse(const Ch* uri, std::size_t len) {
|
||||
//std::cout << "Parse" << std::endl;
|
||||
std::size_t start = 0, pos1 = 0, pos2 = 0;
|
||||
const std::size_t len = uri.length();
|
||||
static const String schemeEnd = GetSchemeEndString().GetString();
|
||||
static const String authStart = GetAuthStartString().GetString();
|
||||
static const String pathStart = GetSlashString().GetString();
|
||||
static const String queryStart = GetQueryStartString().GetString();
|
||||
static const String fragStart = GetFragStartString().GetString();
|
||||
Allocate(len);
|
||||
|
||||
// Look for scheme ([^:/?#]+):)?
|
||||
if (start < len) {
|
||||
pos1 = uri.find(schemeEnd);
|
||||
if (pos1 != std::string::npos) {
|
||||
pos2 = uri.find_first_of(pathStart + queryStart + fragStart);
|
||||
while (pos1 < len) {
|
||||
if (uri[pos1] == ':') break;
|
||||
pos1++;
|
||||
}
|
||||
if (pos1 != len) {
|
||||
while (pos2 < len) {
|
||||
if (uri[pos2] == '/') break;
|
||||
if (uri[pos2] == '?') break;
|
||||
if (uri[pos2] == '#') break;
|
||||
pos2++;
|
||||
}
|
||||
if (pos1 < pos2) {
|
||||
pos1 += schemeEnd.length();
|
||||
scheme_ = uri.substr(start, pos1);
|
||||
pos1++;
|
||||
std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch));
|
||||
scheme_[pos1] = '\0';
|
||||
start = pos1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Look for auth (//([^/?#]*))?
|
||||
auth_ = scheme_ + GetSchemeStringLength() + 1;
|
||||
*auth_ = '\0';
|
||||
if (start < len) {
|
||||
pos1 = uri.find(authStart, start);
|
||||
pos1 = start;
|
||||
while (pos1 < len) {
|
||||
if (uri[pos1] == '/' && uri[pos1 + 1] == '/') break;
|
||||
pos1++;
|
||||
}
|
||||
if (pos1 == start) {
|
||||
pos2 = uri.find_first_of(pathStart + queryStart + fragStart, start + authStart.length());
|
||||
auth_ = uri.substr(start, pos2 - start);
|
||||
pos2 = start + 2;
|
||||
while (pos2 < len) {
|
||||
if (uri[pos2] == '/') break;
|
||||
if (uri[pos2] == '?') break;
|
||||
if (uri[pos2] == '#') break;
|
||||
pos2++;
|
||||
}
|
||||
std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch));
|
||||
auth_[pos2 - start] = '\0';
|
||||
start = pos2;
|
||||
}
|
||||
}
|
||||
// Look for path ([^?#]*)
|
||||
path_ = auth_ + GetAuthStringLength() + 1;
|
||||
*path_ = '\0';
|
||||
if (start < len) {
|
||||
pos2 = uri.find_first_of(queryStart + fragStart, start);
|
||||
pos2 = start;
|
||||
while (pos2 < len) {
|
||||
if (uri[pos2] == '?') break;
|
||||
if (uri[pos2] == '#') break;
|
||||
pos2++;
|
||||
}
|
||||
if (start != pos2) {
|
||||
path_ = uri.substr(start, pos2 - start);
|
||||
if (path_.find(pathStart) == 0) { // absolute path - normalize
|
||||
RemoveDotSegments(path_);
|
||||
}
|
||||
std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch));
|
||||
path_[pos2 - start] = '\0';
|
||||
if (path_[0] == '/')
|
||||
RemoveDotSegments(); // absolute path - normalize
|
||||
start = pos2;
|
||||
}
|
||||
}
|
||||
// Look for query (\?([^#]*))?
|
||||
query_ = path_ + GetPathStringLength() + 1;
|
||||
*query_ = '\0';
|
||||
if (start < len) {
|
||||
pos2 = uri.find(fragStart, start);
|
||||
pos2 = start;
|
||||
while (pos2 < len) {
|
||||
if (uri[pos2] == '#') break;
|
||||
pos2++;
|
||||
}
|
||||
if (start != pos2) {
|
||||
query_ = uri.substr(start, pos2 - start);
|
||||
std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch));
|
||||
query_[pos2 - start] = '\0';
|
||||
start = pos2;
|
||||
}
|
||||
}
|
||||
// Look for fragment (#(.*))?
|
||||
frag_ = query_ + GetQueryStringLength() + 1;
|
||||
*frag_ = '\0';
|
||||
if (start < len) {
|
||||
frag_ = uri.substr(start);
|
||||
std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch));
|
||||
frag_[len - start] = '\0';
|
||||
}
|
||||
base_ = scheme_ + auth_ + path_ + query_;
|
||||
uri_ = base_ + frag_;
|
||||
//std::wcout << L" Parsed uri: " << L"s: " << scheme_ << L" a: " << auth_ << L" p: " << path_ << L" q: " << query_ << L" f: " << frag_ << std::endl;
|
||||
|
||||
// Re-constitute base_ and uri_
|
||||
base_ = frag_ + GetFragStringLength() + 1;
|
||||
SetBase();
|
||||
uri_ = base_ + GetBaseStringLength() + 1;
|
||||
SetUri();
|
||||
//std::wcout << L" Rebuilt uri: " << uri_ << L", length: " << GetStringLength() << std::endl;
|
||||
}
|
||||
|
||||
// Remove . and .. segments from a path
|
||||
// Reconstitute base
|
||||
void SetBase() {
|
||||
Ch* next = base_;
|
||||
std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch));
|
||||
next+= GetSchemeStringLength();
|
||||
std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch));
|
||||
next+= GetAuthStringLength();
|
||||
std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch));
|
||||
next+= GetPathStringLength();
|
||||
std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch));
|
||||
next+= GetQueryStringLength();
|
||||
*next = '\0';
|
||||
}
|
||||
|
||||
// Reconstitute uri
|
||||
void SetUri() {
|
||||
Ch* next = uri_;
|
||||
std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch));
|
||||
next+= GetBaseStringLength();
|
||||
std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch));
|
||||
next+= GetFragStringLength();
|
||||
*next = '\0';
|
||||
}
|
||||
|
||||
// Copy a part from one GenericUri to another
|
||||
// Return the pointer to the next part to be copied to
|
||||
Ch* CopyPart(Ch* to, Ch* from, std::size_t len) {
|
||||
RAPIDJSON_ASSERT(to != 0);
|
||||
RAPIDJSON_ASSERT(from != 0);
|
||||
std::memcpy(to, from, len * sizeof(Ch));
|
||||
to[len] = '\0';
|
||||
Ch* next = to + len + 1;
|
||||
return next;
|
||||
}
|
||||
|
||||
// Remove . and .. segments from the path_ member.
|
||||
// https://tools.ietf.org/html/rfc3986
|
||||
void RemoveDotSegments(String& path) {
|
||||
String temp = path;
|
||||
path.clear();
|
||||
static const String slash = GetSlashString().GetString();
|
||||
static const String dot = GetDotString().GetString();
|
||||
std::size_t pos = 0;
|
||||
// Loop through each path segment
|
||||
while (pos != std::string::npos) {
|
||||
pos = temp.find_first_of(slash);
|
||||
// Get next segment
|
||||
String seg = temp.substr(0, pos);
|
||||
if (seg == dot) {
|
||||
// Discard . segment
|
||||
} else if (seg == dot + dot) {
|
||||
// Backup a .. segment
|
||||
// We expect to find a previously added slash at the end or nothing
|
||||
std::size_t pos1 = path.find_last_of(slash);
|
||||
// Make sure we don't go beyond the start
|
||||
if (pos1 != std::string::npos && pos1 != 0) {
|
||||
// Find the next to last slash and back up to it
|
||||
pos1 = path.find_last_of(slash, pos1 - 1);
|
||||
path = path.substr(0, pos1 + 1);
|
||||
}
|
||||
} else {
|
||||
// Copy segment and add slash if not at end
|
||||
path += seg;
|
||||
if (pos != std::string::npos) path += slash;
|
||||
// This is done in place as we are only removing segments.
|
||||
void RemoveDotSegments() {
|
||||
std::size_t pathlen = GetPathStringLength();
|
||||
std::size_t pathpos = 0; // Position in path_
|
||||
std::size_t newpos = 0; // Position in new path_
|
||||
|
||||
// Loop through each segment in original path_
|
||||
while (pathpos < pathlen) {
|
||||
// Get next segment, bounded by '/' or end
|
||||
size_t slashpos = 0;
|
||||
while ((pathpos + slashpos) < pathlen) {
|
||||
if (path_[pathpos + slashpos] == '/') break;
|
||||
slashpos++;
|
||||
}
|
||||
// Move to next segment if not at end
|
||||
if (pos != std::string::npos) temp = temp.substr(pos + 1);
|
||||
// Check for .. and . segments
|
||||
if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') {
|
||||
// Backup a .. segment in the new path_
|
||||
// We expect to find a previously added slash at the end or nothing
|
||||
//std::wcout << L" Path - backing up .. in " << path_ << std::endl;
|
||||
size_t lastslashpos = newpos;
|
||||
while (lastslashpos > 0) {
|
||||
if (path_[lastslashpos - 1] == '/') break;
|
||||
lastslashpos--;
|
||||
}
|
||||
// Make sure we don't go beyond the start segment
|
||||
if (lastslashpos > 1) {
|
||||
// Find the next to last slash and back up to it
|
||||
lastslashpos--;
|
||||
while (lastslashpos > 0) {
|
||||
if (path_[lastslashpos - 1] == '/') break;
|
||||
lastslashpos--;
|
||||
}
|
||||
// Set the new path_ position
|
||||
newpos = lastslashpos;
|
||||
}
|
||||
} else if (slashpos == 1 && path_[pathpos] == '.') {
|
||||
// Discard . segment, leaves new path_ unchanged
|
||||
//std::wcout << L" Path - removing . in " << path_ << std::endl;
|
||||
} else {
|
||||
// Move any other kind of segment to the new path_
|
||||
RAPIDJSON_ASSERT(newpos <= pathpos);
|
||||
std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch));
|
||||
newpos += slashpos;
|
||||
// Add slash if not at end
|
||||
if ((pathpos + slashpos) < pathlen) {
|
||||
path_[newpos] = '/';
|
||||
newpos++;
|
||||
}
|
||||
}
|
||||
// Move to next segment
|
||||
pathpos += slashpos + 1;
|
||||
}
|
||||
path_[newpos] = '\0';
|
||||
//std::wcout << L" Normalized Path: " << path_ << ", length: " << GetPathStringLength() << std::endl;
|
||||
}
|
||||
|
||||
String uri_; // Full uri
|
||||
String base_; // Everything except fragment
|
||||
String scheme_; // Includes the :
|
||||
String auth_; // Includes the //
|
||||
String path_; // Absolute if starts with /
|
||||
String query_; // Includes the ?
|
||||
String frag_; // Includes the #
|
||||
};
|
||||
Ch* uri_; // Everything
|
||||
Ch* base_; // Everything except fragment
|
||||
Ch* scheme_; // Includes the :
|
||||
Ch* auth_; // Includes the //
|
||||
Ch* path_; // Absolute if starts with /
|
||||
Ch* query_; // Includes the ?
|
||||
Ch* frag_; // Includes the #
|
||||
|
||||
Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
|
||||
Allocator* ownAllocator_; //!< Allocator owned by this Uri.
|
||||
};
|
||||
|
||||
//! GenericUri for Value (UTF-8, default allocator).
|
||||
typedef GenericUri<Value> Uri;
|
||||
typedef GenericUri<Value> Uri;
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
|
@ -659,36 +659,37 @@ static const char kJsonIds[] = "{\n"
|
||||
|
||||
|
||||
TEST(Pointer, GetUri) {
|
||||
typedef std::basic_string<Value::Ch> String;
|
||||
Document d;
|
||||
d.Parse(kJsonIds);
|
||||
Pointer::UriType doc("http://doc");
|
||||
Pointer::UriType root("http://doc/root/");
|
||||
Pointer::UriType empty = Pointer::UriType();
|
||||
|
||||
String doc = String("http://doc");
|
||||
EXPECT_TRUE((Pointer("").GetUri(d, Pointer::UriType(doc)).Get()) == doc);
|
||||
EXPECT_TRUE((Pointer("/foo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||
EXPECT_TRUE((Pointer("/foo/0").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||
EXPECT_TRUE((Pointer("/foo/2").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||
EXPECT_TRUE((Pointer("/foo/2/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/inarray");
|
||||
EXPECT_TRUE((Pointer("/int").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||
EXPECT_TRUE((Pointer("/str").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||
EXPECT_TRUE((Pointer("/obj").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||
EXPECT_TRUE((Pointer("/obj/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/inobj");
|
||||
EXPECT_TRUE((Pointer("/jbo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||
EXPECT_TRUE((Pointer("/jbo/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); // id not string
|
||||
EXPECT_TRUE(Pointer("").GetUri(d, doc) == doc);
|
||||
EXPECT_TRUE(Pointer("/foo").GetUri(d, doc) == root);
|
||||
EXPECT_TRUE(Pointer("/foo/0").GetUri(d, doc) == root);
|
||||
EXPECT_TRUE(Pointer("/foo/2").GetUri(d, doc) == root);
|
||||
EXPECT_TRUE(Pointer("/foo/2/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inarray"));
|
||||
EXPECT_TRUE(Pointer("/int").GetUri(d, doc) == root);
|
||||
EXPECT_TRUE(Pointer("/str").GetUri(d, doc) == root);
|
||||
EXPECT_TRUE(Pointer("/obj").GetUri(d, doc) == root);
|
||||
EXPECT_TRUE(Pointer("/obj/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inobj"));
|
||||
EXPECT_TRUE(Pointer("/jbo").GetUri(d, doc) == root);
|
||||
EXPECT_TRUE(Pointer("/jbo/child").GetUri(d, doc) == root); // id not string
|
||||
|
||||
EXPECT_TRUE((Pointer("/abc").GetUri(d, Pointer::UriType(doc)).Get()) == Pointer::UriType().Get()); // Out of boundary
|
||||
EXPECT_TRUE(Pointer("/abc").GetUri(d, doc) == empty); // Out of boundary
|
||||
size_t unresolvedTokenIndex;
|
||||
EXPECT_TRUE((Pointer("/foo/3").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // Out of boundary
|
||||
EXPECT_TRUE(Pointer("/foo/3").GetUri(d, doc, &unresolvedTokenIndex) == empty); // Out of boundary
|
||||
EXPECT_EQ(1u, unresolvedTokenIndex);
|
||||
EXPECT_TRUE((Pointer("/foo/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo" is an array, cannot query by "a"
|
||||
EXPECT_TRUE(Pointer("/foo/a").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo" is an array, cannot query by "a"
|
||||
EXPECT_EQ(1u, unresolvedTokenIndex);
|
||||
EXPECT_TRUE((Pointer("/foo/0/0").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo/0" is an string, cannot further query
|
||||
EXPECT_TRUE(Pointer("/foo/0/0").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo/0" is an string, cannot further query
|
||||
EXPECT_EQ(2u, unresolvedTokenIndex);
|
||||
EXPECT_TRUE((Pointer("/foo/0/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo/0" is an string, cannot further query
|
||||
EXPECT_TRUE(Pointer("/foo/0/a").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo/0" is an string, cannot further query
|
||||
EXPECT_EQ(2u, unresolvedTokenIndex);
|
||||
|
||||
Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } };
|
||||
EXPECT_TRUE((Pointer(tokens, 1).GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||
EXPECT_TRUE(Pointer(tokens, 1).GetUri(d, doc) == root);
|
||||
}
|
||||
|
||||
TEST(Pointer, Get) {
|
||||
|
@ -1,15 +1,15 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// (C) Copyright IBM Corporation 2021
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#define RAPIDJSON_SCHEMA_VERBOSE 0
|
||||
@ -30,247 +30,593 @@ RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body
|
||||
using namespace rapidjson;
|
||||
|
||||
TEST(Uri, Parse) {
|
||||
typedef std::basic_string<Value::Ch> String;
|
||||
typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
|
||||
MemoryPoolAllocator<CrtAllocator> allocator;
|
||||
typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
|
||||
MemoryPoolAllocator<CrtAllocator> allocator;
|
||||
Value v;
|
||||
Value w;
|
||||
|
||||
String s = "http://auth/path?query#frag";
|
||||
Value v;
|
||||
v.SetString(s, allocator);
|
||||
UriType u = UriType(v);
|
||||
EXPECT_TRUE(u.GetScheme() == "http:");
|
||||
EXPECT_TRUE(u.GetAuth() == "//auth");
|
||||
EXPECT_TRUE(u.GetPath() == "/path");
|
||||
EXPECT_TRUE(u.GetBase() == "http://auth/path?query");
|
||||
EXPECT_TRUE(u.GetQuery() == "?query");
|
||||
EXPECT_TRUE(u.GetFrag() == "#frag");
|
||||
Value w;
|
||||
u.Get(w, allocator);
|
||||
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
||||
v.SetString("http://auth/path/xxx?query#frag", allocator);
|
||||
UriType u = UriType(v, &allocator);
|
||||
EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/xxx") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/path/xxx?query") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetQueryString(), "?query") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag") == 0);
|
||||
u.Get(w, allocator);
|
||||
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
||||
|
||||
s = "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f";
|
||||
v.SetString(s, allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(u.GetScheme() == "urn:");
|
||||
EXPECT_TRUE(u.GetAuth() == "");
|
||||
EXPECT_TRUE(u.GetPath() == "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
||||
EXPECT_TRUE(u.GetBase() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
||||
EXPECT_TRUE(u.GetQuery() == "");
|
||||
EXPECT_TRUE(u.GetFrag() == "");
|
||||
u.Get(w, allocator);
|
||||
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
typedef std::basic_string<Value::Ch> String;
|
||||
String str = "http://auth/path/xxx?query#frag";
|
||||
const UriType uri = UriType(str);
|
||||
EXPECT_TRUE(UriType::GetScheme(uri) == "http:");
|
||||
EXPECT_TRUE(UriType::GetAuth(uri) == "//auth");
|
||||
EXPECT_TRUE(UriType::GetPath(uri) == "/path/xxx");
|
||||
EXPECT_TRUE(UriType::GetBase(uri) == "http://auth/path/xxx?query");
|
||||
EXPECT_TRUE(UriType::GetQuery(uri) == "?query");
|
||||
EXPECT_TRUE(UriType::GetFrag(uri) == "#frag");
|
||||
EXPECT_TRUE(UriType::Get(uri) == str);
|
||||
#endif
|
||||
|
||||
s = "";
|
||||
v.SetString(s, allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(u.GetScheme() == "");
|
||||
EXPECT_TRUE(u.GetAuth() == "");
|
||||
EXPECT_TRUE(u.GetPath() == "");
|
||||
EXPECT_TRUE(u.GetBase() == "");
|
||||
EXPECT_TRUE(u.GetQuery() == "");
|
||||
EXPECT_TRUE(u.GetFrag() == "");
|
||||
v.SetString("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(StrCmp(u.GetSchemeString(), "urn:") == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
u.Get(w, allocator);
|
||||
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
||||
|
||||
s = "http://auth/";
|
||||
v.SetString(s, allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(u.GetScheme() == "http:");
|
||||
EXPECT_TRUE(u.GetAuth() == "//auth");
|
||||
EXPECT_TRUE(u.GetPath() == "/");
|
||||
EXPECT_TRUE(u.GetBase() == "http://auth/");
|
||||
EXPECT_TRUE(u.GetQuery() == "");
|
||||
EXPECT_TRUE(u.GetFrag() == "");
|
||||
v.SetString("", allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(u.GetSchemeStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetPathStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetBaseStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
s = "/path/sub";
|
||||
u = UriType(s);
|
||||
EXPECT_TRUE(u.GetScheme() == "");
|
||||
EXPECT_TRUE(u.GetAuth() == "");
|
||||
EXPECT_TRUE(u.GetPath() == "/path/sub");
|
||||
EXPECT_TRUE(u.GetBase() == "/path/sub");
|
||||
EXPECT_TRUE(u.GetQuery() == "");
|
||||
EXPECT_TRUE(u.GetFrag() == "");
|
||||
v.SetString("http://auth/", allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), "/") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
// absolute path gets normalized
|
||||
s = "/path/../sub/";
|
||||
u = UriType(s);
|
||||
EXPECT_TRUE(u.GetScheme() == "");
|
||||
EXPECT_TRUE(u.GetAuth() == "");
|
||||
EXPECT_TRUE(u.GetPath() == "/sub/");
|
||||
EXPECT_TRUE(u.GetBase() == "/sub/");
|
||||
EXPECT_TRUE(u.GetQuery() == "");
|
||||
EXPECT_TRUE(u.GetFrag() == "");
|
||||
u = UriType("/path/sub");
|
||||
EXPECT_TRUE(u.GetSchemeStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/sub") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "/path/sub") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
// relative path does not
|
||||
s = "path/../sub";
|
||||
u = UriType(s);
|
||||
EXPECT_TRUE(u.GetScheme() == "");
|
||||
EXPECT_TRUE(u.GetAuth() == "");
|
||||
EXPECT_TRUE(u.GetPath() == "path/../sub");
|
||||
EXPECT_TRUE(u.GetBase() == "path/../sub");
|
||||
EXPECT_TRUE(u.GetQuery() == "");
|
||||
EXPECT_TRUE(u.GetFrag() == "");
|
||||
// absolute path gets normalized
|
||||
u = UriType("/path/../sub/");
|
||||
EXPECT_TRUE(u.GetSchemeStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), "/sub/") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "/sub/") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
s = "http://auth#frag/stuff";
|
||||
u = UriType(s);
|
||||
EXPECT_TRUE(u.GetScheme() == "http:");
|
||||
EXPECT_TRUE(u.GetAuth() == "//auth");
|
||||
EXPECT_TRUE(u.GetPath() == "");
|
||||
EXPECT_TRUE(u.GetBase() == "http://auth");
|
||||
EXPECT_TRUE(u.GetQuery() == "");
|
||||
EXPECT_TRUE(u.GetFrag() == "#frag/stuff");
|
||||
EXPECT_TRUE(u.Get() == s);
|
||||
// relative path does not
|
||||
u = UriType("path/../sub");
|
||||
EXPECT_TRUE(u.GetSchemeStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), "path/../sub") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "path/../sub") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
s = "#frag/stuff";
|
||||
u = UriType(s);
|
||||
EXPECT_TRUE(u.GetScheme() == "");
|
||||
EXPECT_TRUE(u.GetAuth() == "");
|
||||
EXPECT_TRUE(u.GetPath() == "");
|
||||
EXPECT_TRUE(u.GetBase() == "");
|
||||
EXPECT_TRUE(u.GetQuery() == "");
|
||||
EXPECT_TRUE(u.GetFrag() == "#frag/stuff");
|
||||
EXPECT_TRUE(u.Get() == s);
|
||||
u = UriType("http://auth#frag/stuff");
|
||||
EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
|
||||
EXPECT_TRUE(u.GetPathStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetString(), "http://auth#frag/stuff") == 0);
|
||||
|
||||
Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
|
||||
u = UriType(c, 11);
|
||||
EXPECT_TRUE(String(u.GetString()) == "#frag/stuff");
|
||||
EXPECT_TRUE(u.GetStringLength() == 11);
|
||||
EXPECT_TRUE(String(u.GetBaseString()) == "");
|
||||
EXPECT_TRUE(u.GetBaseStringLength() == 0);
|
||||
EXPECT_TRUE(String(u.GetFragString()) == "#frag/stuff");
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 11);
|
||||
const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
|
||||
SizeType len = internal::StrLen<Value::Ch>(c);
|
||||
u = UriType(c, len);
|
||||
EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0);
|
||||
EXPECT_TRUE(u.GetStringLength() == len);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0);
|
||||
EXPECT_TRUE(u.GetBaseStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == len);
|
||||
|
||||
u = UriType(c);
|
||||
EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0);
|
||||
EXPECT_TRUE(u.GetStringLength() == len);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0);
|
||||
EXPECT_TRUE(u.GetBaseStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == len);
|
||||
}
|
||||
|
||||
TEST(Uri, Parse_UTF16) {
|
||||
typedef GenericValue<UTF16<> > Value;
|
||||
typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
|
||||
MemoryPoolAllocator<CrtAllocator> allocator;
|
||||
Value v;
|
||||
Value w;
|
||||
|
||||
v.SetString(L"http://auth/path/xxx?query#frag", allocator);
|
||||
UriType u = UriType(v, &allocator);
|
||||
EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/xxx") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/path/xxx?query") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetQueryString(), L"?query") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag") == 0);
|
||||
u.Get(w, allocator);
|
||||
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
typedef std::basic_string<Value::Ch> String;
|
||||
String str = L"http://auth/path/xxx?query#frag";
|
||||
const UriType uri = UriType(str);
|
||||
EXPECT_TRUE(UriType::GetScheme(uri) == L"http:");
|
||||
EXPECT_TRUE(UriType::GetAuth(uri) == L"//auth");
|
||||
EXPECT_TRUE(UriType::GetPath(uri) == L"/path/xxx");
|
||||
EXPECT_TRUE(UriType::GetBase(uri) == L"http://auth/path/xxx?query");
|
||||
EXPECT_TRUE(UriType::GetQuery(uri) == L"?query");
|
||||
EXPECT_TRUE(UriType::GetFrag(uri) == L"#frag");
|
||||
EXPECT_TRUE(UriType::Get(uri) == str);
|
||||
#endif
|
||||
|
||||
v.SetString(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"urn:") == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), L"uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
u.Get(w, allocator);
|
||||
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
||||
|
||||
v.SetString(L"", allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(u.GetSchemeStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetPathStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetBaseStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
v.SetString(L"http://auth/", allocator);
|
||||
u = UriType(v);
|
||||
EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
u = UriType(L"/path/sub");
|
||||
EXPECT_TRUE(u.GetSchemeStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/sub") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/path/sub") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
// absolute path gets normalized
|
||||
u = UriType(L"/path/../sub/");
|
||||
EXPECT_TRUE(u.GetSchemeStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), L"/sub/") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/sub/") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
// relative path does not
|
||||
u = UriType(L"path/../sub");
|
||||
EXPECT_TRUE(u.GetSchemeStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetAuthStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetPathString(), L"path/../sub") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"path/../sub") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == 0);
|
||||
|
||||
u = UriType(L"http://auth#frag/stuff");
|
||||
EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
|
||||
EXPECT_TRUE(u.GetPathStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth") == 0);
|
||||
EXPECT_TRUE(u.GetQueryStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetString(), L"http://auth#frag/stuff") == 0);
|
||||
|
||||
const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
|
||||
SizeType len = internal::StrLen<Value::Ch>(c);
|
||||
u = UriType(c, len);
|
||||
EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0);
|
||||
EXPECT_TRUE(u.GetStringLength() == len);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0);
|
||||
EXPECT_TRUE(u.GetBaseStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == len);
|
||||
|
||||
u = UriType(c);
|
||||
EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0);
|
||||
EXPECT_TRUE(u.GetStringLength() == len);
|
||||
EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0);
|
||||
EXPECT_TRUE(u.GetBaseStringLength() == 0);
|
||||
EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
|
||||
EXPECT_TRUE(u.GetFragStringLength() == len);
|
||||
}
|
||||
|
||||
TEST(Uri, Resolve) {
|
||||
typedef std::basic_string<Value::Ch> String;
|
||||
typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
|
||||
typedef GenericUri<Value> UriType;
|
||||
CrtAllocator allocator;
|
||||
|
||||
// ref is full uri
|
||||
UriType base = UriType(String("http://auth/path/#frag"));
|
||||
UriType ref = UriType(String("http://newauth/newpath#newfrag"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag");
|
||||
// ref is full uri
|
||||
UriType base = UriType("http://auth/path/#frag");
|
||||
UriType ref = UriType("http://newauth/newpath#newfrag");
|
||||
UriType res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0);
|
||||
|
||||
base = UriType(String("/path/#frag"));
|
||||
ref = UriType(String("http://newauth/newpath#newfrag"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag");
|
||||
base = UriType("/path/#frag", &allocator);
|
||||
ref = UriType("http://newauth/newpath#newfrag", &allocator);
|
||||
res = ref.Resolve(base, &allocator);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0);
|
||||
|
||||
// ref is alternate uri
|
||||
base = UriType(String("http://auth/path/#frag"));
|
||||
ref = UriType(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
||||
// ref is alternate uri
|
||||
base = UriType("http://auth/path/#frag");
|
||||
ref = UriType("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
|
||||
|
||||
// ref is absolute path
|
||||
base = UriType(String("http://auth/path/#"));
|
||||
ref = UriType(String("/newpath#newfrag"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newpath#newfrag");
|
||||
// ref is absolute path
|
||||
base = UriType("http://auth/path/#");
|
||||
ref = UriType("/newpath#newfrag");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newpath#newfrag") == 0);
|
||||
|
||||
// ref is relative path
|
||||
base = UriType(String("http://auth/path/file.json#frag"));
|
||||
ref = UriType(String("newfile.json#"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#");
|
||||
// ref is relative path
|
||||
base = UriType("http://auth/path/file.json#frag");
|
||||
ref = UriType("newfile.json#");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#") == 0);
|
||||
|
||||
base = UriType(String("http://auth/path/file.json#frag/stuff"));
|
||||
ref = UriType(String("newfile.json#newfrag/newstuff"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#newfrag/newstuff");
|
||||
base = UriType("http://auth/path/file.json#frag/stuff");
|
||||
ref = UriType("newfile.json#newfrag/newstuff");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#newfrag/newstuff") == 0);
|
||||
|
||||
base = UriType(String("file.json"));
|
||||
ref = UriType(String("newfile.json"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
||||
base = UriType("file.json", &allocator);
|
||||
ref = UriType("newfile.json", &base.GetAllocator());
|
||||
res = ref.Resolve(base, &ref.GetAllocator());
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
|
||||
|
||||
base = UriType(String("file.json"));
|
||||
ref = UriType(String("./newfile.json"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
||||
base = UriType("file.json", &allocator);
|
||||
ref = UriType("./newfile.json", &allocator);
|
||||
res = ref.Resolve(base, &allocator);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
|
||||
|
||||
base = UriType(String("file.json"));
|
||||
ref = UriType(String("parent/../newfile.json"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
||||
base = UriType("file.json");
|
||||
ref = UriType("parent/../newfile.json");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
|
||||
|
||||
base = UriType(String("file.json"));
|
||||
ref = UriType(String("parent/./newfile.json"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json");
|
||||
base = UriType("file.json");
|
||||
ref = UriType("parent/./newfile.json");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "parent/newfile.json") == 0);
|
||||
|
||||
base = UriType(String("file.json"));
|
||||
ref = UriType(String("../../parent/.././newfile.json"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
||||
base = UriType("file.json");
|
||||
ref = UriType("../../parent/.././newfile.json");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
|
||||
|
||||
base = UriType(String("http://auth"));
|
||||
ref = UriType(String("newfile.json"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newfile.json");
|
||||
// This adds a joining slash so resolved length is base length + ref length + 1
|
||||
base = UriType("http://auth");
|
||||
ref = UriType("newfile.json");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newfile.json") == 0);
|
||||
|
||||
// ref is fragment
|
||||
base = UriType(String("#frag/stuff"));
|
||||
ref = UriType(String("#newfrag/newstuff"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff");
|
||||
// ref is fragment
|
||||
base = UriType("#frag/stuff");
|
||||
ref = UriType("#newfrag/newstuff");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "#newfrag/newstuff") == 0);
|
||||
|
||||
// test ref fragment always wins
|
||||
base = UriType(String("/path#frag"));
|
||||
ref = UriType(String(""));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "/path");
|
||||
// test ref fragment always wins
|
||||
base = UriType("/path#frag");
|
||||
ref = UriType("");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "/path") == 0);
|
||||
|
||||
// Examples from RFC3896
|
||||
base = UriType(String("http://a/b/c/d;p?q"));
|
||||
ref = UriType(String("g:h"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "g:h");
|
||||
ref = UriType(String("g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g");
|
||||
ref = UriType(String("./g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g");
|
||||
ref = UriType(String("g/"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g/");
|
||||
ref = UriType(String("/g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||
ref = UriType(String("//g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://g");
|
||||
ref = UriType(String("?y"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?y");
|
||||
ref = UriType(String("g?y"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y");
|
||||
ref = UriType(String("#s"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q#s");
|
||||
ref = UriType(String("g#s"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s");
|
||||
ref = UriType(String("g?y#s"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y#s");
|
||||
ref = UriType(String(";x"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/;x");
|
||||
ref = UriType(String("g;x"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x");
|
||||
ref = UriType(String("g;x?y#s"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x?y#s");
|
||||
ref = UriType(String(""));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q");
|
||||
ref = UriType(String("."));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/");
|
||||
ref = UriType(String("./"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/");
|
||||
ref = UriType(String(".."));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/");
|
||||
ref = UriType(String("../"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/");
|
||||
ref = UriType(String("../g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/g");
|
||||
ref = UriType(String("../.."));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/");
|
||||
ref = UriType(String("../../"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/");
|
||||
ref = UriType(String("../../g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||
ref = UriType(String("../../../g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||
ref = UriType(String("../../../../g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||
ref = UriType(String("/./g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||
ref = UriType(String("/../g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||
ref = UriType(String("g."));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g.");
|
||||
ref = UriType(String(".g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/.g");
|
||||
ref = UriType(String("g.."));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g..");
|
||||
ref = UriType(String("..g"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/..g");
|
||||
ref = UriType(String("g#s/../x"));
|
||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s/../x");
|
||||
// Examples from RFC3896
|
||||
base = UriType("http://a/b/c/d;p?q");
|
||||
ref = UriType("g:h");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "g:h") == 0);
|
||||
ref = UriType("g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0);
|
||||
ref = UriType("./g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0);
|
||||
ref = UriType("g/");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g/") == 0);
|
||||
ref = UriType("/g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
|
||||
ref = UriType("//g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://g") == 0);
|
||||
ref = UriType("?y");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?y") == 0);
|
||||
ref = UriType("g?y");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y") == 0);
|
||||
ref = UriType("#s");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q#s") == 0);
|
||||
ref = UriType("g#s");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s") == 0);
|
||||
ref = UriType("g?y#s");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y#s") == 0);
|
||||
ref = UriType(";x");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/;x") == 0);
|
||||
ref = UriType("g;x");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x") == 0);
|
||||
ref = UriType("g;x?y#s");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x?y#s") == 0);
|
||||
ref = UriType("");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q") == 0);
|
||||
ref = UriType(".");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0);
|
||||
ref = UriType("./");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0);
|
||||
ref = UriType("..");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0);
|
||||
ref = UriType("../");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0);
|
||||
ref = UriType("../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/g") == 0);
|
||||
ref = UriType("../..");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0);
|
||||
ref = UriType("../../");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0);
|
||||
ref = UriType("../../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
|
||||
ref = UriType("../../../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
|
||||
ref = UriType("../../../../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
|
||||
ref = UriType("/./g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
|
||||
ref = UriType("/../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
|
||||
ref = UriType("g.");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g.") == 0);
|
||||
ref = UriType(".g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/.g") == 0);
|
||||
ref = UriType("g..");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g..") == 0);
|
||||
ref = UriType("..g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/..g") == 0);
|
||||
ref = UriType("g#s/../x");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s/../x") == 0);
|
||||
}
|
||||
|
||||
TEST(Uri, Resolve_UTF16) {
|
||||
typedef GenericValue<UTF16<> > Value;
|
||||
typedef GenericUri<Value> UriType;
|
||||
CrtAllocator allocator;
|
||||
|
||||
// ref is full uri
|
||||
UriType base = UriType(L"http://auth/path/#frag");
|
||||
UriType ref = UriType(L"http://newauth/newpath#newfrag");
|
||||
UriType res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0);
|
||||
|
||||
base = UriType(L"/path/#frag");
|
||||
ref = UriType(L"http://newauth/newpath#newfrag");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0);
|
||||
|
||||
// ref is alternate uri
|
||||
base = UriType(L"http://auth/path/#frag");
|
||||
ref = UriType(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
|
||||
|
||||
// ref is absolute path
|
||||
base = UriType(L"http://auth/path/#");
|
||||
ref = UriType(L"/newpath#newfrag");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newpath#newfrag") == 0);
|
||||
|
||||
// ref is relative path
|
||||
base = UriType(L"http://auth/path/file.json#frag");
|
||||
ref = UriType(L"newfile.json#");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#") == 0);
|
||||
|
||||
base = UriType(L"http://auth/path/file.json#frag/stuff");
|
||||
ref = UriType(L"newfile.json#newfrag/newstuff");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#newfrag/newstuff") == 0);
|
||||
|
||||
base = UriType(L"file.json", &allocator);
|
||||
ref = UriType(L"newfile.json", &base.GetAllocator());
|
||||
res = ref.Resolve(base, &ref.GetAllocator());
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
|
||||
|
||||
base = UriType(L"file.json", &allocator);
|
||||
ref = UriType(L"./newfile.json", &allocator);
|
||||
res = ref.Resolve(base, &allocator);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
|
||||
|
||||
base = UriType(L"file.json");
|
||||
ref = UriType(L"parent/../newfile.json");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
|
||||
|
||||
base = UriType(L"file.json");
|
||||
ref = UriType(L"parent/./newfile.json");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"parent/newfile.json") == 0);
|
||||
|
||||
base = UriType(L"file.json");
|
||||
ref = UriType(L"../../parent/.././newfile.json");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
|
||||
|
||||
// This adds a joining slash so resolved length is base length + ref length + 1
|
||||
base = UriType(L"http://auth");
|
||||
ref = UriType(L"newfile.json");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newfile.json") == 0);
|
||||
|
||||
// ref is fragment
|
||||
base = UriType(L"#frag/stuff");
|
||||
ref = UriType(L"#newfrag/newstuff");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"#newfrag/newstuff") == 0);
|
||||
|
||||
// test ref fragment always wins
|
||||
base = UriType(L"/path#frag");
|
||||
ref = UriType(L"");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"/path") == 0);
|
||||
|
||||
// Examples from RFC3896
|
||||
base = UriType(L"http://a/b/c/d;p?q");
|
||||
ref = UriType(L"g:h");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"g:h") == 0);
|
||||
ref = UriType(L"g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0);
|
||||
ref = UriType(L"./g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0);
|
||||
ref = UriType(L"g/");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g/") == 0);
|
||||
ref = UriType(L"/g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
|
||||
ref = UriType(L"//g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://g") == 0);
|
||||
ref = UriType(L"?y");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?y") == 0);
|
||||
ref = UriType(L"g?y");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y") == 0);
|
||||
ref = UriType(L"#s");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q#s") == 0);
|
||||
ref = UriType(L"g#s");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s") == 0);
|
||||
ref = UriType(L"g?y#s");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y#s") == 0);
|
||||
ref = UriType(L";x");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/;x") == 0);
|
||||
ref = UriType(L"g;x");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x") == 0);
|
||||
ref = UriType(L"g;x?y#s");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x?y#s") == 0);
|
||||
ref = UriType(L"");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q") == 0);
|
||||
ref = UriType(L".");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0);
|
||||
ref = UriType(L"./");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0);
|
||||
ref = UriType(L"..");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0);
|
||||
ref = UriType(L"../");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0);
|
||||
ref = UriType(L"../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/g") == 0);
|
||||
ref = UriType(L"../..");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0);
|
||||
ref = UriType(L"../../");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0);
|
||||
ref = UriType(L"../../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
|
||||
ref = UriType(L"../../../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
|
||||
ref = UriType(L"../../../../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
|
||||
ref = UriType(L"/./g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
|
||||
ref = UriType(L"/../g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
|
||||
ref = UriType(L"g.");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g.") == 0);
|
||||
ref = UriType(L".g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/.g") == 0);
|
||||
ref = UriType(L"g..");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g..") == 0);
|
||||
ref = UriType(L"..g");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/..g") == 0);
|
||||
ref = UriType(L"g#s/../x");
|
||||
res = ref.Resolve(base);
|
||||
EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s/../x") == 0);
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) || defined(__clang__)
|
||||
|
Loading…
x
Reference in New Issue
Block a user