Fix web radio streaming: HTTPS URLs were rejected outright

POST /api/streaming's JSON parser only accepted "http://" URLs,
rejecting any "https://" URL with invalid_json before ever attempting
a connection. Nearly every real internet radio stream today is HTTPS-
only, so this made the feature fail for essentially any station a
user would actually try.

Two changes were needed together: the parser now accepts both http://
and https://, and web_radio_stream.cpp's esp_http_client now attaches
ESP-IDF's built-in CA certificate bundle (crt_bundle_attach) so the
TLS handshake actually verifies -- CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
was already enabled in sdkconfig but never wired up here. Requires
adding mbedtls to main's PRIV_REQUIRES (esp_crt_bundle.h lives there).

Confirmed live: an https:// stream URL now gets accepted by the API
and produces a real HTTP response (404, from a guessed-wrong path) --
before this fix it never reached the network at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 00:27:02 +02:00
co-authored by Claude Sonnet 5
parent 012d2a6267
commit b46dcea698
5 changed files with 61 additions and 26 deletions
@@ -19,6 +19,7 @@ namespace {
constexpr std::size_t kMaxUrlLength = 200U;
constexpr std::string_view kHttpPrefix = "http://";
constexpr std::string_view kHttpsPrefix = "https://";
[[nodiscard]] std::string_view extractJsonString(std::string_view json,
std::string_view key)
@@ -84,8 +85,9 @@ std::expected<WebRadioConfig, ParseError> parseWebRadioConfigJson(
}
const std::string_view url = extractJsonString(json, "url");
if (url.empty() || url.size() > kMaxUrlLength
|| url.substr(0, kHttpPrefix.size()) != kHttpPrefix) {
const bool isHttp = url.substr(0, kHttpPrefix.size()) == kHttpPrefix;
const bool isHttps = url.substr(0, kHttpsPrefix.size()) == kHttpsPrefix;
if (url.empty() || url.size() > kMaxUrlLength || !(isHttp || isHttps)) {
return std::unexpected(ParseError::InvalidJson);
}
@@ -40,9 +40,18 @@ namespace {
return EXIT_FAILURE;
}
const auto badScheme = core::parseWebRadioConfigJson(
R"({"enabled":true,"url":"https://example.com/x.mp3"})");
R"({"enabled":true,"url":"ftp://example.com/x.mp3"})");
if (badScheme) {
std::cerr << "non-http scheme should fail\n";
std::cerr << "non-http(s) scheme should fail\n";
return EXIT_FAILURE;
}
// Most public internet radio streams are HTTPS-only; rejecting the
// scheme outright (as this parser used to) made streaming unusable for
// essentially any real station.
const auto httpsOk = core::parseWebRadioConfigJson(
R"({"enabled":true,"url":"https://example.com/x.mp3"})");
if (!httpsOk || httpsOk->url != "https://example.com/x.mp3") {
std::cerr << "https url should parse\n";
return EXIT_FAILURE;
}
const auto malformed = core::parseWebRadioConfigJson("not json");