From 933ea7c9ceefc9b38080f56ee214099a2b1118a4 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 9 Oct 2020 19:33:19 +0200 Subject: [PATCH] Fix IHTTPResult::getHeader/s() - Split functionality "getHeader()" and "getHeaders()" analog to IMessageInterface::getHeader/s() - Fix functionality at various places - Adapt CurlResultTest --- mod/parse_url.php | 11 ++++++----- src/Network/CurlResult.php | 18 ++++++++++++------ src/Network/HTTPRequest.php | 3 +-- src/Network/IHTTPResult.php | 22 +++++++++++++++++++--- src/Protocol/DFRN.php | 4 ++-- src/Protocol/OStatus.php | 6 ++++-- src/Protocol/Salmon.php | 2 +- src/Util/ParseUrl.php | 3 +-- tests/datasets/curl/about.head.php | 20 ++++++++++++++++++++ tests/datasets/curl/about.redirect.php | 21 +++++++++++++++++++++ tests/src/Network/CurlResultTest.php | 14 +++++++++----- 11 files changed, 96 insertions(+), 28 deletions(-) create mode 100644 tests/datasets/curl/about.head.php create mode 100644 tests/datasets/curl/about.redirect.php diff --git a/mod/parse_url.php b/mod/parse_url.php index a1faab6ef..39aae4a5a 100644 --- a/mod/parse_url.php +++ b/mod/parse_url.php @@ -90,12 +90,13 @@ function parse_url_content(App $a) if ($curlResponse->isSuccess()) { // Convert the header fields into an array $hdrs = []; - $h = explode("\n", $curlResponse->getHeader()); + $h = $curlResponse->getHeaders(); foreach ($h as $l) { - $header = array_map('trim', explode(':', trim($l), 2)); - if (count($header) == 2) { - list($k, $v) = $header; - $hdrs[$k] = $v; + foreach ($l as $k => $v) { + if (empty($hdrs[$k])) { + $hdrs[$k] = $v; + } + $hdrs[$k] .= " " . $v; } } $type = null; diff --git a/src/Network/CurlResult.php b/src/Network/CurlResult.php index 1187e45eb..b0e801010 100644 --- a/src/Network/CurlResult.php +++ b/src/Network/CurlResult.php @@ -242,23 +242,29 @@ class CurlResult implements IHTTPResult } /** {@inheritDoc} */ - public function getHeader(string $field = '') + public function getHeader($header) { - if (empty($field)) { - return $this->header; + if (empty($header)) { + return ''; } - $field = strtolower(trim($field)); + $header = strtolower(trim($header)); $headers = $this->getHeaderArray(); - if (isset($headers[$field])) { - return $headers[$field]; + if (isset($headers[$header])) { + return $headers[$header]; } return ''; } + /** {@inheritDoc} */ + public function getHeaders() + { + return $this->getHeaderArray(); + } + /** {@inheritDoc} */ public function inHeader(string $field) { diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 6341c0729..798b0cdea 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -454,8 +454,7 @@ class HTTPRequest implements IHTTPRequest 'timeout' => $timeout, 'accept_content' => $accept_content, 'cookiejar' => $cookiejar - ], - $redirects + ] ); } diff --git a/src/Network/IHTTPResult.php b/src/Network/IHTTPResult.php index be190c80c..5904bcfa3 100644 --- a/src/Network/IHTTPResult.php +++ b/src/Network/IHTTPResult.php @@ -2,6 +2,8 @@ namespace Friendica\Network; +use Psr\Http\Message\MessageInterface; + /** * Temporary class to map Friendica used variables based on PSR-7 HTTPResponse */ @@ -23,15 +25,25 @@ interface IHTTPResult /** * Returns the headers + * @see MessageInterface::getHeader() * - * @param string $field optional header field. Return all fields if empty + * @param string $header optional header field. Return all fields if empty * * @return string the headers or the specified content of the header variable */ - public function getHeader(string $field = ''); + public function getHeader($header); + + /** + * Returns all headers + * @see MessageInterface::getHeaders() + * + * @return string[][] + */ + public function getHeaders(); /** * Check if a specified header exists + * @see MessageInterface::hasHeader() * * @param string $field header field * @@ -41,8 +53,10 @@ interface IHTTPResult /** * Returns the headers as an associated array + * @see MessageInterface::getHeaders() + * @deprecated * - * @return array associated header array + * @return string[][] associated header array */ public function getHeaderArray(); @@ -62,6 +76,8 @@ interface IHTTPResult public function getRedirectUrl(); /** + * @see MessageInterface::getBody() + * * @return string */ public function getBody(); diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index d20864cf7..f718e0a74 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -1358,7 +1358,7 @@ class DFRN return -9; // timed out } - if (($curl_stat == 503) && stristr($postResult->getHeader(), 'retry-after')) { + if (($curl_stat == 503) && $postResult->inHeader('retry-after')) { return -10; } @@ -1453,7 +1453,7 @@ class DFRN return -9; // timed out } - if (($curl_stat == 503) && (stristr($postResult->getHeader(), 'retry-after'))) { + if (($curl_stat == 503) && $postResult->inHeader('retry-after')) { return -10; } diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 0635be87d..ef38aaebe 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -746,7 +746,8 @@ class OStatus $xml = ''; - if (stristr($curlResult->getHeader(), 'Content-Type: application/atom+xml')) { + if ($curlResult->inHeader('Content-Type') && + stristr($curlResult->getHeader('Content-Type'), 'application/atom+xml')) { $xml = $curlResult->getBody(); } @@ -939,7 +940,8 @@ class OStatus $xml = ''; - if (stristr($curlResult->getHeader(), 'Content-Type: application/atom+xml')) { + if ($curlResult->inHeader('Content-Type') && + stristr($curlResult->getHeader('Content-Type'), 'application/atom+xml')) { Logger::log('Directly fetched XML for URI ' . $related_uri, Logger::DEBUG); $xml = $curlResult->getBody(); } diff --git a/src/Protocol/Salmon.php b/src/Protocol/Salmon.php index 169a4d0cb..808462592 100644 --- a/src/Protocol/Salmon.php +++ b/src/Protocol/Salmon.php @@ -215,7 +215,7 @@ class Salmon return -1; } - if (($return_code == 503) && (stristr($postResult->getHeader(), 'retry-after'))) { + if (($return_code == 503) && $postResult->inHeader('retry-after')) { return -1; } diff --git a/src/Util/ParseUrl.php b/src/Util/ParseUrl.php index 1596e015b..c3cbda70e 100644 --- a/src/Util/ParseUrl.php +++ b/src/Util/ParseUrl.php @@ -175,7 +175,6 @@ class ParseUrl return $siteinfo; } - $header = $curlResult->getHeader(); $body = $curlResult->getBody(); if ($do_oembed) { @@ -204,7 +203,7 @@ class ParseUrl $charset = ''; // Look for a charset, first in headers // Expected form: Content-Type: text/html; charset=ISO-8859-4 - if (preg_match('/charset=([a-z0-9-_.\/]+)/i', $header, $matches)) { + if (preg_match('/charset=([a-z0-9-_.\/]+)/i', $curlResult->getContentType(), $matches)) { $charset = trim(trim(trim(array_pop($matches)), ';,')); } diff --git a/tests/datasets/curl/about.head.php b/tests/datasets/curl/about.head.php new file mode 100644 index 000000000..d0be0feb4 --- /dev/null +++ b/tests/datasets/curl/about.head.php @@ -0,0 +1,20 @@ + '', + 'date' => 'Thu, 11 Oct 2018 18:43:54 GMT', + 'content-type' => 'text/html; charset=utf-8', + 'vary' => 'Accept-Encoding', + 'server' => 'Mastodon', + 'x-frame-options' => 'SAMEORIGIN', + 'x-content-type-options' => 'nosniff', + 'x-xss-protection' => '1; mode=block', + 'etag' => 'W/"706e6c48957e1d46ecf9d7597a7880af"', + 'cache-control' => 'max-age=0, private, must-revalidate', + 'set-cookie' => '_mastodon_session=v3kcy%2FW3aZYBBvZUohuwksEKwzYIyEUlEuJ1KqTAfWPKvVQq%2F4UuJ39zp621VyfpQNlvY46TL%2FYutzXowSLYQBNFCJcrEiF04aU0TdtHls9zynMiyeHhoVgCijOXWXNt9%2FCmpQ49RkNEujkv9NaJ0cum32MCVZKjE9%2BMKmLM%2F8ZygZeLBGJ7sg%3D%3D--QGIiU0%2FpXc3Aym8F--he2iRRPePOdtEs3z%2BufSXg%3D%3D; path=/; secure; HttpOnly', + 'x-request-id' => 'a0c0b8e7-cd60-4efa-b79b-cf1b0d5a0784', + 'x-runtime' => '0.049566', + 'strict-transport-security' => 'max-age=31536000; includeSubDomains; preload', + 'referrer-policy' => 'same-origin', + 'content-security-policy' => "frame-ancestors 'none'; script-src 'self'; object-src 'self'; img-src * data: blob:; media-src 'self' data:; font-src 'self' data: https://fonts.gstatic.com/; connect-src 'self' blob: wss://mastodonten.de", +]; diff --git a/tests/datasets/curl/about.redirect.php b/tests/datasets/curl/about.redirect.php new file mode 100644 index 000000000..5ae3fd88f --- /dev/null +++ b/tests/datasets/curl/about.redirect.php @@ -0,0 +1,21 @@ + '', + 'date' => 'Thu, 11 Oct 2018 18:43:54 GMT', + 'content-type' => 'text/html; charset=utf-8', + 'vary' => 'Accept-Encoding', + 'server' => 'Mastodon', + 'location' => 'https://test.other/some/', + 'x-frame-options' => 'SAMEORIGIN', + 'x-content-type-options' => 'nosniff', + 'x-xss-protection' => '1; mode=block', + 'etag' => 'W/"706e6c48957e1d46ecf9d7597a7880af"', + 'cache-control' => 'max-age=0, private, must-revalidate', + 'set-cookie' => '_mastodon_session=v3kcy%2FW3aZYBBvZUohuwksEKwzYIyEUlEuJ1KqTAfWPKvVQq%2F4UuJ39zp621VyfpQNlvY46TL%2FYutzXowSLYQBNFCJcrEiF04aU0TdtHls9zynMiyeHhoVgCijOXWXNt9%2FCmpQ49RkNEujkv9NaJ0cum32MCVZKjE9%2BMKmLM%2F8ZygZeLBGJ7sg%3D%3D--QGIiU0%2FpXc3Aym8F--he2iRRPePOdtEs3z%2BufSXg%3D%3D; path=/; secure; HttpOnly', + 'x-request-id' => 'a0c0b8e7-cd60-4efa-b79b-cf1b0d5a0784', + 'x-runtime' => '0.049566', + 'strict-transport-security' => 'max-age=31536000; includeSubDomains; preload', + 'referrer-policy' => 'same-origin', + 'content-security-policy' => "frame-ancestors 'none'; script-src 'self'; object-src 'self'; img-src * data: blob:; media-src 'self' data:; font-src 'self' data: https://fonts.gstatic.com/; connect-src 'self' blob: wss://mastodonten.de", +]; diff --git a/tests/src/Network/CurlResultTest.php b/tests/src/Network/CurlResultTest.php index e066fb89b..03a528812 100644 --- a/tests/src/Network/CurlResultTest.php +++ b/tests/src/Network/CurlResultTest.php @@ -53,6 +53,7 @@ class CurlResultTest extends TestCase public function testNormal() { $header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head'); + $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php'); $body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body'); @@ -65,7 +66,7 @@ class CurlResultTest extends TestCase $this->assertTrue($curlResult->isSuccess()); $this->assertFalse($curlResult->isTimeout()); $this->assertFalse($curlResult->isRedirectUrl()); - $this->assertSame($header, $curlResult->getHeader()); + $this->assertSame($headerArray, $curlResult->getHeaders()); $this->assertSame($body, $curlResult->getBody()); $this->assertSame('text/html; charset=utf-8', $curlResult->getContentType()); $this->assertSame('https://test.local', $curlResult->getUrl()); @@ -80,6 +81,7 @@ class CurlResultTest extends TestCase public function testRedirect() { $header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head'); + $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php'); $body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body'); @@ -93,7 +95,7 @@ class CurlResultTest extends TestCase $this->assertTrue($curlResult->isSuccess()); $this->assertFalse($curlResult->isTimeout()); $this->assertTrue($curlResult->isRedirectUrl()); - $this->assertSame($header, $curlResult->getHeader()); + $this->assertSame($headerArray, $curlResult->getHeaders()); $this->assertSame($body, $curlResult->getBody()); $this->assertSame('text/html; charset=utf-8', $curlResult->getContentType()); $this->assertSame('https://test.local/test/it', $curlResult->getUrl()); @@ -106,6 +108,7 @@ class CurlResultTest extends TestCase public function testTimeout() { $header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head'); + $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php'); $body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body'); @@ -119,7 +122,7 @@ class CurlResultTest extends TestCase $this->assertFalse($curlResult->isSuccess()); $this->assertTrue($curlResult->isTimeout()); $this->assertFalse($curlResult->isRedirectUrl()); - $this->assertSame($header, $curlResult->getHeader()); + $this->assertSame($headerArray, $curlResult->getHeaders()); $this->assertSame($body, $curlResult->getBody()); $this->assertSame('text/html; charset=utf-8', $curlResult->getContentType()); $this->assertSame('https://test.local/test/it', $curlResult->getRedirectUrl()); @@ -134,6 +137,7 @@ class CurlResultTest extends TestCase public function testRedirectHeader() { $header = file_get_contents(__DIR__ . '/../../datasets/curl/about.redirect'); + $headerArray = include(__DIR__ . '/../../datasets/curl/about.redirect.php'); $body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body'); @@ -146,7 +150,7 @@ class CurlResultTest extends TestCase $this->assertTrue($curlResult->isSuccess()); $this->assertFalse($curlResult->isTimeout()); $this->assertTrue($curlResult->isRedirectUrl()); - $this->assertSame($header, $curlResult->getHeader()); + $this->assertSame($headerArray, $curlResult->getHeaders()); $this->assertSame($body, $curlResult->getBody()); $this->assertSame('text/html; charset=utf-8', $curlResult->getContentType()); $this->assertSame('https://test.local/test/it?key=value', $curlResult->getUrl()); @@ -204,7 +208,7 @@ class CurlResultTest extends TestCase 'url' => 'https://test.local' ]); - $this->assertNotEmpty($curlResult->getHeader()); + $this->assertNotEmpty($curlResult->getHeaders()); $this->assertEmpty($curlResult->getHeader('wrongHeader')); } }