srctree

David Zero parent 5d4c1ebc 1057585e
Add URI utility class

inlinesplit
util/BUILD added: 213, removed: 6, total 207
@@ -2,12 +2,16 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
 
cc_library(
name = "util",
hdrs = ["base_parser.h"],
srcs = ["uri.cpp"],
hdrs = [
"base_parser.h",
"uri.h",
],
visibility = ["//visibility:public"],
)
 
cc_test(
name = "util_test",
name = "base_parser_test",
size = "small",
srcs = ["base_parser_test.cpp"],
deps = [
@@ -15,3 +19,13 @@ cc_test(
"//etest",
],
)
 
cc_test(
name = "uri_test",
size = "small",
srcs = ["uri_test.cpp"],
deps = [
":util",
"//etest",
],
)
 
filename was Deleted added: 213, removed: 6, total 207
@@ -0,0 +1,57 @@
#include "util/uri.h"
 
#include <regex>
#include <utility>
 
namespace util {
 
std::optional<Uri> Uri::parse(std::string uristr){
std::smatch match;
 
// Regex taken from RFC 3986.
std::regex uri_regex("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?");
if (!std::regex_search(uristr, match, uri_regex)) {
return std::nullopt;
}
 
Authority authority{};
 
std::string hostport = match.str(4);
size_t userinfo_end = match.str(4).find_first_of("@");
if (userinfo_end != std::string::npos) {
// Userinfo present.
std::string userinfo(match.str(4).substr(0, userinfo_end));
hostport = match.str(4).substr(userinfo_end + 1, match.str(4).size() - userinfo_end);
 
size_t user_end = userinfo.find_first_of(":");
if (user_end != std::string::npos) {
// Password present.
authority.user = userinfo.substr(0, user_end);
authority.passwd = userinfo.substr(user_end + 1, userinfo.size() - user_end);
} else {
// Password not present.
authority.user = userinfo;
}
}
 
size_t host_end = hostport.find_first_of(":");
if (host_end != std::string::npos) {
// Port present.
authority.host = hostport.substr(0, host_end);
authority.port = hostport.substr(host_end + 1, hostport.size() - host_end);
} else {
// Port not present.
authority.host = hostport;
}
 
return Uri {
.uri{std::move(uristr)},
.scheme{match.str(2)},
.authority{std::move(authority)},
.path{match.str(5)},
.query{match.str(7)},
.fragment{match.str(9)},
};
}
 
} // namespace util
 
filename was Deleted added: 213, removed: 6, total 207
@@ -0,0 +1,29 @@
#ifndef UTIL_URI_H_
#define UTIL_URI_H_
 
#include <optional>
#include <string>
 
namespace util {
 
struct Authority {
std::string user;
std::string passwd;
std::string host;
std::string port;
};
 
struct Uri {
static std::optional<Uri> parse(std::string uri);
 
std::string uri;
std::string scheme;
Authority authority;
std::string path;
std::string query;
std::string fragment;
};
 
} //namespace util
 
#endif
 
filename was Deleted added: 213, removed: 6, total 207
@@ -0,0 +1,107 @@
#include "util/uri.h"
 
#include "etest/etest.h"
 
using etest::expect;
using util::Uri;
 
int main() {
etest::test("https: user, pass, port, path, query", [] {
auto https_uri = *Uri::parse("https://zero-one:muh_password@example-domain.net:8080/muh/long/path.html?foo=bar");
 
expect(https_uri.scheme == "https");
expect(https_uri.authority.user == "zero-one");
expect(https_uri.authority.passwd == "muh_password");
expect(https_uri.authority.host == "example-domain.net");
expect(https_uri.authority.port == "8080");
expect(https_uri.path == "/muh/long/path.html");
expect(https_uri.query == "foo=bar");
expect(https_uri.fragment == "");
});
 
etest::test("https: user, pass, path, query", [] {
auto https_uri = *Uri::parse("https://zero-one:muh_password@example-domain.net/muh/long/path.html?foo=bar");
 
expect(https_uri.scheme == "https");
expect(https_uri.authority.user == "zero-one");
expect(https_uri.authority.passwd == "muh_password");
expect(https_uri.authority.host == "example-domain.net");
expect(https_uri.authority.port == "");
expect(https_uri.path == "/muh/long/path.html");
expect(https_uri.query == "foo=bar");
expect(https_uri.fragment == "");
});
 
etest::test("https: user, path, query", [] {
auto https_uri = *Uri::parse("https://zero-one@example-domain.net/muh/long/path.html?foo=bar");
 
expect(https_uri.scheme == "https");
expect(https_uri.authority.user == "zero-one");
expect(https_uri.authority.passwd == "");
expect(https_uri.authority.host == "example-domain.net");
expect(https_uri.authority.port == "");
expect(https_uri.path == "/muh/long/path.html");
expect(https_uri.query == "foo=bar");
expect(https_uri.fragment == "");
});
 
etest::test("https: path, query", [] {
auto https_uri = *Uri::parse("https://example-domain.net/muh/long/path.html?foo=bar");
 
expect(https_uri.scheme == "https");
expect(https_uri.authority.user == "");
expect(https_uri.authority.passwd == "");
expect(https_uri.authority.host == "example-domain.net");
expect(https_uri.authority.port == "");
expect(https_uri.path == "/muh/long/path.html");
expect(https_uri.query == "foo=bar");
expect(https_uri.fragment == "");
});
 
etest::test("https: path, fragment", [] {
auto https_uri = *Uri::parse("https://example-domain.net/muh/long/path.html#About");
 
expect(https_uri.scheme == "https");
expect(https_uri.authority.user == "");
expect(https_uri.authority.passwd == "");
expect(https_uri.authority.host == "example-domain.net");
expect(https_uri.authority.port == "");
expect(https_uri.path == "/muh/long/path.html");
expect(https_uri.query == "");
expect(https_uri.fragment == "About");
});
 
etest::test("mailto: path", [] {
auto mailto_uri = *Uri::parse("mailto:example@example.net");
 
expect(mailto_uri.scheme == "mailto");
expect(mailto_uri.authority.user == "");
expect(mailto_uri.authority.passwd == "");
expect(mailto_uri.authority.host == "");
expect(mailto_uri.authority.port == "");
expect(mailto_uri.path == "example@example.net");
expect(mailto_uri.query == "");
expect(mailto_uri.fragment == "");
});
 
etest::test("tel: path", [] {
auto tel_uri = *Uri::parse("tel:+1-830-476-5664");
 
expect(tel_uri.scheme == "tel");
expect(tel_uri.authority.user == "");
expect(tel_uri.authority.passwd == "");
expect(tel_uri.authority.host == "");
expect(tel_uri.authority.port == "");
expect(tel_uri.path == "+1-830-476-5664");
expect(tel_uri.query == "");
expect(tel_uri.fragment == "");
});
 
// TODO(Zer0-One): Test for parsing failure.
// etest::test("parse failure", [] {
// auto tel_uri = Uri::parse("");
// expect(!tel_uri.has_value());
// });
 
return etest::run_all_tests();
}