--- /dev/null
+<?php
+
+namespace Friendica\Core\Logger\Exception;
+
+use Throwable;
+
+class LogLevelException extends \InvalidArgumentException
+{
+ public function __construct($message = "", Throwable $previous = null)
+ {
+ parent::__construct($message, 500, $previous);
+ }
+}
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Logger\Exception\LoggerException;
use Friendica\Core;
+use Friendica\Core\Logger\Exception\LogLevelException;
use Friendica\Database\Database;
use Friendica\Util\FileSystem;
use Friendica\Util\Introspection;
Core\Logger::class,
Profiler::class,
'Friendica\\Core\\Logger\\Type',
- 'Friendica\\Core\\Logger\\Type\\Monolog',
];
/** @var string The log-channel (app, worker, ...) */
* @param IManageConfigValues $config The config
* @param Profiler $profiler The profiler of the app
* @param FileSystem $fileSystem FileSystem utils
+ * @param string|null $minLevel (optional) Override minimum Loglevel to log
*
* @return LoggerInterface The PSR-3 compliant logger instance
*/
- public function create(Database $database, IManageConfigValues $config, Profiler $profiler, FileSystem $fileSystem): LoggerInterface
+ public function create(Database $database, IManageConfigValues $config, Profiler $profiler, FileSystem $fileSystem, ?string $minLevel = null): LoggerInterface
{
if (empty($config->get('system', 'debugging', false))) {
$logger = new NullLogger();
}
$introspection = new Introspection(self::$ignoreClassList);
- $level = $config->get('system', 'loglevel');
- $loglevel = self::mapLegacyConfigDebugLevel((string)$level);
+ $minLevel = $minLevel ?? $config->get('system', 'loglevel');
+ $loglevel = self::mapLegacyConfigDebugLevel((string)$minLevel);
switch ($config->get('system', 'logger_config', 'stream')) {
case 'monolog':
static::addStreamHandler($logger, $stream, $loglevel);
} catch (\Throwable $e) {
// No Logger ..
- /// @todo isn't it possible to give the admin any hint about this wrong configuration?
- $logger = new NullLogger();
+ try {
+ $logger = new SyslogLogger($this->channel, $introspection, $loglevel);
+ } catch (\Throwable $e) {
+ // No logger ...
+ $logger = new NullLogger();
+ }
}
}
break;
case 'syslog':
try {
$logger = new SyslogLogger($this->channel, $introspection, $loglevel);
+ } catch (LogLevelException $exception) {
+ // If there's a wrong config value for loglevel, try again with standard
+ $logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE);
+ $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
} catch (\Throwable $e) {
// No logger ...
- /// @todo isn't it possible to give the admin any hint about this wrong configuration?
$logger = new NullLogger();
}
break;
if (!is_file($stream) || is_writable($stream)) {
try {
$logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel);
+ } catch (LogLevelException $exception) {
+ // If there's a wrong config value for loglevel, try again with standard
+ $logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE);
+ $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
} catch (\Throwable $t) {
// No logger ...
- /// @todo isn't it possible to give the admin any hint about this wrong configuration?
$logger = new NullLogger();
}
} else {
- /// @todo isn't it possible to give the admin any hint about this wrong configuration?
- $logger = new NullLogger();
+ try {
+ $logger = new SyslogLogger($this->channel, $introspection, $loglevel);
+ } catch (LogLevelException $exception) {
+ // If there's a wrong config value for loglevel, try again with standard
+ $logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE);
+ $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
+ } catch (\Throwable $e) {
+ // No logger ...
+ $logger = new NullLogger();
+ }
}
break;
}
use Friendica\Core\Logger\Exception\LoggerArgumentException;
use Friendica\Core\Logger\Exception\LoggerException;
+use Friendica\Core\Logger\Exception\LogLevelException;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\FileSystem;
use Friendica\Util\Introspection;
* @param string $level The minimum loglevel at which this logger will be triggered
*
* @throws LoggerArgumentException
+ * @throws LogLevelException
*/
public function __construct($channel, $stream, Introspection $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG)
{
if (array_key_exists($level, $this->levelToInt)) {
$this->logLevel = $this->levelToInt[$level];
} else {
- throw new LoggerArgumentException(sprintf('The level "%s" is not valid.', $level));
+ throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
}
$this->checkStream();
* @return void
*
* @throws LoggerException
- * @throws LoggerArgumentException
+ * @throws LogLevelException
*/
protected function addEntry($level, string $message, array $context = [])
{
if (!array_key_exists($level, $this->levelToInt)) {
- throw new LoggerArgumentException(sprintf('The level "%s" is not valid.', $level));
+ throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
}
$logLevel = $this->levelToInt[$level];
namespace Friendica\Core\Logger\Type;
-use Friendica\Core\Logger\Exception\LoggerArgumentException;
use Friendica\Core\Logger\Exception\LoggerException;
+use Friendica\Core\Logger\Exception\LogLevelException;
use Friendica\Util\Introspection;
-use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
/**
* @param int $logOpts Indicates what logging options will be used when generating a log message
* @param int $logFacility Used to specify what type of program is logging the message
*
- * @throws LoggerArgumentException
+ * @throws LogLevelException
+ * @throws LoggerException
*/
public function __construct($channel, Introspection $introspection, string $level = LogLevel::NOTICE, int $logOpts = LOG_PID, int $logFacility = LOG_USER)
{
* @param string $message
* @param array $context
*
- * @throws LoggerArgumentException in case the level isn't valid
+ * @throws LogLevelException in case the level isn't valid
* @throws LoggerException In case the syslog cannot be opened for writing
*/
protected function addEntry($level, string $message, array $context = [])
*
* @return int The SysLog priority
*
- * @throws LoggerArgumentException If the loglevel isn't valid
+ * @throws LogLevelException If the loglevel isn't valid
*/
public function mapLevelToPriority(string $level): int
{
if (!array_key_exists($level, $this->logLevels)) {
- throw new LoggerArgumentException(sprintf('The level "%s" is not valid.', $level));
+ throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
}
return $this->logLevels[$level];
//
/**
- * @return Network\HTTPClient\Capability\ICanRequestPerHttp
+ * @return Network\HTTPClient\Capability\ICanSendHttpRequests
*/
public static function httpClient()
{
- return self::$dice->create(Network\HTTPClient\Capability\ICanRequestPerHttp::class);
+ return self::$dice->create(Network\HTTPClient\Capability\ICanSendHttpRequests::class);
}
//
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2021, the Friendica project
- *
- * @license GNU APGL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Network\HTTPClient\Capability;
-
-use GuzzleHttp\Exception\TransferException;
-
-/**
- * Interface for calling HTTP requests and returning their responses
- */
-interface ICanRequestPerHttp
-{
- /**
- * Fetches the content of an URL
- *
- * Set the cookiejar argument to a string (e.g. "/tmp/friendica-cookies.txt")
- * to preserve cookies from one request to the next.
- *
- * @param string $url URL to fetch
- * @param int $timeout Timeout in seconds, default system config value or 60 seconds
- * @param string $accept_content supply Accept: header with 'accept_content' as the value
- * @param string $cookiejar Path to cookie jar file
- *
- * @return string The fetched content
- */
- public function fetch(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = ''): string;
-
- /**
- * Fetches the whole response of an URL.
- *
- * Inner workings and parameters are the same as @ref fetchUrl but returns an array with
- * all the information collected during the fetch.
- *
- * @param string $url URL to fetch
- * @param int $timeout Timeout in seconds, default system config value or 60 seconds
- * @param string $accept_content supply Accept: header with 'accept_content' as the value
- * @param string $cookiejar Path to cookie jar file
- *
- * @return ICanHandleHttpResponses With all relevant information, 'body' contains the actual fetched content.
- */
- public function fetchFull(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = ''): ICanHandleHttpResponses;
-
- /**
- * Send a HEAD to a URL.
- *
- * @param string $url URL to fetch
- * @param array $opts (optional parameters) associative array with:
- * 'accept_content' => (string array) supply Accept: header with 'accept_content' as the value
- * 'timeout' => int Timeout in seconds, default system config value or 60 seconds
- * 'cookiejar' => path to cookie jar file
- * 'header' => header array
- *
- * @return ICanHandleHttpResponses
- */
- public function head(string $url, array $opts = []): ICanHandleHttpResponses;
-
- /**
- * Send a GET to an URL.
- *
- * @param string $url URL to fetch
- * @param array $opts (optional parameters) associative array with:
- * 'accept_content' => (string array) supply Accept: header with 'accept_content' as the value
- * 'timeout' => int Timeout in seconds, default system config value or 60 seconds
- * 'cookiejar' => path to cookie jar file
- * 'header' => header array
- * 'content_length' => int maximum File content length
- *
- * @return ICanHandleHttpResponses
- */
- public function get(string $url, array $opts = []): ICanHandleHttpResponses;
-
- /**
- * Sends a HTTP request to a given url
- *
- * @param string $method A HTTP request
- * @param string $url Url to send to
- * @param array $opts (optional parameters) associative array with:
- * 'body' => (mixed) setting the body for sending data
- * 'accept_content' => (string array) supply Accept: header with 'accept_content' as the value
- * 'timeout' => int Timeout in seconds, default system config value or 60 seconds
- * 'cookiejar' => path to cookie jar file
- * 'header' => header array
- * 'content_length' => int maximum File content length
- * 'auth' => array authentication settings
- *
- * @return ICanHandleHttpResponses
- */
- public function request(string $method, string $url, array $opts = []): ICanHandleHttpResponses;
-
- /**
- * Send POST request to an URL
- *
- * @param string $url URL to post
- * @param mixed $params array of POST variables
- * @param array $headers HTTP headers
- * @param int $timeout The timeout in seconds, default system config value or 60 seconds
- *
- * @return ICanHandleHttpResponses The content
- */
- public function post(string $url, $params, array $headers = [], int $timeout = 0): ICanHandleHttpResponses;
-
- /**
- * Returns the original URL of the provided URL
- *
- * This function strips tracking query params and follows redirections, either
- * through HTTP code or meta refresh tags. Stops after 10 redirections.
- *
- * @param string $url A user-submitted URL
- *
- * @return string A canonical URL
- *
- * @throws TransferException In case there's an error during the resolving
- */
- public function finalUrl(string $url): string;
-}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU APGL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Network\HTTPClient\Capability;
+
+use GuzzleHttp\Exception\TransferException;
+
+/**
+ * Interface for calling HTTP requests and returning their responses
+ */
+interface ICanSendHttpRequests
+{
+ /**
+ * Fetches the content of an URL
+ *
+ * Set the cookiejar argument to a string (e.g. "/tmp/friendica-cookies.txt")
+ * to preserve cookies from one request to the next.
+ *
+ * @param string $url URL to fetch
+ * @param int $timeout Timeout in seconds, default system config value or 60 seconds
+ * @param string $accept_content supply Accept: header with 'accept_content' as the value
+ * @param string $cookiejar Path to cookie jar file
+ *
+ * @return string The fetched content
+ */
+ public function fetch(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = ''): string;
+
+ /**
+ * Fetches the whole response of an URL.
+ *
+ * Inner workings and parameters are the same as @ref fetchUrl but returns an array with
+ * all the information collected during the fetch.
+ *
+ * @param string $url URL to fetch
+ * @param int $timeout Timeout in seconds, default system config value or 60 seconds
+ * @param string $accept_content supply Accept: header with 'accept_content' as the value
+ * @param string $cookiejar Path to cookie jar file
+ *
+ * @return ICanHandleHttpResponses With all relevant information, 'body' contains the actual fetched content.
+ */
+ public function fetchFull(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = ''): ICanHandleHttpResponses;
+
+ /**
+ * Send a HEAD to a URL.
+ *
+ * @param string $url URL to fetch
+ * @param array $opts (optional parameters) associative array with:
+ * 'accept_content' => (string array) supply Accept: header with 'accept_content' as the value
+ * 'timeout' => int Timeout in seconds, default system config value or 60 seconds
+ * 'cookiejar' => path to cookie jar file
+ * 'header' => header array
+ *
+ * @return ICanHandleHttpResponses
+ */
+ public function head(string $url, array $opts = []): ICanHandleHttpResponses;
+
+ /**
+ * Send a GET to an URL.
+ *
+ * @param string $url URL to fetch
+ * @param array $opts (optional parameters) associative array with:
+ * 'accept_content' => (string array) supply Accept: header with 'accept_content' as the value
+ * 'timeout' => int Timeout in seconds, default system config value or 60 seconds
+ * 'cookiejar' => path to cookie jar file
+ * 'header' => header array
+ * 'content_length' => int maximum File content length
+ *
+ * @return ICanHandleHttpResponses
+ */
+ public function get(string $url, array $opts = []): ICanHandleHttpResponses;
+
+ /**
+ * Sends a HTTP request to a given url
+ *
+ * @param string $method A HTTP request
+ * @param string $url Url to send to
+ * @param array $opts (optional parameters) associative array with:
+ * 'body' => (mixed) setting the body for sending data
+ * 'accept_content' => (string array) supply Accept: header with 'accept_content' as the value
+ * 'timeout' => int Timeout in seconds, default system config value or 60 seconds
+ * 'cookiejar' => path to cookie jar file
+ * 'header' => header array
+ * 'content_length' => int maximum File content length
+ * 'auth' => array authentication settings
+ *
+ * @return ICanHandleHttpResponses
+ */
+ public function request(string $method, string $url, array $opts = []): ICanHandleHttpResponses;
+
+ /**
+ * Send POST request to an URL
+ *
+ * @param string $url URL to post
+ * @param mixed $params array of POST variables
+ * @param array $headers HTTP headers
+ * @param int $timeout The timeout in seconds, default system config value or 60 seconds
+ *
+ * @return ICanHandleHttpResponses The content
+ */
+ public function post(string $url, $params, array $headers = [], int $timeout = 0): ICanHandleHttpResponses;
+
+ /**
+ * Returns the original URL of the provided URL
+ *
+ * This function strips tracking query params and follows redirections, either
+ * through HTTP code or meta refresh tags. Stops after 10 redirections.
+ *
+ * @param string $url A user-submitted URL
+ *
+ * @return string A canonical URL
+ *
+ * @throws TransferException In case there's an error during the resolving
+ */
+ public function finalUrl(string $url): string;
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU APGL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Network\HTTPClient\Client;
+
+use Friendica\Core\System;
+use Friendica\Network\HTTPClient\Response\CurlResult;
+use Friendica\Network\HTTPClient\Response\GuzzleResponse;
+use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
+use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses;
+use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Util\Network;
+use Friendica\Util\Profiler;
+use GuzzleHttp\Client;
+use GuzzleHttp\Cookie\FileCookieJar;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\TransferException;
+use GuzzleHttp\RequestOptions;
+use mattwright\URLResolver;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Log\InvalidArgumentException;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Performs HTTP requests to a given URL
+ */
+class HttpClient implements ICanSendHttpRequests
+{
+ /** @var LoggerInterface */
+ private $logger;
+ /** @var Profiler */
+ private $profiler;
+ /** @var Client */
+ private $client;
+ /** @var URLResolver */
+ private $resolver;
+
+ public function __construct(LoggerInterface $logger, Profiler $profiler, Client $client, URLResolver $resolver)
+ {
+ $this->logger = $logger;
+ $this->profiler = $profiler;
+ $this->client = $client;
+ $this->resolver = $resolver;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function request(string $method, string $url, array $opts = []): ICanHandleHttpResponses
+ {
+ $this->profiler->startRecording('network');
+ $this->logger->debug('Request start.', ['url' => $url, 'method' => $method]);
+
+ if (Network::isLocalLink($url)) {
+ $this->logger->info('Local link', ['url' => $url, 'callstack' => System::callstack(20)]);
+ }
+
+ if (strlen($url) > 1000) {
+ $this->logger->debug('URL is longer than 1000 characters.', ['url' => $url, 'callstack' => System::callstack(20)]);
+ $this->profiler->stopRecording();
+ return CurlResult::createErrorCurl(substr($url, 0, 200));
+ }
+
+ $parts2 = [];
+ $parts = parse_url($url);
+ $path_parts = explode('/', $parts['path'] ?? '');
+ foreach ($path_parts as $part) {
+ if (strlen($part) <> mb_strlen($part)) {
+ $parts2[] = rawurlencode($part);
+ } else {
+ $parts2[] = $part;
+ }
+ }
+ $parts['path'] = implode('/', $parts2);
+ $url = Network::unparseURL($parts);
+
+ if (Network::isUrlBlocked($url)) {
+ $this->logger->info('Domain is blocked.', ['url' => $url]);
+ $this->profiler->stopRecording();
+ return CurlResult::createErrorCurl($url);
+ }
+
+ $conf = [];
+
+ if (!empty($opts[HttpClientOptions::COOKIEJAR])) {
+ $jar = new FileCookieJar($opts[HttpClientOptions::COOKIEJAR]);
+ $conf[RequestOptions::COOKIES] = $jar;
+ }
+
+ $headers = [];
+
+ if (!empty($opts[HttpClientOptions::ACCEPT_CONTENT])) {
+ $headers['Accept'] = $opts[HttpClientOptions::ACCEPT_CONTENT];
+ }
+
+ if (!empty($opts[HttpClientOptions::LEGACY_HEADER])) {
+ $this->logger->notice('Wrong option \'headers\' used.');
+ $headers = array_merge($opts[HttpClientOptions::LEGACY_HEADER], $headers);
+ }
+
+ if (!empty($opts[HttpClientOptions::HEADERS])) {
+ $headers = array_merge($opts[HttpClientOptions::HEADERS], $headers);
+ }
+
+ $conf[RequestOptions::HEADERS] = array_merge($this->client->getConfig(RequestOptions::HEADERS), $headers);
+
+ if (!empty($opts[HttpClientOptions::TIMEOUT])) {
+ $conf[RequestOptions::TIMEOUT] = $opts[HttpClientOptions::TIMEOUT];
+ }
+
+ if (!empty($opts[HttpClientOptions::BODY])) {
+ $conf[RequestOptions::BODY] = $opts[HttpClientOptions::BODY];
+ }
+
+ if (!empty($opts[HttpClientOptions::AUTH])) {
+ $conf[RequestOptions::AUTH] = $opts[HttpClientOptions::AUTH];
+ }
+
+ $conf[RequestOptions::ON_HEADERS] = function (ResponseInterface $response) use ($opts) {
+ if (!empty($opts[HttpClientOptions::CONTENT_LENGTH]) &&
+ (int)$response->getHeaderLine('Content-Length') > $opts[HttpClientOptions::CONTENT_LENGTH]) {
+ throw new TransferException('The file is too big!');
+ }
+ };
+
+ try {
+ $this->logger->debug('http request config.', ['url' => $url, 'method' => $method, 'options' => $conf]);
+
+ $response = $this->client->request($method, $url, $conf);
+ return new GuzzleResponse($response, $url);
+ } catch (TransferException $exception) {
+ if ($exception instanceof RequestException &&
+ $exception->hasResponse()) {
+ return new GuzzleResponse($exception->getResponse(), $url, $exception->getCode(), '');
+ } else {
+ return new CurlResult($url, '', ['http_code' => 500], $exception->getCode(), '');
+ }
+ } catch (InvalidArgumentException | \InvalidArgumentException $argumentException) {
+ $this->logger->info('Invalid Argument for HTTP call.', ['url' => $url, 'method' => $method, 'exception' => $argumentException]);
+ return new CurlResult($url, '', ['http_code' => 500], $argumentException->getCode(), $argumentException->getMessage());
+ } finally {
+ $this->logger->debug('Request stop.', ['url' => $url, 'method' => $method]);
+ $this->profiler->stopRecording();
+ }
+ }
+
+ /** {@inheritDoc}
+ */
+ public function head(string $url, array $opts = []): ICanHandleHttpResponses
+ {
+ return $this->request('head', $url, $opts);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get(string $url, array $opts = []): ICanHandleHttpResponses
+ {
+ return $this->request('get', $url, $opts);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function post(string $url, $params, array $headers = [], int $timeout = 0): ICanHandleHttpResponses
+ {
+ $opts = [];
+
+ $opts[HttpClientOptions::BODY] = $params;
+
+ if (!empty($headers)) {
+ $opts[HttpClientOptions::HEADERS] = $headers;
+ }
+
+ if (!empty($timeout)) {
+ $opts[HttpClientOptions::TIMEOUT] = $timeout;
+ }
+
+ return $this->request('post', $url, $opts);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function finalUrl(string $url): string
+ {
+ $this->profiler->startRecording('network');
+
+ if (Network::isLocalLink($url)) {
+ $this->logger->debug('Local link', ['url' => $url, 'callstack' => System::callstack(20)]);
+ }
+
+ if (Network::isUrlBlocked($url)) {
+ $this->logger->info('Domain is blocked.', ['url' => $url]);
+ return $url;
+ }
+
+ if (Network::isRedirectBlocked($url)) {
+ $this->logger->info('Domain should not be redirected.', ['url' => $url]);
+ return $url;
+ }
+
+ $url = Network::stripTrackingQueryParams($url);
+
+ $url = trim($url, "'");
+
+ $urlResult = $this->resolver->resolveURL($url);
+
+ if ($urlResult->didErrorOccur()) {
+ throw new TransferException($urlResult->getErrorMessageString(), $urlResult->getHTTPStatusCode());
+ }
+
+ return $urlResult->getURL();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetch(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = ''): string
+ {
+ $ret = $this->fetchFull($url, $timeout, $accept_content, $cookiejar);
+
+ return $ret->getBody();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function fetchFull(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = ''): ICanHandleHttpResponses
+ {
+ return $this->get(
+ $url,
+ [
+ 'timeout' => $timeout,
+ 'accept_content' => $accept_content,
+ 'cookiejar' => $cookiejar
+ ]
+ );
+ }
+}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2021, the Friendica project
- *
- * @license GNU APGL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Network\HTTPClient\Client;
-
-use Friendica\Core\System;
-use Friendica\Network\HTTPClient\Response\CurlResult;
-use Friendica\Network\HTTPClient\Response\GuzzleResponse;
-use Friendica\Network\HTTPClient\Capability\ICanRequestPerHttp;
-use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses;
-use Friendica\Network\HTTPException\InternalServerErrorException;
-use Friendica\Util\Network;
-use Friendica\Util\Profiler;
-use GuzzleHttp\Client;
-use GuzzleHttp\Cookie\FileCookieJar;
-use GuzzleHttp\Exception\RequestException;
-use GuzzleHttp\Exception\TransferException;
-use GuzzleHttp\RequestOptions;
-use mattwright\URLResolver;
-use Psr\Http\Message\ResponseInterface;
-use Psr\Log\InvalidArgumentException;
-use Psr\Log\LoggerInterface;
-
-/**
- * Performs HTTP requests to a given URL
- */
-class HttpClientCan implements ICanRequestPerHttp
-{
- /** @var LoggerInterface */
- private $logger;
- /** @var Profiler */
- private $profiler;
- /** @var Client */
- private $client;
- /** @var URLResolver */
- private $resolver;
-
- public function __construct(LoggerInterface $logger, Profiler $profiler, Client $client, URLResolver $resolver)
- {
- $this->logger = $logger;
- $this->profiler = $profiler;
- $this->client = $client;
- $this->resolver = $resolver;
- }
-
- /**
- * {@inheritDoc}
- */
- public function request(string $method, string $url, array $opts = []): ICanHandleHttpResponses
- {
- $this->profiler->startRecording('network');
- $this->logger->debug('Request start.', ['url' => $url, 'method' => $method]);
-
- if (Network::isLocalLink($url)) {
- $this->logger->info('Local link', ['url' => $url, 'callstack' => System::callstack(20)]);
- }
-
- if (strlen($url) > 1000) {
- $this->logger->debug('URL is longer than 1000 characters.', ['url' => $url, 'callstack' => System::callstack(20)]);
- $this->profiler->stopRecording();
- return CurlResult::createErrorCurl(substr($url, 0, 200));
- }
-
- $parts2 = [];
- $parts = parse_url($url);
- $path_parts = explode('/', $parts['path'] ?? '');
- foreach ($path_parts as $part) {
- if (strlen($part) <> mb_strlen($part)) {
- $parts2[] = rawurlencode($part);
- } else {
- $parts2[] = $part;
- }
- }
- $parts['path'] = implode('/', $parts2);
- $url = Network::unparseURL($parts);
-
- if (Network::isUrlBlocked($url)) {
- $this->logger->info('Domain is blocked.', ['url' => $url]);
- $this->profiler->stopRecording();
- return CurlResult::createErrorCurl($url);
- }
-
- $conf = [];
-
- if (!empty($opts[HttpClientOptions::COOKIEJAR])) {
- $jar = new FileCookieJar($opts[HttpClientOptions::COOKIEJAR]);
- $conf[RequestOptions::COOKIES] = $jar;
- }
-
- $headers = [];
-
- if (!empty($opts[HttpClientOptions::ACCEPT_CONTENT])) {
- $headers['Accept'] = $opts[HttpClientOptions::ACCEPT_CONTENT];
- }
-
- if (!empty($opts[HttpClientOptions::LEGACY_HEADER])) {
- $this->logger->notice('Wrong option \'headers\' used.');
- $headers = array_merge($opts[HttpClientOptions::LEGACY_HEADER], $headers);
- }
-
- if (!empty($opts[HttpClientOptions::HEADERS])) {
- $headers = array_merge($opts[HttpClientOptions::HEADERS], $headers);
- }
-
- $conf[RequestOptions::HEADERS] = array_merge($this->client->getConfig(RequestOptions::HEADERS), $headers);
-
- if (!empty($opts[HttpClientOptions::TIMEOUT])) {
- $conf[RequestOptions::TIMEOUT] = $opts[HttpClientOptions::TIMEOUT];
- }
-
- if (!empty($opts[HttpClientOptions::BODY])) {
- $conf[RequestOptions::BODY] = $opts[HttpClientOptions::BODY];
- }
-
- if (!empty($opts[HttpClientOptions::AUTH])) {
- $conf[RequestOptions::AUTH] = $opts[HttpClientOptions::AUTH];
- }
-
- $conf[RequestOptions::ON_HEADERS] = function (ResponseInterface $response) use ($opts) {
- if (!empty($opts[HttpClientOptions::CONTENT_LENGTH]) &&
- (int)$response->getHeaderLine('Content-Length') > $opts[HttpClientOptions::CONTENT_LENGTH]) {
- throw new TransferException('The file is too big!');
- }
- };
-
- try {
- $this->logger->debug('http request config.', ['url' => $url, 'method' => $method, 'options' => $conf]);
-
- $response = $this->client->request($method, $url, $conf);
- return new GuzzleResponse($response, $url);
- } catch (TransferException $exception) {
- if ($exception instanceof RequestException &&
- $exception->hasResponse()) {
- return new GuzzleResponse($exception->getResponse(), $url, $exception->getCode(), '');
- } else {
- return new CurlResult($url, '', ['http_code' => 500], $exception->getCode(), '');
- }
- } catch (InvalidArgumentException | \InvalidArgumentException $argumentException) {
- $this->logger->info('Invalid Argument for HTTP call.', ['url' => $url, 'method' => $method, 'exception' => $argumentException]);
- return new CurlResult($url, '', ['http_code' => 500], $argumentException->getCode(), $argumentException->getMessage());
- } finally {
- $this->logger->debug('Request stop.', ['url' => $url, 'method' => $method]);
- $this->profiler->stopRecording();
- }
- }
-
- /** {@inheritDoc}
- */
- public function head(string $url, array $opts = []): ICanHandleHttpResponses
- {
- return $this->request('head', $url, $opts);
- }
-
- /**
- * {@inheritDoc}
- */
- public function get(string $url, array $opts = []): ICanHandleHttpResponses
- {
- return $this->request('get', $url, $opts);
- }
-
- /**
- * {@inheritDoc}
- */
- public function post(string $url, $params, array $headers = [], int $timeout = 0): ICanHandleHttpResponses
- {
- $opts = [];
-
- $opts[HttpClientOptions::BODY] = $params;
-
- if (!empty($headers)) {
- $opts[HttpClientOptions::HEADERS] = $headers;
- }
-
- if (!empty($timeout)) {
- $opts[HttpClientOptions::TIMEOUT] = $timeout;
- }
-
- return $this->request('post', $url, $opts);
- }
-
- /**
- * {@inheritDoc}
- */
- public function finalUrl(string $url): string
- {
- $this->profiler->startRecording('network');
-
- if (Network::isLocalLink($url)) {
- $this->logger->debug('Local link', ['url' => $url, 'callstack' => System::callstack(20)]);
- }
-
- if (Network::isUrlBlocked($url)) {
- $this->logger->info('Domain is blocked.', ['url' => $url]);
- return $url;
- }
-
- if (Network::isRedirectBlocked($url)) {
- $this->logger->info('Domain should not be redirected.', ['url' => $url]);
- return $url;
- }
-
- $url = Network::stripTrackingQueryParams($url);
-
- $url = trim($url, "'");
-
- $urlResult = $this->resolver->resolveURL($url);
-
- if ($urlResult->didErrorOccur()) {
- throw new TransferException($urlResult->getErrorMessageString(), $urlResult->getHTTPStatusCode());
- }
-
- return $urlResult->getURL();
- }
-
- /**
- * {@inheritDoc}
- */
- public function fetch(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = ''): string
- {
- $ret = $this->fetchFull($url, $timeout, $accept_content, $cookiejar);
-
- return $ret->getBody();
- }
-
- /**
- * {@inheritDoc}
- */
- public function fetchFull(string $url, int $timeout = 0, string $accept_content = '', string $cookiejar = ''): ICanHandleHttpResponses
- {
- return $this->get(
- $url,
- [
- 'timeout' => $timeout,
- 'accept_content' => $accept_content,
- 'cookiejar' => $cookiejar
- ]
- );
- }
-}
use Friendica\BaseFactory;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Network\HTTPClient\Client;
-use Friendica\Network\HTTPClient\Capability\ICanRequestPerHttp;
+use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
use Friendica\Util\Profiler;
use Friendica\Util\Strings;
use GuzzleHttp;
*
* @param HandlerStack|null $handlerStack (optional) A handler replacement (just usefull at test environments)
*
- * @return ICanRequestPerHttp
+ * @return ICanSendHttpRequests
*/
- public function createClient(HandlerStack $handlerStack = null): ICanRequestPerHttp
+ public function createClient(HandlerStack $handlerStack = null): ICanSendHttpRequests
{
$proxy = $this->config->get('system', 'proxy');
// Some websites test the browser for cookie support, so this enhances results.
$resolver->setCookieJar(get_temppath() .'/resolver-cookie-' . Strings::getRandomName(10));
- return new Client\HttpClientCan($logger, $this->profiler, $guzzle, $resolver);
+ return new Client\HttpClient($logger, $this->profiler, $guzzle, $resolver);
}
}
'index',
],
'call' => [
- ['create', ['index'], Dice::CHAIN_CALL],
+ ['create', [], Dice::CHAIN_CALL],
],
],
'$devLogger' => [
['getBackend', [], Dice::CHAIN_CALL],
],
],
- Network\HTTPClient\Capability\ICanRequestPerHttp::class => [
+ Network\HTTPClient\Capability\ICanSendHttpRequests::class => [
'instanceOf' => Network\HTTPClient\Factory\HttpClient::class,
'call' => [
['createClient', [], Dice::CHAIN_CALL],
use Dice\Dice;
use Friendica\DI;
use Friendica\Network\HTTPClient\Factory\HttpClient;
-use Friendica\Network\HTTPClient\Capability\ICanRequestPerHttp;
+use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
use GuzzleHttp\HandlerStack;
/**
$dice = DI::getDice();
// addRule() clones the current instance and returns a new one, so no concurrency problems :-)
- $newDice = $dice->addRule(ICanRequestPerHttp::class, [
+ $newDice = $dice->addRule(ICanSendHttpRequests::class, [
'instanceOf' => HttpClient::class,
'call' => [
['createClient', [$this->httpRequestHandler], Dice::CHAIN_CALL],
use Friendica\Core\Config\ValueObject\Cache;
use Friendica\DI;
use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses;
-use Friendica\Network\HTTPClient\Capability\ICanRequestPerHttp;
+use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
use Friendica\Test\MockedTest;
use Friendica\Test\Util\VFSTrait;
use Mockery;
->andReturn('test Error');
// Mocking the CURL Request
- $networkMock = Mockery::mock(ICanRequestPerHttp::class);
+ $networkMock = Mockery::mock(ICanSendHttpRequests::class);
$networkMock
->shouldReceive('fetchFull')
->with('https://test/install/testrewrite')
->andReturn($IHTTPResult);
$this->dice->shouldReceive('create')
- ->with(ICanRequestPerHttp::class)
+ ->with(ICanSendHttpRequests::class)
->andReturn($networkMock);
DI::init($this->dice);
->andReturn('204');
// Mocking the CURL Request
- $networkMock = Mockery::mock(ICanRequestPerHttp::class);
+ $networkMock = Mockery::mock(ICanSendHttpRequests::class);
$networkMock
->shouldReceive('fetchFull')
->with('https://test/install/testrewrite')
->andReturn($IHTTPResultW);
$this->dice->shouldReceive('create')
- ->with(ICanRequestPerHttp::class)
+ ->with(ICanSendHttpRequests::class)
->andReturn($networkMock);
DI::init($this->dice);
use Friendica\Core\Logger\Exception\LoggerArgumentException;
use Friendica\Core\Logger\Exception\LoggerException;
+use Friendica\Core\Logger\Exception\LogLevelException;
use Friendica\Util\FileSystem;
use Friendica\Test\Util\VFSTrait;
use Friendica\Core\Logger\Type\StreamLogger;
*/
public function testWrongMinimumLevel()
{
- $this->expectException(LoggerArgumentException::class);
+ $this->expectException(LogLevelException::class);
$this->expectExceptionMessageMatches("/The level \".*\" is not valid./");
$logger = new StreamLogger('test', 'file.text', $this->introspection, $this->fileSystem, 'NOPE');
*/
public function testWrongLogLevel()
{
- $this->expectException(LoggerArgumentException::class);
+ $this->expectException(LogLevelException::class);
$this->expectExceptionMessageMatches("/The level \".*\" is not valid./");
$logfile = vfsStream::newFile('friendica.log')
use Friendica\Core\Logger\Exception\LoggerArgumentException;
use Friendica\Core\Logger\Exception\LoggerException;
+use Friendica\Core\Logger\Exception\LogLevelException;
use Friendica\Core\Logger\Type\SyslogLogger;
use Psr\Log\LogLevel;
*/
public function testWrongMinimumLevel()
{
- $this->expectException(LoggerArgumentException::class);
+ $this->expectException(LogLevelException::class);
$this->expectExceptionMessageMatches("/The level \".*\" is not valid./");
$logger = new SyslogLoggerWrapper('test', $this->introspection, 'NOPE');
*/
public function testWrongLogLevel()
{
- $this->expectException(LoggerArgumentException::class);
+ $this->expectException(LogLevelException::class);
$this->expectExceptionMessageMatches("/The level \".*\" is not valid./");
$logger = new SyslogLoggerWrapper('test', $this->introspection);
use Friendica\Core\Config\Factory\Config;
use Friendica\Core\Config\Repository;
use Friendica\Core\Storage\Type;
-use Friendica\Network\HTTPClient\Client\HttpClientCan;
+use Friendica\Network\HTTPClient\Client\HttpClient;
use Friendica\Test\DatabaseTest;
use Friendica\Test\Util\Database\StaticDatabase;
use Friendica\Test\Util\VFSTrait;
private $logger;
/** @var L10n */
private $l10n;
- /** @var HttpClientCan */
+ /** @var HttpClient */
private $httpRequest;
protected function setUp(): void
$this->l10n = \Mockery::mock(L10n::class);
- $this->httpRequest = \Mockery::mock(HttpClientCan::class);
+ $this->httpRequest = \Mockery::mock(HttpClient::class);
}
protected function tearDown(): void