* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
#include "uri.h"
#include <vector>
#include "hilog/log.h"
#include "string_ex.h"
using OHOS::HiviewDFX::HiLog;
using std::string;
namespace OHOS {
namespace {
const string NOT_CACHED = "NOT VALID";
const string EMPTY = "";
const size_t NOT_FOUND = string::npos;
const int NOT_CALCULATED = -2;
const int PORT_NONE = -1;
const char SCHEME_SEPARATOR = ':';
const char SCHEME_FRAGMENT = '#';
const char LEFT_SEPARATOR = '/';
const char RIGHT_SEPARATOR = '\\';
const char QUERY_FLAG = '?';
const char USER_HOST_SEPARATOR = '@';
const char PORT_SEPARATOR = ':';
const size_t POS_INC = 1;
const size_t POS_INC_MORE = 2;
const size_t POS_INC_AGAIN = 3;
};
Uri::Uri(const string& uriString)
{
cachedSsi_ = NOT_FOUND;
cachedFsi_ = NOT_FOUND;
port_ = NOT_CALCULATED;
if (uriString.empty()) {
return;
}
uriString_ = uriString;
scheme_ = NOT_CACHED;
ssp_ = NOT_CACHED;
authority_ = NOT_CACHED;
host_ = NOT_CACHED;
userInfo_ = NOT_CACHED;
query_ = NOT_CACHED;
path_ = NOT_CACHED;
fragment_ = NOT_CACHED;
if (!CheckScheme()) {
uriString_ = EMPTY;
HILOG_IMPL(LOG_CORE, LOG_ERROR, 0xD001305, "URI", "Scheme wrong");
}
}
Uri::~Uri() {}
bool Uri::CheckScheme()
{
scheme_ = ParseScheme();
if (scheme_.empty()) {
return true;
}
auto isAlpha = [](char ch) -> bool { return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); };
auto isDigit = [](char ch) -> bool { return (ch >= '0' && ch <= '9'); };
auto isSchemeChar = [&isAlpha, &isDigit](char ch) -> bool {
return isAlpha(ch) || isDigit(ch) || ch == '+' || ch == '-' || ch == '.';
};
if (!isAlpha(scheme_.front())) {
return false;
}
for (size_t i = 1; i < scheme_.size(); ++i) {
if (!isSchemeChar(scheme_[i])) {
return false;
}
}
return true;
}
string Uri::GetScheme()
{
if (uriString_.empty()) {
return EMPTY;
}
if (scheme_ == NOT_CACHED) {
scheme_ = ParseScheme();
}
return scheme_;
}
string Uri::ParseScheme()
{
size_t ssi = FindSchemeSeparator();
return (ssi == NOT_FOUND) ? EMPTY : uriString_.substr(0, ssi);
}
string Uri::GetSchemeSpecificPart()
{
if (uriString_.empty()) {
return EMPTY;
}
return (ssp_ == NOT_CACHED) ? (ssp_ = ParseSsp()) : ssp_;
}
string Uri::ParseSsp()
{
size_t ssi = FindSchemeSeparator();
size_t fsi = FindFragmentSeparator();
size_t start = (ssi == NOT_FOUND) ? 0 : (ssi + 1);
size_t end = (fsi == NOT_FOUND) ? uriString_.size() : fsi;
string ssp = EMPTY;
if (end > start) {
ssp = uriString_.substr(start, end - start);
}
return ssp;
}
string Uri::GetAuthority()
{
if (uriString_.empty()) {
return EMPTY;
}
if (authority_ == NOT_CACHED) {
authority_ = ParseAuthority();
}
return authority_;
}
string Uri::ParseAuthority()
{
size_t ssi = FindSchemeSeparator();
if (ssi == NOT_FOUND) {
return EMPTY;
}
size_t length = uriString_.length();
if ((length > (ssi + POS_INC_MORE)) && (uriString_.at(ssi + POS_INC) == LEFT_SEPARATOR) &&
(uriString_.at(ssi + POS_INC_MORE) == LEFT_SEPARATOR)) {
size_t start = ssi + POS_INC_AGAIN;
size_t end = start;
while (end < length) {
char ch = uriString_.at(end);
if ((ch == LEFT_SEPARATOR) || (ch == RIGHT_SEPARATOR) || (ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) {
break;
}
end++;
}
return uriString_.substr(start, end - start);
} else {
return EMPTY;
}
}
string Uri::GetUserInfo()
{
if (uriString_.empty()) {
return EMPTY;
}
if (userInfo_ == NOT_CACHED) {
userInfo_ = ParseUserInfo();
}
return userInfo_;
}
string Uri::ParseUserInfo()
{
string authority = GetAuthority();
if (authority.empty()) {
return EMPTY;
}
size_t end = authority.find_last_of(USER_HOST_SEPARATOR);
return (end == NOT_FOUND) ? EMPTY : authority.substr(0, end);
}
string Uri::GetHost()
{
if (uriString_.empty()) {
return EMPTY;
}
if (host_ == NOT_CACHED) {
host_ = ParseHost();
}
return host_;
}
string Uri::ParseHost()
{
string authority = GetAuthority();
if (authority.empty()) {
return EMPTY;
}
size_t userInfoSeparator = authority.find_last_of(USER_HOST_SEPARATOR);
size_t start = (userInfoSeparator == NOT_FOUND) ? 0 : (userInfoSeparator + 1);
size_t portSeparator = authority.find_first_of(PORT_SEPARATOR, start);
size_t end = (portSeparator == NOT_FOUND) ? authority.size() : portSeparator;
string host = EMPTY;
if (start < end) {
host = authority.substr(start, end - start);
}
return host;
}
int Uri::GetPort()
{
if (uriString_.empty()) {
return PORT_NONE;
}
if (port_ == NOT_CALCULATED) {
port_ = ParsePort();
}
return port_;
}
int Uri::ParsePort()
{
string authority = GetAuthority();
if (authority.empty()) {
return PORT_NONE;
}
size_t userInfoSeparator = authority.find_last_of(USER_HOST_SEPARATOR);
size_t start = (userInfoSeparator == NOT_FOUND) ? 0 : (userInfoSeparator + 1);
size_t portSeparator = authority.find_first_of(PORT_SEPARATOR, start);
if (portSeparator == NOT_FOUND) {
return PORT_NONE;
}
start = portSeparator + 1;
string portString = authority.substr(start);
int value = PORT_NONE;
return StrToInt(portString, value) ? value : PORT_NONE;
}
string Uri::GetQuery()
{
if (uriString_.empty()) {
return EMPTY;
}
if (query_ == NOT_CACHED) {
query_ = ParseQuery();
}
return query_;
}
string Uri::ParseQuery()
{
size_t ssi = FindSchemeSeparator();
if (ssi == NOT_FOUND) {
ssi = 0;
}
size_t qsi = uriString_.find_first_of(QUERY_FLAG, ssi);
if (qsi == NOT_FOUND) {
return EMPTY;
}
size_t start = qsi + 1;
size_t fsi = FindFragmentSeparator();
if (fsi == NOT_FOUND) {
return uriString_.substr(start);
}
if (fsi < qsi) {
return EMPTY;
}
return uriString_.substr(start, fsi - start);
}
string Uri::GetPath()
{
if (uriString_.empty()) {
return EMPTY;
}
if (path_ == NOT_CACHED) {
path_ = ParsePath();
}
return path_;
}
void Uri::GetPathSegments(std::vector<std::string>& segments)
{
if (uriString_.empty()) {
return;
}
if (path_ == NOT_CACHED) {
path_ = ParsePath();
}
size_t previous = 0;
size_t current;
while ((current = path_.find(LEFT_SEPARATOR, previous)) != std::string::npos) {
if (previous < current) {
segments.emplace_back(path_.substr(previous, current - previous));
}
previous = current + POS_INC;
}
if (previous < path_.length()) {
segments.emplace_back(path_.substr(previous));
}
}
string Uri::ParsePath()
{
size_t ssi = FindSchemeSeparator();
if (ssi != NOT_FOUND) {
if ((ssi + 1) == uriString_.length()) {
return EMPTY;
}
if (uriString_.at(ssi + 1) != LEFT_SEPARATOR) {
return EMPTY;
}
} else {
}
return ParsePath(ssi);
}
string Uri::ParsePath(size_t ssi)
{
size_t length = uriString_.length();
size_t pathStart = (ssi == NOT_FOUND) ? 0 : (ssi + POS_INC);
if ((length > (pathStart + POS_INC)) && (uriString_.at(pathStart) == LEFT_SEPARATOR) &&
(uriString_.at(pathStart + POS_INC) == LEFT_SEPARATOR)) {
pathStart += POS_INC_MORE;
while (pathStart < length) {
char ch = uriString_.at(pathStart);
if ((ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) {
return EMPTY;
}
if ((ch == LEFT_SEPARATOR) || (ch == RIGHT_SEPARATOR)) {
break;
}
pathStart++;
}
}
size_t pathEnd = pathStart;
while (pathEnd < length) {
char ch = uriString_.at(pathEnd);
if ((ch == QUERY_FLAG) || (ch == SCHEME_FRAGMENT)) {
break;
}
pathEnd++;
}
return uriString_.substr(pathStart, pathEnd - pathStart);
}
string Uri::GetFragment()
{
if (uriString_.empty()) {
return EMPTY;
}
if (fragment_ == NOT_CACHED) {
fragment_ = ParseFragment();
}
return fragment_;
}
string Uri::ParseFragment()
{
size_t fsi = FindFragmentSeparator();
return (fsi == NOT_FOUND) ? EMPTY : uriString_.substr(fsi + 1);
}
size_t Uri::FindSchemeSeparator()
{
if (cachedSsi_ == NOT_FOUND) {
cachedSsi_ = uriString_.find_first_of(SCHEME_SEPARATOR);
}
return cachedSsi_;
}
size_t Uri::FindFragmentSeparator()
{
if (cachedFsi_ == NOT_FOUND) {
cachedFsi_ = uriString_.find_first_of(SCHEME_FRAGMENT, FindSchemeSeparator());
}
return cachedFsi_;
}
bool Uri::IsHierarchical()
{
if (uriString_.empty()) {
return false;
}
size_t ssi = FindSchemeSeparator();
if (ssi == NOT_FOUND) {
return true;
}
if (uriString_.length() == (ssi + 1)) {
return false;
}
return (uriString_.at(ssi + 1) == LEFT_SEPARATOR);
}
bool Uri::IsAbsolute()
{
if (uriString_.empty()) {
return false;
}
return !IsRelative();
}
bool Uri::IsRelative()
{
if (uriString_.empty()) {
return false;
}
return FindSchemeSeparator() == NOT_FOUND;
}
bool Uri::Equals(const Uri& other) const
{
return uriString_ == other.ToString();
}
int Uri::CompareTo(const Uri& other) const
{
return uriString_.compare(other.ToString());
}
string Uri::ToString() const
{
return uriString_;
}
bool Uri::operator==(const Uri& other) const
{
return uriString_ == other.ToString();
}
bool Uri::Marshalling(Parcel& parcel) const
{
return parcel.WriteString(uriString_);
}
Uri* Uri::Unmarshalling(Parcel& parcel)
{
return new Uri(parcel.ReadString());
}
}