| 354 | } |
| 355 | |
| 356 | bool parse_https_url(const std::string& url, ParsedHttpsUrl* parsed) { |
| 357 | if (parsed == nullptr) { |
| 358 | return false; |
| 359 | } |
| 360 | |
| 361 | constexpr std::string_view kHttpsPrefix = "https://"; |
| 362 | if (!url.starts_with(kHttpsPrefix)) { |
| 363 | return false; |
| 364 | } |
| 365 | |
| 366 | const size_t authority_start = kHttpsPrefix.size(); |
| 367 | const size_t path_start = url.find('/', authority_start); |
| 368 | const std::string authority = |
| 369 | (path_start == std::string::npos) ? url.substr(authority_start) |
| 370 | : url.substr(authority_start, path_start - authority_start); |
| 371 | if (authority.empty()) { |
| 372 | return false; |
| 373 | } |
| 374 | |
| 375 | const size_t colon_pos = authority.rfind(':'); |
| 376 | if (colon_pos != std::string::npos) { |
| 377 | parsed->host = authority.substr(0, colon_pos); |
| 378 | const std::string port_text = authority.substr(colon_pos + 1); |
| 379 | if (parsed->host.empty() || port_text.empty()) { |
| 380 | return false; |
| 381 | } |
| 382 | parsed->port = std::atoi(port_text.c_str()); |
| 383 | if (parsed->port <= 0) { |
| 384 | return false; |
| 385 | } |
| 386 | } else { |
| 387 | parsed->host = authority; |
| 388 | parsed->port = 443; |
| 389 | } |
| 390 | |
| 391 | parsed->target = (path_start == std::string::npos) ? "/" : url.substr(path_start); |
| 392 | return !parsed->host.empty() && !parsed->target.empty(); |
| 393 | } |
| 394 | |
| 395 | std::string header_value_ci(std::string_view headers, std::string_view key) { |
| 396 | std::string lower_headers(headers); |
no outgoing calls
no test coverage detected