aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRichard Walters <rwalters@digitalstirling.com>2018-06-30 22:30:19 -0700
committerRichard Walters <rwalters@digitalstirling.com>2018-06-30 22:30:19 -0700
commit8c752fdcf71f1d8f3980c8066e2bedf782d9739c (patch)
tree7bc5ad87eff0ddd584f77c08858c240cd6839721 /src
parenta43820d0b4014878e4bbfede6acde25f5830faa7 (diff)
Add more element parsing of URIs
* Add IsRelativeReference. * Add IsRelativePath. * Add Query. * Add Fragment. * Add UserInfo. * Fix parsing of URIs that have no scheme.
Diffstat (limited to 'src')
-rw-r--r--src/Uri.cpp127
1 files changed, 109 insertions, 18 deletions
diff --git a/src/Uri.cpp b/src/Uri.cpp
index 78f3b4d..984d3ed 100644
--- a/src/Uri.cpp
+++ b/src/Uri.cpp
@@ -22,6 +22,11 @@ namespace Uri {
std::string scheme;
/**
+ * This is the "UserInfo" element of the URI.
+ */
+ std::string userInfo;
+
+ /**
* This is the "host" element of the URI.
*/
std::string host;
@@ -42,6 +47,18 @@ namespace Uri {
* as a sequence of segments.
*/
std::vector< std::string > path;
+
+ /**
+ * This is the "query" element of the URI,
+ * if it has one.
+ */
+ std::string query;
+
+ /**
+ * This is the "fragment" element of the URI,
+ * if it has one.
+ */
+ std::string fragment;
};
Uri::~Uri() = default;
@@ -54,20 +71,49 @@ namespace Uri {
bool Uri::ParseFromString(const std::string& uriString) {
// First parse the scheme.
const auto schemeEnd = uriString.find(':');
- impl_->scheme = uriString.substr(0, schemeEnd);
- auto rest = uriString.substr(schemeEnd + 1);
+ std::string rest;
+ if (schemeEnd == std::string::npos) {
+ impl_->scheme.clear();
+ rest = uriString;
+ } else {
+ impl_->scheme = uriString.substr(0, schemeEnd);
+ rest = uriString.substr(schemeEnd + 1);
+ }
- // Next parse the host.
+ // Next parse the authority.
impl_->hasPort = false;
- if (rest.substr(0, 2) == "//") {
- const auto authorityEnd = rest.find('/', 2);
- const auto portDelimiter = rest.find(':');
+ const auto pathEnd = rest.find_first_of("?#");
+ auto authorityAndPathString = rest.substr(0, pathEnd);
+ const auto queryAndOrFragment = rest.substr(authorityAndPathString.length());
+ std::string hostPortAndPathString;
+ if (authorityAndPathString.substr(0, 2) == "//") {
+ // Strip off authority marker.
+ authorityAndPathString = authorityAndPathString.substr(2);
+
+ // First separate the authority from the path.
+ auto authorityEnd = authorityAndPathString.find('/');
+ if (authorityEnd == std::string::npos) {
+ authorityEnd = authorityAndPathString.length();
+ }
+
+ // Next, check if there is a UserInfo, and if so, extract it.
+ const auto userInfoDelimiter = authorityAndPathString.find('@');
+ if (userInfoDelimiter == std::string::npos) {
+ impl_->userInfo.clear();
+ hostPortAndPathString = authorityAndPathString;
+ } else {
+ impl_->userInfo = authorityAndPathString.substr(0, userInfoDelimiter);
+ hostPortAndPathString = authorityAndPathString.substr(userInfoDelimiter + 1);
+ }
+
+ // Next, parsing host and port from authority and path.
+ const auto portDelimiter = hostPortAndPathString.find(':');
if (portDelimiter == std::string::npos) {
- impl_->host = rest.substr(2, authorityEnd - 2);
+ impl_->host = hostPortAndPathString.substr(0, authorityEnd);
} else {
- impl_->host = rest.substr(2, portDelimiter - 2);
+ impl_->host = hostPortAndPathString.substr(0, portDelimiter);
uint32_t newPort = 0;
- for (auto c: rest.substr(portDelimiter + 1, authorityEnd - portDelimiter - 1)) {
+ for (auto c: hostPortAndPathString.substr(portDelimiter + 1, authorityEnd - portDelimiter - 1)) {
if (
(c < '0')
|| (c > '9')
@@ -85,32 +131,53 @@ namespace Uri {
impl_->port = (uint16_t)newPort;
impl_->hasPort = true;
}
- rest = rest.substr(authorityEnd);
+ hostPortAndPathString = authorityAndPathString.substr(authorityEnd);
} else {
impl_->host.clear();
+ hostPortAndPathString = authorityAndPathString;
}
+ auto pathString = hostPortAndPathString;
- // Finally, parse the path.
+ // Next, parse the path.
impl_->path.clear();
- if (rest == "/") {
+ if (pathString == "/") {
// Special case of a path that is empty but needs a single
// empty-string element to indicate that it is absolute.
impl_->path.push_back("");
- } else if (!rest.empty()) {
+ pathString.clear();
+ } else if (!pathString.empty()) {
for(;;) {
- auto pathDelimiter = rest.find('/');
+ auto pathDelimiter = pathString.find('/');
if (pathDelimiter == std::string::npos) {
- impl_->path.push_back(rest);
+ impl_->path.push_back(pathString);
+ pathString.clear();
break;
} else {
impl_->path.emplace_back(
- rest.begin(),
- rest.begin() + pathDelimiter
+ pathString.begin(),
+ pathString.begin() + pathDelimiter
);
- rest = rest.substr(pathDelimiter + 1);
+ pathString = pathString.substr(pathDelimiter + 1);
}
}
}
+
+ // Next, parse the fragment if there is one.
+ const auto fragmentDelimiter = queryAndOrFragment.find('#');
+ if (fragmentDelimiter == std::string::npos) {
+ impl_->fragment.clear();
+ rest = queryAndOrFragment;
+ } else {
+ impl_->fragment = queryAndOrFragment.substr(fragmentDelimiter + 1);
+ rest = queryAndOrFragment.substr(0, fragmentDelimiter);
+ }
+
+ // Finally, if anything is left, it's the query.
+ if (rest.empty()) {
+ impl_->query.clear();
+ } else {
+ impl_->query = rest.substr(1);
+ }
return true;
}
@@ -118,6 +185,10 @@ namespace Uri {
return impl_->scheme;
}
+ std::string Uri::GetUserInfo() const {
+ return impl_->userInfo;
+ }
+
std::string Uri::GetHost() const {
return impl_->host;
}
@@ -134,4 +205,24 @@ namespace Uri {
return impl_->port;
}
+ bool Uri::IsRelativeReference() const {
+ return impl_->scheme.empty();
+ }
+
+ bool Uri::ContainsRelativePath() const {
+ if (impl_->path.empty()) {
+ return true;
+ } else {
+ return !impl_->path[0].empty();
+ }
+ }
+
+ std::string Uri::GetQuery() const {
+ return impl_->query;
+ }
+
+ std::string Uri::GetFragment() const {
+ return impl_->fragment;
+ }
+
}