HTTP: keep a complete header line filed under its own name

The socket transport's 401 retry stores "Authorization: Basic ..." (and
Digest, Bearer) under the 'Authorization' key. The first version of this
change prefixed every named value, so that retry would have sent
"Authorization: Authorization: Basic ...". Both transports now share
Request::headerLines(), which leaves such a value as it is.
This commit is contained in:
Fathi Ben Nasr 2026-10-07 14:59:23 +00:00
parent a761a838d5
commit 3a173a6dff
3 changed files with 27 additions and 14 deletions

View file

@ -166,4 +166,29 @@ abstract class Request
{
return self::$scheme_ports[$scheme] ?? 0;
}
/**
* Extra headers as the flat list of "Name: value" lines both transports send.
*
* Callers pass either form: a list of ready lines, or an associative array
* such as ['Accept-Encoding' => 'gzip']. The
* associative form used to reach CURLOPT_HTTPHEADER as bare values, so the
* header was sent as a malformed line and dropped.
*
* Some callers file a complete line under its own name
* (['Authorization' => 'Authorization: Basic ...'] in the socket 401
* retry). Such a value is kept as it is, otherwise it would go out
* as "Authorization: Authorization: Basic ...".
*/
public static function headerLines(array $headers) : array
{
$lines = array();
foreach ($headers as $name => $value) {
$value = (string) $value;
$lines[] = (\is_int($name) || 0 === \stripos($value, $name . ':'))
? $value
: "{$name}: {$value}";
}
return $lines;
}
}

View file

@ -47,14 +47,7 @@ class CURL extends \SnappyMail\HTTP\Request
\curl_setopt($c, CURLOPT_CAINFO, $this->ca_bundle);
}
if ($extra_headers) {
// CURLOPT_HTTPHEADER expects a flat list of "Name: value" strings.
// Callers may pass an associative array, in which case curl sends
// the bare values as malformed header lines and drops them.
$aHeaderLines = array();
foreach ($extra_headers as $mKey => $sValue) {
$aHeaderLines[] = \is_int($mKey) ? $sValue : "{$mKey}: {$sValue}";
}
\curl_setopt($c, CURLOPT_HTTPHEADER, $aHeaderLines);
\curl_setopt($c, CURLOPT_HTTPHEADER, static::headerLines($extra_headers));
}
if ($this->auth['user'] && $this->auth['type']) {
if ($this->auth['type'] & self::AUTH_BEARER ) {

View file

@ -43,12 +43,7 @@ class Socket extends \SnappyMail\HTTP\Request
$extra_headers['Authorization'] = static::$Authorization[$host];
}
if ($extra_headers) {
// $headers is a flat list of "Name: value" strings, so an
// associative $extra_headers would lose its keys in the implode()
// below and emit bare values as malformed header lines.
foreach ($extra_headers as $mKey => $sValue) {
$headers[] = \is_int($mKey) ? $sValue : "{$mKey}: {$sValue}";
}
$headers = \array_merge($headers, static::headerLines($extra_headers));
}
$headers = \implode("\r\n", $headers);
if (!\is_null($body)) {