42 std::string rest = base_url;
44 auto scheme_end = rest.find(
"://");
45 if (scheme_end != std::string::npos) {
46 std::string scheme = rest.substr(0, scheme_end);
47 std::transform(scheme.begin(), scheme.end(), scheme.begin(),
49 return static_cast<char>(std::tolower(c));
51 if (scheme !=
"http" && scheme !=
"https") {
52 throw std::invalid_argument(
"unsupported HTTP endpoint scheme");
54 out.tls = scheme ==
"https";
55 rest = rest.substr(scheme_end + 3);
58 auto path_start = rest.find_first_of(
"/?#");
59 std::string authority;
60 if (path_start != std::string::npos) {
61 authority = rest.substr(0, path_start);
62 if (rest[path_start] !=
'/') {
63 throw std::invalid_argument(
64 "HTTP endpoint base URL must not contain a query or fragment");
66 out.prefix = rest.substr(path_start);
67 if (out.prefix.find(
'#') != std::string::npos) {
68 throw std::invalid_argument(
69 "HTTP endpoint base URL must not contain a fragment");
75 if (authority.empty() || authority.find(
'@') != std::string::npos) {
76 throw std::invalid_argument(
"invalid HTTP endpoint authority");
78 if (authority.front() ==
'[') {
79 const auto close = authority.find(
']');
80 if (close == std::string::npos) {
81 throw std::invalid_argument(
"invalid bracketed HTTP endpoint host");
83 out.host = authority.substr(1, close - 1);
84 if (close + 1 < authority.size()) {
85 if (authority[close + 1] !=
':' || close + 2 == authority.size()) {
86 throw std::invalid_argument(
"invalid HTTP endpoint port");
88 out.port = authority.substr(close + 2);
91 const auto colon = authority.find(
':');
92 if (colon != std::string::npos) {
93 if (authority.find(
':', colon + 1) != std::string::npos) {
94 throw std::invalid_argument(
95 "IPv6 HTTP endpoint hosts must use brackets");
97 out.host = authority.substr(0, colon);
98 out.port = authority.substr(colon + 1);
100 out.host = authority;
103 if (out.host.empty()) {
104 throw std::invalid_argument(
"HTTP endpoint host is empty");
106 if (out.port.empty()) out.port = out.tls ?
"443" :
"80";
107 unsigned int numeric_port = 0;
108 const auto [port_end, port_error] = std::from_chars(
109 out.port.data(), out.port.data() + out.port.size(), numeric_port);
110 if (port_error != std::errc{} ||
111 port_end != out.port.data() + out.port.size() ||
112 numeric_port == 0 || numeric_port > 65535) {
113 throw std::invalid_argument(
"invalid HTTP endpoint port");
115 out.port = std::to_string(numeric_port);
123 std::transform(scheme.begin(), scheme.end(), scheme.begin(),
132 std::transform(lower.begin(), lower.end(), lower.begin(),