Skip to content

Commit

Permalink
Fix some radio stations not playing with the relaying
Browse files Browse the repository at this point in the history
The problem was how we handled the HTTP headers in case the original stream
URL redirected the connection to another URL. And specifically, if the
redirection response returned the Content-Length header with value 0 and the
final URL didn't have this header (as is typical for live streams). In that
case, the 0-length was erroneously returned to the client as the length of
the stream.

The problem originated from how the "associative" mode of the PHP built-in
function get_headers works. It makes it basically impossible to know for
certain, which headers come from the final URL and which ones from the
intermediate redirection response(s). To work around this, we now use the
function in non-associative mode and parse the result to a dictionary with
our own logic.

refs owncloud#1194
  • Loading branch information
paulijar committed Jan 16, 2025
1 parent 302f14e commit 5f8f72b
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
- Dashboard widget:
* Playback controls disappearing when the playing track changes with Aurora.js backend (used when the audio format has no native browser support)
* Clicking the previously played song didn't play it again after stopping the playback with the keyboard 'stop' media key
- Radio stream relaying not working on some redirecting stream URLs, depending on the headers
[#1194](https://github.com/owncloud/music/issues/1194)

## 2.1.1 - 2025-01-03

Expand Down
42 changes: 21 additions & 21 deletions lib/Utility/HttpUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,28 +74,28 @@ public static function createContext(?int $timeout_s=null, array $extraHeaders =
public static function getUrlHeaders(string $url, $context) : ?array {
$result = null;
if (self::isUrlSchemeOneOf($url, self::ALLOWED_SCHEMES)) {
// the type of the second parameter of get_header has changed in PHP 8.0
$associative = \version_compare(\phpversion(), '8.0', '<') ? 1 : true;
$result = @\get_headers($url, /** @scrutinizer ignore-type */ $associative, $context);
// The built-in associative mode of get_headers because it mixes up the headers from the redirection
// responses with those of the last response after all the redirections, making it impossible to know,
// what is the source of each header. Hence, we roll out our own parsing logic which discards all the
// headers from the intermediate redirection responses.

if ($result === false) {
$result = null;
} else {
// Do some post-processing on the headers
foreach ($result as $key => $value) {
// Some of the headers got may be array-valued after a redirection or several, containing value
// from each redirected jump. In such cases, preserve only the last value.
if (\is_array($value)) {
$result[$key] = \end($value);
}

// The status header like "HTTP/1.1 200 OK" can found from the index 0. If there were any redirects,
// then the statuses after the redirections can be found from indices 1, 2, 3, ... That is, the status
// after the last redirection can be found from the highest numerical index. We are interested about the
// status code after the last redirection.
if (\is_int($key)) {
$result['status_code'] = (int)(\explode(' ', $value, 3)[1] ?? 500);
unset($result[$key]);
// the type of the second parameter of get_header has changed in PHP 8.0
$associative = \version_compare(\phpversion(), '8.0', '<') ? 0 : false;
$rawHeaders = @\get_headers($url, /** @scrutinizer ignore-type */ $associative, $context);

if ($rawHeaders !== false) {
$result = [];

foreach ($rawHeaders as $row) {
if (Util::startsWith($row, 'HTTP/', /*ignoreCase=*/true)) {
// Start of new response. If we have already parsed some headers, then those are from some
// intermediate redirect response and those should be discarded.
$result = ['status_code' => (int)(\explode(' ', $row, 3)[1] ?? 500)];
} else {
list($key, $value) = \explode(':', $row, 2);
if (isset($value)) {

Check failure on line 96 in lib/Utility/HttpUtil.php

View workflow job for this annotation

GitHub Actions / build

Variable $value in isset() always exists and is not nullable.
$result[\trim($key)] = \trim($value);
}
}
}
}
Expand Down

0 comments on commit 5f8f72b

Please sign in to comment.