url: support IPv6 literals (RFC 2732) (#292)
This commit is contained in:
parent
8bed1cc743
commit
874a1d8c33
@ -54,6 +54,12 @@ public:
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool isXDigit(const char_t c) {
|
||||
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
|
||||
(c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
static bool isAlpha(const char_t c) {
|
||||
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
|
@ -126,7 +126,7 @@ const string url::build() const {
|
||||
oss << "@";
|
||||
}
|
||||
|
||||
oss << urlUtils::encode(m_host);
|
||||
oss << urlUtils::encodeHost(m_host);
|
||||
|
||||
if (m_port != UNSPECIFIED_PORT) {
|
||||
|
||||
@ -166,6 +166,53 @@ const string url::build() const {
|
||||
}
|
||||
|
||||
|
||||
static bool extractHostIPv6(string& hostPart, string& host, string& port) {
|
||||
|
||||
if (hostPart[0] != '[') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto len = hostPart.find(']');
|
||||
|
||||
if (len == string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
host.assign(&hostPart[1], len - 1);
|
||||
|
||||
if (hostPart[len] == '\0') {
|
||||
return true;
|
||||
}
|
||||
if (hostPart[len + 1] != ':') {
|
||||
return false;
|
||||
}
|
||||
|
||||
port = hostPart.substr(len + 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void extractHost(string& hostPart, string& host, string& port) {
|
||||
|
||||
if (extractHostIPv6(hostPart, host, port)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t colonPos = hostPart.find(':');
|
||||
|
||||
if (colonPos == string::npos) {
|
||||
|
||||
host = utility::stringUtils::trim(hostPart);
|
||||
|
||||
} else {
|
||||
|
||||
host = utility::stringUtils::trim(string(hostPart.begin(), hostPart.begin() + colonPos));
|
||||
port = utility::stringUtils::trim(string(hostPart.begin() + colonPos + 1, hostPart.end()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void url::parse(const string& str) {
|
||||
|
||||
// Protocol
|
||||
@ -222,20 +269,9 @@ void url::parse(const string& str) {
|
||||
}
|
||||
|
||||
// Host/port
|
||||
const size_t colonPos = hostPart.find(':');
|
||||
|
||||
string host;
|
||||
string port;
|
||||
|
||||
if (colonPos == string::npos) {
|
||||
|
||||
host = utility::stringUtils::trim(hostPart);
|
||||
|
||||
} else {
|
||||
|
||||
host = utility::stringUtils::trim(string(hostPart.begin(), hostPart.begin() + colonPos));
|
||||
port = utility::stringUtils::trim(string(hostPart.begin() + colonPos + 1, hostPart.end()));
|
||||
}
|
||||
extractHost(hostPart, host, port);
|
||||
|
||||
// Path
|
||||
string path = utility::stringUtils::trim(string(str.begin() + slashPos, str.end()));
|
||||
|
@ -66,6 +66,22 @@ const string urlUtils::encode(const string& s) {
|
||||
}
|
||||
|
||||
|
||||
const string urlUtils::encodeHost(const string& s) {
|
||||
|
||||
if (s.size() == 0) {
|
||||
return encode(s);
|
||||
}
|
||||
|
||||
for (auto it = s.begin() + 1 ; it != s.end() - 1 ; ++it) {
|
||||
if (!parserHelpers::isXDigit(*it) && *it != ':') {
|
||||
return encode(s);
|
||||
}
|
||||
}
|
||||
|
||||
return "[" + s + "]";
|
||||
}
|
||||
|
||||
|
||||
const string urlUtils::decode(const string& s) {
|
||||
|
||||
string result;
|
||||
|
@ -44,6 +44,10 @@ public:
|
||||
*/
|
||||
static const string encode(const string& s);
|
||||
|
||||
/** Encode the host portion of a URL string.
|
||||
*/
|
||||
static const string encodeHost(const string& s);
|
||||
|
||||
/** Decode an hex-encoded URL (see encode()).
|
||||
*/
|
||||
static const string decode(const string& s);
|
||||
|
@ -35,6 +35,7 @@ VMIME_TEST_SUITE_BEGIN(urlTest)
|
||||
VMIME_TEST(testParse3)
|
||||
VMIME_TEST(testParse4)
|
||||
VMIME_TEST(testParse5)
|
||||
VMIME_TEST(testParseIPv6)
|
||||
VMIME_TEST(testGenerate)
|
||||
VMIME_TEST(testUtilsEncode)
|
||||
VMIME_TEST(testUtilsDecode)
|
||||
@ -201,6 +202,18 @@ VMIME_TEST_SUITE_BEGIN(urlTest)
|
||||
VASSERT_EQ("4", "myserver.com", u1.getHost());
|
||||
}
|
||||
|
||||
void testParseIPv6() {
|
||||
|
||||
vmime::utility::url u1("", "");
|
||||
|
||||
VASSERT_EQ("1", true, parseHelper(u1, "http://a:b@[::1]:80/p"));
|
||||
VASSERT_EQ("2", "a", u1.getUsername());
|
||||
VASSERT_EQ("3", "b", u1.getPassword());
|
||||
VASSERT_EQ("4", "::1", u1.getHost());
|
||||
VASSERT_EQ("5", 80, u1.getPort());
|
||||
VASSERT_EQ("6", "/p", u1.getPath());
|
||||
}
|
||||
|
||||
void testGenerate() {
|
||||
|
||||
vmime::utility::url u1("proto", "host", 12345, "path", "user", "password");
|
||||
@ -235,6 +248,13 @@ VMIME_TEST_SUITE_BEGIN(urlTest)
|
||||
"proto://host/?%26=%3D",
|
||||
static_cast <vmime::string>(u3)
|
||||
);
|
||||
|
||||
vmime::utility::url u5("http", "::1", 80, "p");
|
||||
VASSERT_EQ(
|
||||
"4",
|
||||
"http://[::1]:80/p",
|
||||
static_cast <vmime::string>(u5)
|
||||
);
|
||||
}
|
||||
|
||||
void testUtilsEncode() {
|
||||
|
Loading…
Reference in New Issue
Block a user