diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h index baecb6c..b698a8f 100644 --- a/include/rapidjson/internal/strfunc.h +++ b/include/rapidjson/internal/strfunc.h @@ -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 +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(*s1) < static_cast(*s2) ? -1 : static_cast(*s1) > static_cast(*s2); +} + //! Returns number of code points in a encoded string. template bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index b5e952b..96f33fa 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -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(GenericStringRef(t->name, t->length))); @@ -576,7 +575,7 @@ public: // Error: unresolved token if (unresolvedTokenIndex) *unresolvedTokenIndex = static_cast(t - tokens_); - return UriType(); + return UriType(allocator_); } return base; } diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 791aca5..a48288b 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -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(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 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)) { diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 98b0a15..640d793 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -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 -class GenericUri { -public: + template + class GenericUri { + public: typedef typename ValueType::Ch Ch; +#if RAPIDJSON_HAS_STDSTRING typedef std::basic_string 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(uri)); } // Use with specializations of GenericValue - template GenericUri(const T& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() { - Parse(uri.template Get()); + template GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + const Ch* u = uri.template Get(); // TypeHelper from document.h + Parse(u, internal::StrLen(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(uri.c_str())); + } +#endif - // Use with specializations of GenericValue - template void Get(T& uri, Allocator& allocator) { - uri.template Set(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(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(base_.length()); } - - const Ch* GetFragString() const { return frag_.c_str(); } - SizeType GetFragStringLength() const { return static_cast(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 void Get(T& uri, Allocator& allocator) { + uri.template Set(this->GetString(), allocator); // TypeHelper from document.h + } + + const Ch* GetString() const { return uri_; } + SizeType GetStringLength() const { return internal::StrLen(uri_); } + const Ch* GetBaseString() const { return base_; } + SizeType GetBaseStringLength() const { return internal::StrLen(base_); } + const Ch* GetSchemeString() const { return scheme_; } + SizeType GetSchemeStringLength() const { return internal::StrLen(scheme_); } + const Ch* GetAuthString() const { return auth_; } + SizeType GetAuthStringLength() const { return internal::StrLen(auth_); } + const Ch* GetPathString() const { return path_; } + SizeType GetPathStringLength() const { return internal::StrLen(path_); } + const Ch* GetQueryString() const { return query_; } + SizeType GetQueryStringLength() const { return internal::StrLen(query_); } + const Ch* GetFragString() const { return frag_; } + SizeType GetFragStringLength() const { return internal::StrLen(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(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(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(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 Uri; + typedef GenericUri Uri; RAPIDJSON_NAMESPACE_END diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index a835af4..c00658c 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -659,36 +659,37 @@ static const char kJsonIds[] = "{\n" TEST(Pointer, GetUri) { - typedef std::basic_string 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) { diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp index b5eda29..936b831 100644 --- a/test/unittest/uritest.cpp +++ b/test/unittest/uritest.cpp @@ -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 String; - typedef GenericUri > UriType; - MemoryPoolAllocator allocator; +typedef GenericUri > UriType; +MemoryPoolAllocator 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 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(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 > Value; +typedef GenericUri > UriType; +MemoryPoolAllocator 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 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(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 String; - typedef GenericUri > UriType; +typedef GenericUri 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 > Value; +typedef GenericUri 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__)