diff --git a/libraries/joomla/application/web.php b/libraries/joomla/application/web.php index 0c23e6ba192ee..96d9546a4ae3f 100644 --- a/libraries/joomla/application/web.php +++ b/libraries/joomla/application/web.php @@ -92,6 +92,33 @@ class JApplicationWeb extends JApplicationBase 308 => 'Permanent Redirect' ); + /** + * A map of HTTP Response headers which may only send a single value, all others + * are considered to allow multiple + * + * @var object + * @since 3.5.2 + * @see https://tools.ietf.org/html/rfc7230 + */ + private $singleValueResponseHeaders = array( + 'status', // This is not a valid header name, but the representation used by Joomla to identify the HTTP Response Code + 'Content-Length', + 'Host', + 'Content-Type', + 'Content-Location', + 'Date', + 'Location', + 'Retry-After', + 'Server', + 'Mime-Version', + 'Last-Modified', + 'ETag', + 'Accept-Ranges', + 'Content-Range', + 'Age', + 'Expires' + ); + /** * Class constructor. * @@ -625,23 +652,36 @@ public function setHeader($name, $value, $replace = false) $name = (string) $name; $value = (string) $value; - // If the replace flag is set, unset all known headers with the given name. - if ($replace) + // Create an array of duplicate header names + $keys = false; + if ($this->response->headers) { + $names = array(); foreach ($this->response->headers as $key => $header) { - if ($name == $header['name']) - { - unset($this->response->headers[$key]); - } + $names[$key] = $header['name']; } + // Find existing headers by name + $keys = array_keys($names, $name); + } - // Clean up the array as unsetting nested arrays leaves some junk. - $this->response->headers = array_values($this->response->headers); + // Remove if $replace is true and there are duplicate names + if ($replace && $keys) + { + $this->response->headers = array_diff_key($this->response->headers, array_flip($keys)); } - // Add the header to the internal array. - $this->response->headers[] = array('name' => $name, 'value' => $value); + /** + * If no keys found, safe to insert (!$keys) + * If ($keys && $replace) it's a replacement and previous have been deleted + * if($keys && !in_array...) it's a multiple value header + */ + $single = in_array($name, $this->singleValueResponseHeaders); + if ($value && (!$keys || ($keys && ($replace || !$single)))) + { + // Add the header to the internal array. + $this->response->headers[] = array('name' => $name, 'value' => $value); + } return $this; } @@ -650,8 +690,8 @@ public function setHeader($name, $value, $replace = false) * Method to get the array of response headers to be sent when the response is sent * to the client. * - * @return array - * + * @return array * + * * @since 11.3 */ public function getHeaders() @@ -684,6 +724,8 @@ public function sendHeaders() { if (!$this->checkHeadersSent()) { + // Creating an array of headers, making arrays of headers with multiple values + $val = array(); foreach ($this->response->headers as $header) { if ('status' == strtolower($header['name'])) @@ -693,7 +735,8 @@ public function sendHeaders() } else { - $this->header($header['name'] . ': ' . $header['value']); + $val[$header['name']] = !isset($val[$header['name']])?$header['value']:implode(', ', array($val[$header['name']], $header['value'])); + $this->header($header['name'] . ': ' . $val[$header['name']], true); } } }