mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-12 01:12:12 +00:00
917 lines
18 KiB
C++
917 lines
18 KiB
C++
//
|
|
// URI.cpp
|
|
//
|
|
// $Id: //poco/1.4/Foundation/src/URI.cpp#5 $
|
|
//
|
|
// Library: Foundation
|
|
// Package: URI
|
|
// Module: URI
|
|
//
|
|
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
|
|
// and Contributors.
|
|
//
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
//
|
|
|
|
|
|
#include "Poco/URI.h"
|
|
#include "Poco/NumberFormatter.h"
|
|
#include "Poco/Exception.h"
|
|
#include "Poco/String.h"
|
|
#include "Poco/NumberParser.h"
|
|
#include "Poco/Path.h"
|
|
|
|
|
|
namespace Poco {
|
|
|
|
|
|
const std::string URI::RESERVED_PATH = "?#";
|
|
const std::string URI::RESERVED_QUERY = "?#/";
|
|
const std::string URI::RESERVED_FRAGMENT = "";
|
|
const std::string URI::ILLEGAL = "%<>{}|\\\"^`";
|
|
|
|
|
|
URI::URI():
|
|
_port(0)
|
|
{
|
|
}
|
|
|
|
|
|
URI::URI(const std::string& uri):
|
|
_port(0)
|
|
{
|
|
parse(uri);
|
|
}
|
|
|
|
|
|
URI::URI(const char* uri):
|
|
_port(0)
|
|
{
|
|
parse(std::string(uri));
|
|
}
|
|
|
|
|
|
URI::URI(const std::string& scheme, const std::string& pathEtc):
|
|
_scheme(scheme),
|
|
_port(0)
|
|
{
|
|
toLowerInPlace(_scheme);
|
|
_port = getWellKnownPort();
|
|
std::string::const_iterator beg = pathEtc.begin();
|
|
std::string::const_iterator end = pathEtc.end();
|
|
parsePathEtc(beg, end);
|
|
}
|
|
|
|
|
|
URI::URI(const std::string& scheme, const std::string& authority, const std::string& pathEtc):
|
|
_scheme(scheme)
|
|
{
|
|
toLowerInPlace(_scheme);
|
|
std::string::const_iterator beg = authority.begin();
|
|
std::string::const_iterator end = authority.end();
|
|
parseAuthority(beg, end);
|
|
beg = pathEtc.begin();
|
|
end = pathEtc.end();
|
|
parsePathEtc(beg, end);
|
|
}
|
|
|
|
|
|
URI::URI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query):
|
|
_scheme(scheme),
|
|
_path(path),
|
|
_query(query)
|
|
{
|
|
toLowerInPlace(_scheme);
|
|
std::string::const_iterator beg = authority.begin();
|
|
std::string::const_iterator end = authority.end();
|
|
parseAuthority(beg, end);
|
|
}
|
|
|
|
|
|
URI::URI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query, const std::string& fragment):
|
|
_scheme(scheme),
|
|
_path(path),
|
|
_query(query),
|
|
_fragment(fragment)
|
|
{
|
|
toLowerInPlace(_scheme);
|
|
std::string::const_iterator beg = authority.begin();
|
|
std::string::const_iterator end = authority.end();
|
|
parseAuthority(beg, end);
|
|
}
|
|
|
|
|
|
URI::URI(const URI& uri):
|
|
_scheme(uri._scheme),
|
|
_userInfo(uri._userInfo),
|
|
_host(uri._host),
|
|
_port(uri._port),
|
|
_path(uri._path),
|
|
_query(uri._query),
|
|
_fragment(uri._fragment)
|
|
{
|
|
}
|
|
|
|
|
|
URI::URI(const URI& baseURI, const std::string& relativeURI):
|
|
_scheme(baseURI._scheme),
|
|
_userInfo(baseURI._userInfo),
|
|
_host(baseURI._host),
|
|
_port(baseURI._port),
|
|
_path(baseURI._path),
|
|
_query(baseURI._query),
|
|
_fragment(baseURI._fragment)
|
|
{
|
|
resolve(relativeURI);
|
|
}
|
|
|
|
|
|
URI::URI(const Path& path):
|
|
_scheme("file"),
|
|
_port(0)
|
|
{
|
|
Path absolutePath(path);
|
|
absolutePath.makeAbsolute();
|
|
_path = absolutePath.toString(Path::PATH_UNIX);
|
|
}
|
|
|
|
|
|
URI::~URI()
|
|
{
|
|
}
|
|
|
|
|
|
URI& URI::operator = (const URI& uri)
|
|
{
|
|
if (&uri != this)
|
|
{
|
|
_scheme = uri._scheme;
|
|
_userInfo = uri._userInfo;
|
|
_host = uri._host;
|
|
_port = uri._port;
|
|
_path = uri._path;
|
|
_query = uri._query;
|
|
_fragment = uri._fragment;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
URI& URI::operator = (const std::string& uri)
|
|
{
|
|
clear();
|
|
parse(uri);
|
|
return *this;
|
|
}
|
|
|
|
|
|
URI& URI::operator = (const char* uri)
|
|
{
|
|
clear();
|
|
parse(std::string(uri));
|
|
return *this;
|
|
}
|
|
|
|
|
|
void URI::swap(URI& uri)
|
|
{
|
|
std::swap(_scheme, uri._scheme);
|
|
std::swap(_userInfo, uri._userInfo);
|
|
std::swap(_host, uri._host);
|
|
std::swap(_port, uri._port);
|
|
std::swap(_path, uri._path);
|
|
std::swap(_query, uri._query);
|
|
std::swap(_fragment, uri._fragment);
|
|
}
|
|
|
|
|
|
void URI::clear()
|
|
{
|
|
_scheme.clear();
|
|
_userInfo.clear();
|
|
_host.clear();
|
|
_port = 0;
|
|
_path.clear();
|
|
_query.clear();
|
|
_fragment.clear();
|
|
}
|
|
|
|
|
|
std::string URI::toString() const
|
|
{
|
|
std::string uri;
|
|
if (isRelative())
|
|
{
|
|
encode(_path, RESERVED_PATH, uri);
|
|
}
|
|
else
|
|
{
|
|
uri = _scheme;
|
|
uri += ':';
|
|
std::string auth = getAuthority();
|
|
if (!auth.empty() || _scheme == "file")
|
|
{
|
|
uri.append("//");
|
|
uri.append(auth);
|
|
}
|
|
if (!_path.empty())
|
|
{
|
|
if (!auth.empty() && _path[0] != '/')
|
|
uri += '/';
|
|
encode(_path, RESERVED_PATH, uri);
|
|
}
|
|
else if (!_query.empty() || !_fragment.empty())
|
|
{
|
|
uri += '/';
|
|
}
|
|
}
|
|
if (!_query.empty())
|
|
{
|
|
uri += '?';
|
|
uri.append(_query);
|
|
}
|
|
if (!_fragment.empty())
|
|
{
|
|
uri += '#';
|
|
encode(_fragment, RESERVED_FRAGMENT, uri);
|
|
}
|
|
return uri;
|
|
}
|
|
|
|
|
|
void URI::setScheme(const std::string& scheme)
|
|
{
|
|
_scheme = scheme;
|
|
toLowerInPlace(_scheme);
|
|
if (_port == 0)
|
|
_port = getWellKnownPort();
|
|
}
|
|
|
|
|
|
void URI::setUserInfo(const std::string& userInfo)
|
|
{
|
|
_userInfo.clear();
|
|
decode(userInfo, _userInfo);
|
|
}
|
|
|
|
|
|
void URI::setHost(const std::string& host)
|
|
{
|
|
_host = host;
|
|
}
|
|
|
|
|
|
unsigned short URI::getPort() const
|
|
{
|
|
if (_port == 0)
|
|
return getWellKnownPort();
|
|
else
|
|
return _port;
|
|
}
|
|
|
|
|
|
void URI::setPort(unsigned short port)
|
|
{
|
|
_port = port;
|
|
}
|
|
|
|
|
|
std::string URI::getAuthority() const
|
|
{
|
|
std::string auth;
|
|
if (!_userInfo.empty())
|
|
{
|
|
auth.append(_userInfo);
|
|
auth += '@';
|
|
}
|
|
if (_host.find(':') != std::string::npos)
|
|
{
|
|
auth += '[';
|
|
auth += _host;
|
|
auth += ']';
|
|
}
|
|
else auth.append(_host);
|
|
if (_port && !isWellKnownPort())
|
|
{
|
|
auth += ':';
|
|
NumberFormatter::append(auth, _port);
|
|
}
|
|
return auth;
|
|
}
|
|
|
|
|
|
void URI::setAuthority(const std::string& authority)
|
|
{
|
|
_userInfo.clear();
|
|
_host.clear();
|
|
_port = 0;
|
|
std::string::const_iterator beg = authority.begin();
|
|
std::string::const_iterator end = authority.end();
|
|
parseAuthority(beg, end);
|
|
}
|
|
|
|
|
|
void URI::setPath(const std::string& path)
|
|
{
|
|
_path.clear();
|
|
decode(path, _path);
|
|
}
|
|
|
|
|
|
void URI::setRawQuery(const std::string& query)
|
|
{
|
|
_query = query;
|
|
}
|
|
|
|
|
|
void URI::setQuery(const std::string& query)
|
|
{
|
|
_query.clear();
|
|
encode(query, RESERVED_QUERY, _query);
|
|
}
|
|
|
|
|
|
void URI::addQueryParameter(const std::string& param, const std::string& val)
|
|
{
|
|
std::string reserved(RESERVED_QUERY);
|
|
reserved += "=&";
|
|
if (!_query.empty()) _query += '&';
|
|
encode(param, reserved, _query);
|
|
_query += '=';
|
|
encode(val, reserved, _query);
|
|
}
|
|
|
|
|
|
std::string URI::getQuery() const
|
|
{
|
|
std::string query;
|
|
decode(_query, query);
|
|
return query;
|
|
}
|
|
|
|
|
|
URI::QueryParameters URI::getQueryParameters() const
|
|
{
|
|
QueryParameters result;
|
|
std::string::const_iterator it(_query.begin());
|
|
std::string::const_iterator end(_query.end());
|
|
while (it != end)
|
|
{
|
|
std::string name;
|
|
std::string value;
|
|
while (it != end && *it != '=' && *it != '&')
|
|
{
|
|
if (*it == '+')
|
|
name += ' ';
|
|
else
|
|
name += *it;
|
|
++it;
|
|
}
|
|
if (it != end && *it == '=')
|
|
{
|
|
++it;
|
|
while (it != end && *it != '&')
|
|
{
|
|
if (*it == '+')
|
|
value += ' ';
|
|
else
|
|
value += *it;
|
|
++it;
|
|
}
|
|
}
|
|
std::string decodedName;
|
|
std::string decodedValue;
|
|
URI::decode(name, decodedName);
|
|
URI::decode(value, decodedValue);
|
|
result.push_back(std::make_pair(decodedName, decodedValue));
|
|
if (it != end && *it == '&') ++it;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
void URI::setQueryParameters(const QueryParameters& params)
|
|
{
|
|
_query.clear();
|
|
for (QueryParameters::const_iterator it = params.begin(); it != params.end(); ++it)
|
|
{
|
|
addQueryParameter(it->first, it->second);
|
|
}
|
|
}
|
|
|
|
|
|
void URI::setFragment(const std::string& fragment)
|
|
{
|
|
_fragment.clear();
|
|
decode(fragment, _fragment);
|
|
}
|
|
|
|
|
|
void URI::setPathEtc(const std::string& pathEtc)
|
|
{
|
|
_path.clear();
|
|
_query.clear();
|
|
_fragment.clear();
|
|
std::string::const_iterator beg = pathEtc.begin();
|
|
std::string::const_iterator end = pathEtc.end();
|
|
parsePathEtc(beg, end);
|
|
}
|
|
|
|
|
|
std::string URI::getPathEtc() const
|
|
{
|
|
std::string pathEtc;
|
|
encode(_path, RESERVED_PATH, pathEtc);
|
|
if (!_query.empty())
|
|
{
|
|
pathEtc += '?';
|
|
pathEtc += _query;
|
|
}
|
|
if (!_fragment.empty())
|
|
{
|
|
pathEtc += '#';
|
|
encode(_fragment, RESERVED_FRAGMENT, pathEtc);
|
|
}
|
|
return pathEtc;
|
|
}
|
|
|
|
|
|
std::string URI::getPathAndQuery() const
|
|
{
|
|
std::string pathAndQuery;
|
|
encode(_path, RESERVED_PATH, pathAndQuery);
|
|
if (!_query.empty())
|
|
{
|
|
pathAndQuery += '?';
|
|
pathAndQuery += _query;
|
|
}
|
|
return pathAndQuery;
|
|
}
|
|
|
|
|
|
void URI::resolve(const std::string& relativeURI)
|
|
{
|
|
URI parsedURI(relativeURI);
|
|
resolve(parsedURI);
|
|
}
|
|
|
|
|
|
void URI::resolve(const URI& relativeURI)
|
|
{
|
|
if (!relativeURI._scheme.empty())
|
|
{
|
|
_scheme = relativeURI._scheme;
|
|
_userInfo = relativeURI._userInfo;
|
|
_host = relativeURI._host;
|
|
_port = relativeURI._port;
|
|
_path = relativeURI._path;
|
|
_query = relativeURI._query;
|
|
removeDotSegments();
|
|
}
|
|
else
|
|
{
|
|
if (!relativeURI._host.empty())
|
|
{
|
|
_userInfo = relativeURI._userInfo;
|
|
_host = relativeURI._host;
|
|
_port = relativeURI._port;
|
|
_path = relativeURI._path;
|
|
_query = relativeURI._query;
|
|
removeDotSegments();
|
|
}
|
|
else
|
|
{
|
|
if (relativeURI._path.empty())
|
|
{
|
|
if (!relativeURI._query.empty())
|
|
_query = relativeURI._query;
|
|
}
|
|
else
|
|
{
|
|
if (relativeURI._path[0] == '/')
|
|
{
|
|
_path = relativeURI._path;
|
|
removeDotSegments();
|
|
}
|
|
else
|
|
{
|
|
mergePath(relativeURI._path);
|
|
}
|
|
_query = relativeURI._query;
|
|
}
|
|
}
|
|
}
|
|
_fragment = relativeURI._fragment;
|
|
}
|
|
|
|
|
|
bool URI::isRelative() const
|
|
{
|
|
return _scheme.empty();
|
|
}
|
|
|
|
|
|
bool URI::empty() const
|
|
{
|
|
return _scheme.empty() && _host.empty() && _path.empty() && _query.empty() && _fragment.empty();
|
|
}
|
|
|
|
|
|
bool URI::operator == (const URI& uri) const
|
|
{
|
|
return equals(uri);
|
|
}
|
|
|
|
|
|
bool URI::operator == (const std::string& uri) const
|
|
{
|
|
URI parsedURI(uri);
|
|
return equals(parsedURI);
|
|
}
|
|
|
|
|
|
bool URI::operator != (const URI& uri) const
|
|
{
|
|
return !equals(uri);
|
|
}
|
|
|
|
|
|
bool URI::operator != (const std::string& uri) const
|
|
{
|
|
URI parsedURI(uri);
|
|
return !equals(parsedURI);
|
|
}
|
|
|
|
|
|
bool URI::equals(const URI& uri) const
|
|
{
|
|
return _scheme == uri._scheme
|
|
&& _userInfo == uri._userInfo
|
|
&& _host == uri._host
|
|
&& getPort() == uri.getPort()
|
|
&& _path == uri._path
|
|
&& _query == uri._query
|
|
&& _fragment == uri._fragment;
|
|
}
|
|
|
|
|
|
void URI::normalize()
|
|
{
|
|
removeDotSegments(!isRelative());
|
|
}
|
|
|
|
|
|
void URI::removeDotSegments(bool removeLeading)
|
|
{
|
|
if (_path.empty()) return;
|
|
|
|
bool leadingSlash = *(_path.begin()) == '/';
|
|
bool trailingSlash = *(_path.rbegin()) == '/';
|
|
std::vector<std::string> segments;
|
|
std::vector<std::string> normalizedSegments;
|
|
getPathSegments(segments);
|
|
for (std::vector<std::string>::const_iterator it = segments.begin(); it != segments.end(); ++it)
|
|
{
|
|
if (*it == "..")
|
|
{
|
|
if (!normalizedSegments.empty())
|
|
{
|
|
if (normalizedSegments.back() == "..")
|
|
normalizedSegments.push_back(*it);
|
|
else
|
|
normalizedSegments.pop_back();
|
|
}
|
|
else if (!removeLeading)
|
|
{
|
|
normalizedSegments.push_back(*it);
|
|
}
|
|
}
|
|
else if (*it != ".")
|
|
{
|
|
normalizedSegments.push_back(*it);
|
|
}
|
|
}
|
|
buildPath(normalizedSegments, leadingSlash, trailingSlash);
|
|
}
|
|
|
|
|
|
void URI::getPathSegments(std::vector<std::string>& segments)
|
|
{
|
|
getPathSegments(_path, segments);
|
|
}
|
|
|
|
|
|
void URI::getPathSegments(const std::string& path, std::vector<std::string>& segments)
|
|
{
|
|
std::string::const_iterator it = path.begin();
|
|
std::string::const_iterator end = path.end();
|
|
std::string seg;
|
|
while (it != end)
|
|
{
|
|
if (*it == '/')
|
|
{
|
|
if (!seg.empty())
|
|
{
|
|
segments.push_back(seg);
|
|
seg.clear();
|
|
}
|
|
}
|
|
else seg += *it;
|
|
++it;
|
|
}
|
|
if (!seg.empty())
|
|
segments.push_back(seg);
|
|
}
|
|
|
|
|
|
void URI::encode(const std::string& str, const std::string& reserved, std::string& encodedStr)
|
|
{
|
|
for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
|
|
{
|
|
char c = *it;
|
|
if ((c >= 'a' && c <= 'z') ||
|
|
(c >= 'A' && c <= 'Z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
c == '-' || c == '_' ||
|
|
c == '.' || c == '~')
|
|
{
|
|
encodedStr += c;
|
|
}
|
|
else if (c <= 0x20 || c >= 0x7F || ILLEGAL.find(c) != std::string::npos || reserved.find(c) != std::string::npos)
|
|
{
|
|
encodedStr += '%';
|
|
encodedStr += NumberFormatter::formatHex((unsigned) (unsigned char) c, 2);
|
|
}
|
|
else encodedStr += c;
|
|
}
|
|
}
|
|
|
|
|
|
void URI::decode(const std::string& str, std::string& decodedStr, bool plusAsSpace)
|
|
{
|
|
bool inQuery = false;
|
|
std::string::const_iterator it = str.begin();
|
|
std::string::const_iterator end = str.end();
|
|
while (it != end)
|
|
{
|
|
char c = *it++;
|
|
if (c == '?') inQuery = true;
|
|
// spaces may be encoded as plus signs in the query
|
|
if (inQuery && plusAsSpace && c == '+') c = ' ';
|
|
else if (c == '%')
|
|
{
|
|
if (it == end) throw SyntaxException("URI encoding: no hex digit following percent sign", str);
|
|
char hi = *it++;
|
|
if (it == end) throw SyntaxException("URI encoding: two hex digits must follow percent sign", str);
|
|
char lo = *it++;
|
|
if (hi >= '0' && hi <= '9')
|
|
c = hi - '0';
|
|
else if (hi >= 'A' && hi <= 'F')
|
|
c = hi - 'A' + 10;
|
|
else if (hi >= 'a' && hi <= 'f')
|
|
c = hi - 'a' + 10;
|
|
else throw SyntaxException("URI encoding: not a hex digit");
|
|
c *= 16;
|
|
if (lo >= '0' && lo <= '9')
|
|
c += lo - '0';
|
|
else if (lo >= 'A' && lo <= 'F')
|
|
c += lo - 'A' + 10;
|
|
else if (lo >= 'a' && lo <= 'f')
|
|
c += lo - 'a' + 10;
|
|
else throw SyntaxException("URI encoding: not a hex digit");
|
|
}
|
|
decodedStr += c;
|
|
}
|
|
}
|
|
|
|
|
|
bool URI::isWellKnownPort() const
|
|
{
|
|
return _port == getWellKnownPort();
|
|
}
|
|
|
|
|
|
unsigned short URI::getWellKnownPort() const
|
|
{
|
|
if (_scheme == "ftp")
|
|
return 21;
|
|
else if (_scheme == "ssh")
|
|
return 22;
|
|
else if (_scheme == "telnet")
|
|
return 23;
|
|
else if (_scheme == "http")
|
|
return 80;
|
|
else if (_scheme == "nntp")
|
|
return 119;
|
|
else if (_scheme == "ldap")
|
|
return 389;
|
|
else if (_scheme == "https")
|
|
return 443;
|
|
else if (_scheme == "rtsp")
|
|
return 554;
|
|
else if (_scheme == "sip")
|
|
return 5060;
|
|
else if (_scheme == "sips")
|
|
return 5061;
|
|
else if (_scheme == "xmpp")
|
|
return 5222;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
void URI::parse(const std::string& uri)
|
|
{
|
|
std::string::const_iterator it = uri.begin();
|
|
std::string::const_iterator end = uri.end();
|
|
if (it == end) return;
|
|
if (*it != '/' && *it != '.' && *it != '?' && *it != '#')
|
|
{
|
|
std::string scheme;
|
|
while (it != end && *it != ':' && *it != '?' && *it != '#' && *it != '/') scheme += *it++;
|
|
if (it != end && *it == ':')
|
|
{
|
|
++it;
|
|
if (it == end) throw SyntaxException("URI scheme must be followed by authority or path", uri);
|
|
setScheme(scheme);
|
|
if (*it == '/')
|
|
{
|
|
++it;
|
|
if (it != end && *it == '/')
|
|
{
|
|
++it;
|
|
parseAuthority(it, end);
|
|
}
|
|
else --it;
|
|
}
|
|
parsePathEtc(it, end);
|
|
}
|
|
else
|
|
{
|
|
it = uri.begin();
|
|
parsePathEtc(it, end);
|
|
}
|
|
}
|
|
else parsePathEtc(it, end);
|
|
}
|
|
|
|
|
|
void URI::parseAuthority(std::string::const_iterator& it, const std::string::const_iterator& end)
|
|
{
|
|
std::string userInfo;
|
|
std::string part;
|
|
while (it != end && *it != '/' && *it != '?' && *it != '#')
|
|
{
|
|
if (*it == '@')
|
|
{
|
|
userInfo = part;
|
|
part.clear();
|
|
}
|
|
else part += *it;
|
|
++it;
|
|
}
|
|
std::string::const_iterator pbeg = part.begin();
|
|
std::string::const_iterator pend = part.end();
|
|
parseHostAndPort(pbeg, pend);
|
|
_userInfo = userInfo;
|
|
}
|
|
|
|
|
|
void URI::parseHostAndPort(std::string::const_iterator& it, const std::string::const_iterator& end)
|
|
{
|
|
if (it == end) return;
|
|
std::string host;
|
|
if (*it == '[')
|
|
{
|
|
// IPv6 address
|
|
++it;
|
|
while (it != end && *it != ']') host += *it++;
|
|
if (it == end) throw SyntaxException("unterminated IPv6 address");
|
|
++it;
|
|
}
|
|
else
|
|
{
|
|
while (it != end && *it != ':') host += *it++;
|
|
}
|
|
if (it != end && *it == ':')
|
|
{
|
|
++it;
|
|
std::string port;
|
|
while (it != end) port += *it++;
|
|
if (!port.empty())
|
|
{
|
|
int nport = 0;
|
|
if (NumberParser::tryParse(port, nport) && nport > 0 && nport < 65536)
|
|
_port = (unsigned short) nport;
|
|
else
|
|
throw SyntaxException("bad or invalid port number", port);
|
|
}
|
|
else _port = getWellKnownPort();
|
|
}
|
|
else _port = getWellKnownPort();
|
|
_host = host;
|
|
toLowerInPlace(_host);
|
|
}
|
|
|
|
|
|
void URI::parsePath(std::string::const_iterator& it, const std::string::const_iterator& end)
|
|
{
|
|
std::string path;
|
|
while (it != end && *it != '?' && *it != '#') path += *it++;
|
|
decode(path, _path);
|
|
}
|
|
|
|
|
|
void URI::parsePathEtc(std::string::const_iterator& it, const std::string::const_iterator& end)
|
|
{
|
|
if (it == end) return;
|
|
if (*it != '?' && *it != '#')
|
|
parsePath(it, end);
|
|
if (it != end && *it == '?')
|
|
{
|
|
++it;
|
|
parseQuery(it, end);
|
|
}
|
|
if (it != end && *it == '#')
|
|
{
|
|
++it;
|
|
parseFragment(it, end);
|
|
}
|
|
}
|
|
|
|
|
|
void URI::parseQuery(std::string::const_iterator& it, const std::string::const_iterator& end)
|
|
{
|
|
_query.clear();
|
|
while (it != end && *it != '#') _query += *it++;
|
|
}
|
|
|
|
|
|
void URI::parseFragment(std::string::const_iterator& it, const std::string::const_iterator& end)
|
|
{
|
|
std::string fragment;
|
|
while (it != end) fragment += *it++;
|
|
decode(fragment, _fragment);
|
|
}
|
|
|
|
|
|
void URI::mergePath(const std::string& path)
|
|
{
|
|
std::vector<std::string> segments;
|
|
std::vector<std::string> normalizedSegments;
|
|
bool addLeadingSlash = false;
|
|
if (!_path.empty())
|
|
{
|
|
getPathSegments(segments);
|
|
bool endsWithSlash = *(_path.rbegin()) == '/';
|
|
if (!endsWithSlash && !segments.empty())
|
|
segments.pop_back();
|
|
addLeadingSlash = _path[0] == '/';
|
|
}
|
|
getPathSegments(path, segments);
|
|
addLeadingSlash = addLeadingSlash || (!path.empty() && path[0] == '/');
|
|
bool hasTrailingSlash = (!path.empty() && *(path.rbegin()) == '/');
|
|
bool addTrailingSlash = false;
|
|
for (std::vector<std::string>::const_iterator it = segments.begin(); it != segments.end(); ++it)
|
|
{
|
|
if (*it == "..")
|
|
{
|
|
addTrailingSlash = true;
|
|
if (!normalizedSegments.empty())
|
|
normalizedSegments.pop_back();
|
|
}
|
|
else if (*it != ".")
|
|
{
|
|
addTrailingSlash = false;
|
|
normalizedSegments.push_back(*it);
|
|
}
|
|
else addTrailingSlash = true;
|
|
}
|
|
buildPath(normalizedSegments, addLeadingSlash, hasTrailingSlash || addTrailingSlash);
|
|
}
|
|
|
|
|
|
void URI::buildPath(const std::vector<std::string>& segments, bool leadingSlash, bool trailingSlash)
|
|
{
|
|
_path.clear();
|
|
bool first = true;
|
|
for (std::vector<std::string>::const_iterator it = segments.begin(); it != segments.end(); ++it)
|
|
{
|
|
if (first)
|
|
{
|
|
first = false;
|
|
if (leadingSlash)
|
|
_path += '/';
|
|
else if (_scheme.empty() && (*it).find(':') != std::string::npos)
|
|
_path.append("./");
|
|
}
|
|
else _path += '/';
|
|
_path.append(*it);
|
|
}
|
|
if (trailingSlash)
|
|
_path += '/';
|
|
}
|
|
|
|
|
|
} // namespace Poco
|