diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 93f45f6b..e2a70004 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -76,40 +76,5 @@ class Application extends App implements IBootstrap { $manager = $context->getServerContainer() ->getNotificationManager(); $manager->registerNotifierService(Notifier::class); - - try { - $context->injectFn(Closure::fromCallable([$this, 'checkUpgradeStatus'])); - } catch (Throwable $e) { - } - } - - - /** - * Register Navigation Tab - * - * @param IServerContainer $container - */ - protected function checkUpgradeStatus(IServerContainer $container) { - $upgradeChecked = $container->getConfig() - ->getAppValue(Application::APP_NAME, 'update_checked', ''); - - if ($upgradeChecked === '0.3') { - return; - } - - try { - $configService = $container->query(ConfigService::class); - $updateService = $container->query(UpdateService::class); - } catch (QueryException $e) { - return; - } - - /** @var ISchemaWrapper $schema */ - $schema = new SchemaWrapper($container->get(IDBConnection::class)); - if ($schema->hasTable('social_a2_stream')) { - $updateService->checkUpdateStatus(); - } - - $configService->setAppValue('update_checked', '0.3'); } } diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php index 7d7a8698..d5bc865c 100644 --- a/lib/Db/CoreRequestBuilder.php +++ b/lib/Db/CoreRequestBuilder.php @@ -1263,7 +1263,6 @@ class CoreRequestBuilder { * this just empty all tables from the app. */ public function emptyAll() { - /** @var ISchemaWrapper|SchemaWrapper $schema */ $schema = new SchemaWrapper(Server::get(IDBConnection::class)); foreach (array_keys(self::$tables) as $table) { if ($schema->hasTable($table)) { @@ -1279,7 +1278,6 @@ class CoreRequestBuilder { * this just empty all tables from the app. */ public function uninstallSocialTables() { - /** @var ISchemaWrapper|SchemaWrapper $schema */ $schema = new SchemaWrapper(Server::get(IDBConnection::class)); foreach (array_keys(self::$tables) as $table) { if ($schema->hasTable($table)) { diff --git a/lib/Db/RequestQueueRequest.php b/lib/Db/RequestQueueRequest.php index 9cb1f908..4da8fbe2 100644 --- a/lib/Db/RequestQueueRequest.php +++ b/lib/Db/RequestQueueRequest.php @@ -107,7 +107,7 @@ class RequestQueueRequest extends RequestQueueRequestBuilder { */ public function getFromToken(string $token, int $status = -1): array { $qb = $this->getRequestQueueSelectSql(); - $this->limitToToken($qb, $token); + $qb->limitToToken($token); if ($status > -1) { $this->limitToStatus($qb, $status); diff --git a/lib/Db/RequestQueueRequestBuilder.php b/lib/Db/RequestQueueRequestBuilder.php index 127949a5..a0ae7b33 100644 --- a/lib/Db/RequestQueueRequestBuilder.php +++ b/lib/Db/RequestQueueRequestBuilder.php @@ -67,9 +67,9 @@ class RequestQueueRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getRequestQueueSelectSql(): IQueryBuilder { + protected function getRequestQueueSelectSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ diff --git a/lib/Service/ActivityService.php b/lib/Service/ActivityService.php index 3365fe2c..561883d4 100644 --- a/lib/Service/ActivityService.php +++ b/lib/Service/ActivityService.php @@ -30,14 +30,6 @@ declare(strict_types=1); namespace OCA\Social\Service; -use OCA\Social\Tools\Exceptions\RequestContentException; -use OCA\Social\Tools\Exceptions\RequestNetworkException; -use OCA\Social\Tools\Exceptions\RequestResultNotJsonException; -use OCA\Social\Tools\Exceptions\RequestResultSizeException; -use OCA\Social\Tools\Exceptions\RequestServerException; -use OCA\Social\Tools\Model\NCRequest; -use OCA\Social\Tools\Model\Request; -use OCA\Social\Tools\Traits\TArrayTools; use Exception; use OCA\Social\AP; use OCA\Social\Db\FollowsRequest; @@ -58,6 +50,14 @@ use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Model\ActivityPub\Object\Tombstone; use OCA\Social\Model\InstancePath; use OCA\Social\Model\RequestQueue; +use OCA\Social\Tools\Exceptions\RequestContentException; +use OCA\Social\Tools\Exceptions\RequestNetworkException; +use OCA\Social\Tools\Exceptions\RequestResultNotJsonException; +use OCA\Social\Tools\Exceptions\RequestResultSizeException; +use OCA\Social\Tools\Exceptions\RequestServerException; +use OCA\Social\Tools\Model\NCRequest; +use OCA\Social\Tools\Model\Request; +use OCA\Social\Tools\Traits\TArrayTools; use Psr\Log\LoggerInterface; /** @@ -190,12 +190,9 @@ class ActivityService { * @throws SocialAppConfigException */ public function request(ACore $activity): string { -// $this->saveActivity($activity); - $author = $this->getAuthorFromItem($activity); $instancePaths = $this->generateInstancePaths($activity); - $token = - $this->requestQueueService->generateRequestQueue($instancePaths, $activity, $author); + $token = $this->requestQueueService->generateRequestQueue($instancePaths, $activity, $author); if ($token === '') { return ''; @@ -212,8 +209,7 @@ class ActivityService { return $token; } - $requests = - $this->requestQueueService->getRequestFromToken($token, RequestQueue::STATUS_STANDBY); + $requests = $this->requestQueueService->getRequestFromToken($token, RequestQueue::STATUS_STANDBY); if (sizeof($requests) > 0) { $this->curlService->asyncWithToken($token); } @@ -245,6 +241,7 @@ class ActivityService { $this->logger->error("Error while trying to init request", [ 'exception' => $e, ]); + return; } diff --git a/lib/Service/CurlService.php b/lib/Service/CurlService.php index cddb6994..69f3582b 100644 --- a/lib/Service/CurlService.php +++ b/lib/Service/CurlService.php @@ -30,20 +30,6 @@ declare(strict_types=1); namespace OCA\Social\Service; -use OCA\Social\Tools\Exceptions\ArrayNotFoundException; -use OCA\Social\Tools\Exceptions\MalformedArrayException; -use OCA\Social\Tools\Exceptions\RequestContentException; -use OCA\Social\Tools\Exceptions\RequestNetworkException; -use OCA\Social\Tools\Exceptions\RequestResultNotJsonException; -use OCA\Social\Tools\Exceptions\RequestResultSizeException; -use OCA\Social\Tools\Exceptions\RequestServerException; -use OCA\Social\Tools\Model\NCRequest; -use OCA\Social\Tools\Model\Request; -use OCA\Social\Tools\Traits\TNCRequest; -use OCA\Social\Tools\Traits\TArrayTools; -use OCA\Social\Tools\Traits\TPathTools; -use OCA\Social\Tools\Traits\TNCSetup; -use OCA\Social\Tools\Traits\TNCLogger; use Exception; use OCA\Social\AP; use OCA\Social\Exceptions\HostMetaException; @@ -55,42 +41,49 @@ use OCA\Social\Exceptions\RetrieveAccountFormatException; use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Exceptions\UnauthorizedFediverseException; use OCA\Social\Model\ActivityPub\Actor\Person; +use OCA\Social\Tools\Exceptions\ArrayNotFoundException; +use OCA\Social\Tools\Exceptions\MalformedArrayException; +use OCA\Social\Tools\Exceptions\RequestContentException; +use OCA\Social\Tools\Exceptions\RequestNetworkException; +use OCA\Social\Tools\Exceptions\RequestResultNotJsonException; +use OCA\Social\Tools\Exceptions\RequestResultSizeException; +use OCA\Social\Tools\Exceptions\RequestServerException; +use OCA\Social\Tools\Model\NCRequest; +use OCA\Social\Tools\Model\Request; +use OCA\Social\Tools\Traits\TArrayTools; +use OCA\Social\Tools\Traits\TPathTools; +use Psr\Log\LoggerInterface; class CurlService { use TArrayTools; - use TNCSetup; - use TNCLogger; use TPathTools; - use TNCRequest { - retrieveJson as retrieveJsonOrig; - doRequest as doRequestOrig; - } public const ASYNC_REQUEST_TOKEN = '/async/request/{token}'; public const USER_AGENT = 'Nextcloud Social'; private ConfigService $configService; private FediverseService $fediverseService; - private MiscService $miscService; + private LoggerInterface $logger; + private int $maxDownloadSize; + private bool $maxDownloadSizeReached = false; /** * CurlService constructor. * * @param ConfigService $configService * @param FediverseService $fediverseService - * @param MiscService $miscService + * @param LoggerInterface $logger */ public function __construct( - ConfigService $configService, FediverseService $fediverseService, MiscService $miscService + ConfigService $configService, + FediverseService $fediverseService, + LoggerInterface $logger ) { $this->configService = $configService; $this->fediverseService = $fediverseService; - $this->miscService = $miscService; - - $maxDlSize = $this->configService->getAppValue(ConfigService::SOCIAL_MAX_SIZE) * (1024 * 1024); - $this->setMaxDownloadSize($maxDlSize); - $this->setup('app', 'social'); + $this->logger = $logger; + $this->maxDownloadSize = $this->configService->getAppValue(ConfigService::SOCIAL_MAX_SIZE) * 1048576; } @@ -108,7 +101,7 @@ class CurlService { * @throws UnauthorizedFediverseException */ public function webfingerAccount(string &$account): array { - $this->debug('webfingerAccount', ['account' => $account]); + $this->logger->debug('webfingerAccount', ['account' => $account]); $account = $this->withoutBeginAt($account); // we consider an account is like an email @@ -136,7 +129,7 @@ class CurlService { $request->setProtocols($protocols); $result = $this->retrieveJson($request); - $this->notice('webfingerAccount, request result', false, ['request' => $request]); + $this->logger->notice('webfingerAccount, request result', ['request' => $request]); $subject = $this->get('subject', $result, ''); list($type, $temp) = explode(':', $subject, 2); @@ -160,12 +153,12 @@ class CurlService { $request->setHost($host); $request->setProtocols($protocols); - $this->debug('hostMeta', ['host' => $host, 'protocols' => $protocols]); + $this->logger->debug('hostMeta', ['host' => $host, 'protocols' => $protocols]); try { $result = $this->retrieveJson($request); } catch (Exception $e) { - $this->exception($e, self::$NOTICE, ['request' => $request]); + $this->logger->notice('during hostMeta', ['request' => $request, 'exception' => $e]); throw new HostMetaException(get_class($e) . ' - ' . $e->getMessage()); } @@ -200,7 +193,7 @@ class CurlService { * @throws UnauthorizedFediverseException */ public function retrieveAccount(string &$account): Person { - $this->debug('retrieveAccount', ['account' => $account]); + $this->logger->debug('retrieveAccount', ['account' => $account]); $result = $this->webfingerAccount($account); try { @@ -212,7 +205,9 @@ class CurlService { $id = $this->get('href', $link, ''); $data = $this->retrieveObject($id); - $this->debug('retrieveAccount, details', ['link' => $link, 'data' => $data, 'account' => $account]); + $this->logger->debug( + 'retrieveAccount, details', ['link' => $link, 'data' => $data, 'account' => $account] + ); /** @var Person $actor */ $actor = AP::$activityPub->getItemFromData($data); @@ -244,17 +239,17 @@ class CurlService { * @throws UnauthorizedFediverseException */ public function retrieveObject($id): array { - $this->debug('retrieveObject', ['id' => $id]); + $this->logger->debug('retrieveObject', ['id' => $id]); $url = parse_url($id); $this->mustContains(['path', 'host', 'scheme'], $url); $request = new NCRequest($url['path'], Request::TYPE_GET); $request->setHost($url['host']); $request->setProtocol($url['scheme']); - $this->debug('retrieveObject', ['request' => $request]); + $this->logger->debug('retrieveObject', ['request' => $request]); $result = $this->retrieveJson($request); - $this->notice('retrieveObject, request result', false, ['request' => $request]); + $this->logger->notice('retrieveObject, request result', ['request' => $request]); if (is_array($result)) { $result['_host'] = $request->getHost(); @@ -275,7 +270,8 @@ class CurlService { try { return $this->retrieveJsonOrig($request); } catch (RequestNetworkException | RequestContentException $e) { - $this->exception($e, self::$NOTICE, ['request' => $request]); + $this->logger->notice('during retrieveJson', ['request' => $request, 'exception' => $e]); + throw $e; } } @@ -291,7 +287,6 @@ class CurlService { * @throws RequestResultSizeException * @throws RequestServerException */ - // migration ? public function doRequest(NCRequest $request): string { $this->fediverseService->authorized($request->getAddress()); $this->configService->configureRequest($request); @@ -331,9 +326,255 @@ class CurlService { $this->retrieveJson($request); } catch (RequestResultNotJsonException $e) { } catch (Exception $e) { - $this->miscService->log( - 'Cannot initiate AsyncWithToken ' . json_encode($token) . ' (' . get_class($e) - . ' - ' . json_encode($e) . ')', 1 + $this->logger->error('Cannot initiate AsyncWithToken', ['token' => $token, 'exception' => $e]); + } + } + + + /** + * @param NCRequest $request + * + * @return array + * @throws RequestContentException + * @throws RequestNetworkException + * @throws RequestResultNotJsonException + * @throws RequestResultSizeException + * @throws RequestServerException + */ + public function retrieveJsonOrig(NCRequest $request): array { + $result = $this->doRequestOrig($request); + + if (strpos($request->getContentType(), 'application/xrd') === 0) { + $xml = simplexml_load_string($result); + $result = json_encode($xml, JSON_UNESCAPED_SLASHES); + } + + $result = json_decode((string)$result, true); + if (is_array($result)) { + return $result; + } + + throw new RequestResultNotJsonException(); + } + + + /** + * @throws RequestContentException + * @throws RequestNetworkException + * @throws RequestResultSizeException + * @throws RequestServerException + */ + public function doRequestOrig(Request $request): string { + $this->maxDownloadSizeReached = false; + + $ignoreProtocolOnErrors = [7]; + $result = ''; + foreach ($request->getProtocols() as $protocol) { + $request->setUsedProtocol($protocol); + $curl = $this->initRequest($request); + + $result = curl_exec($curl); + $this->logger->debug('[>>] ' . json_encode($request) . ' result: ' . json_encode($result)); + + if (in_array(curl_errno($curl), $ignoreProtocolOnErrors)) { + continue; + } + + if ($this->maxDownloadSizeReached === true) { + throw new RequestResultSizeException(); + } + + $this->parseRequestResult($curl, $request); + break; + } + + if ($result === false) { + return ''; + } + + return (string)$result; + } + + + /** + * @param Request $request + * + * @return resource + */ + private function initRequest(Request $request) { + $curl = $this->generateCurlRequest($request); + $this->initRequestHeaders($curl, $request); + + curl_setopt($curl, CURLOPT_USERAGENT, $request->getUserAgent()); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $request->getTimeout()); + curl_setopt($curl, CURLOPT_TIMEOUT, $request->getTimeout()); + + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_BINARYTRANSFER, $request->isBinary()); + + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $request->isVerifyPeer()); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $request->isFollowLocation()); + + curl_setopt($curl, CURLOPT_BUFFERSIZE, 128); + curl_setopt($curl, CURLOPT_NOPROGRESS, false); + curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, + /** + * @param $downloadSize + * @param int $downloaded + * @param $uploadSize + * @param int $uploaded + * + * @return int + */ + function ($downloadSize, int $downloaded, $uploadSize, int $uploaded) { + if ($downloaded > $this->maxDownloadSize) { + $this->maxDownloadSizeReached = true; + + return 1; + } + + return 0; + } + ); + + return $curl; + } + + + /** + * @param Request $request + * + * @return resource + */ + private function generateCurlRequest(Request $request) { + $url = $request->getUsedProtocol() . '://' . $request->getHost() . $request->getParsedUrl(); + if ($request->getType() !== Request::TYPE_GET) { + $curl = curl_init($url); + curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getDataBody()); + + return $curl; + } + + $curl = curl_init($url . $request->getQueryString()); + switch ($request->getType()) { + case Request::TYPE_POST: + curl_setopt($curl, CURLOPT_POST, true); + break; + case Request::TYPE_PUT: + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT'); + break; + case Request::TYPE_DELETE: + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + break; + } + + return $curl; + } + + + /** + * @param Request $request + */ + private function initRequestGet(Request $request) { + if ($request->getType() !== Request::TYPE_GET) { + return; + } + } + + /** + * @param resource $curl + * @param Request $request + */ + private function initRequestHeaders($curl, Request $request) { + $headers = []; + foreach ($request->getHeaders() as $name => $value) { + $headers[] = $name . ': ' . $value; + } + + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + } + + + /** + * @param resource $curl + * @param Request $request + * + * @throws RequestContentException + * @throws RequestServerException + * @throws RequestNetworkException + */ + private function parseRequestResult($curl, Request $request): void { + $this->parseRequestResultCurl($curl, $request); + + $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); + $request->setContentType((!is_string($contentType)) ? '' : $contentType); + $request->setResultCode($code); + + $this->parseRequestResultCode301($code, $request); + $this->parseRequestResultCode4xx($code, $request); + $this->parseRequestResultCode5xx($code, $request); + } + + + /** + * @param resource $curl + * @param Request $request + * + * @throws RequestNetworkException + */ + private function parseRequestResultCurl($curl, Request $request) { + $errno = curl_errno($curl); + if ($errno > 0) { + throw new RequestNetworkException( + $errno . ' - ' . curl_error($curl) . ' - ' . json_encode( + $request, JSON_UNESCAPED_SLASHES + ), $errno + ); + } + } + + + /** + * @param int $code + * @param Request $request + * + * @throws RequestContentException + */ + private function parseRequestResultCode301(int $code, Request $request) { + if ($code === 301) { + throw new RequestContentException( + '301 - ' . json_encode($request, JSON_UNESCAPED_SLASHES) + ); + } + } + + + /** + * @param int $code + * @param Request $request + * + * @throws RequestContentException + */ + private function parseRequestResultCode4xx(int $code, Request $request) { + if ($code === 404 || $code === 410) { + throw new RequestContentException( + $code . ' - ' . json_encode($request, JSON_UNESCAPED_SLASHES) + ); + } + } + + + /** + * @param int $code + * @param Request $request + * + * @throws RequestServerException + */ + private function parseRequestResultCode5xx(int $code, Request $request) { + if ($code === 500) { + throw new RequestServerException( + $code . ' - ' . json_encode($request, JSON_UNESCAPED_SLASHES) ); } } diff --git a/lib/Service/FollowService.php b/lib/Service/FollowService.php index aaa42dc9..8c11f661 100644 --- a/lib/Service/FollowService.php +++ b/lib/Service/FollowService.php @@ -55,21 +55,17 @@ use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Model\ActivityPub\Object\Follow; use OCA\Social\Model\ActivityPub\OrderedCollection; use OCA\Social\Model\InstancePath; +use Psr\Log\LoggerInterface; class FollowService { use TArrayTools; private FollowsRequest $followsRequest; - private ActivityService $activityService; - private CacheActorService $cacheActorService; - private ConfigService $configService; - - private MiscService $miscService; - + private LoggerInterface $logger; private ?Person $viewer = null; @@ -81,17 +77,20 @@ class FollowService { * @param ActivityService $activityService * @param CacheActorService $cacheActorService * @param ConfigService $configService - * @param MiscService $miscService + * @param LoggerInterface $logger */ public function __construct( - FollowsRequest $followsRequest, ActivityService $activityService, - CacheActorService $cacheActorService, ConfigService $configService, MiscService $miscService + FollowsRequest $followsRequest, + ActivityService $activityService, + CacheActorService $cacheActorService, + ConfigService $configService, + LoggerInterface $logger ) { $this->followsRequest = $followsRequest; $this->activityService = $activityService; $this->cacheActorService = $cacheActorService; $this->configService = $configService; - $this->miscService = $miscService; + $this->logger = $logger; } diff --git a/lib/Service/RequestQueueService.php b/lib/Service/RequestQueueService.php index 80e43f39..e1e76272 100644 --- a/lib/Service/RequestQueueService.php +++ b/lib/Service/RequestQueueService.php @@ -75,8 +75,7 @@ class RequestQueueService { * * @return string */ - public function generateRequestQueue(array $instancePaths, ACore $item, string $author - ): string { + public function generateRequestQueue(array $instancePaths, ACore $item, string $author): string { $activity = json_encode($item, JSON_UNESCAPED_SLASHES); $token = ''; @@ -99,6 +98,9 @@ class RequestQueueService { /** + * deciding if we run request on main thread, + * based on set priority, and number of request linked to one token + * * @param string $token * * @return RequestQueue diff --git a/lib/Service/SignatureService.php b/lib/Service/SignatureService.php index 0d8cdcbc..371cdc27 100644 --- a/lib/Service/SignatureService.php +++ b/lib/Service/SignatureService.php @@ -125,7 +125,7 @@ class SignatureService { * @param RequestQueue $queue * * @throws ActorDoesNotExistException - * @throws SocialAppConfigException // TODO: implement in TNCRequest ? + * @throws SocialAppConfigException */ public function signRequest(NCRequest $request, RequestQueue $queue): void { $date = gmdate(self::DATE_HEADER); diff --git a/lib/Tools/Traits/TNCRequest.php b/lib/Tools/Traits/TNCRequest.php deleted file mode 100644 index 0431d9a1..00000000 --- a/lib/Tools/Traits/TNCRequest.php +++ /dev/null @@ -1,342 +0,0 @@ - - * @copyright 2018, Maxence Lange - * @license GNU AGPL 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 . - * - */ - - -namespace OCA\Social\Tools\Traits; - -use OCA\Social\Tools\Exceptions\RequestContentException; -use OCA\Social\Tools\Exceptions\RequestNetworkException; -use OCA\Social\Tools\Exceptions\RequestResultNotJsonException; -use OCA\Social\Tools\Exceptions\RequestResultSizeException; -use OCA\Social\Tools\Exceptions\RequestServerException; -use OCA\Social\Tools\Model\Request; - -/** - * Trait TNCRequest - * - * @package OCA\Social\Tools\Traits - */ -trait TNCRequest { - /** @var int */ - private $maxDownloadSize = 100; - - /** @var bool */ - private $maxDownloadSizeReached = false; - - - /** - * @param int $size - */ - public function setMaxDownloadSize(int $size) { - $this->maxDownloadSize = $size; - } - - - /** - * @param Request $request - * - * @return array - * @throws RequestContentException - * @throws RequestNetworkException - * @throws RequestResultNotJsonException - * @throws RequestResultSizeException - * @throws RequestServerException - */ - public function retrieveJson(Request $request): array { - $result = $this->doRequest($request); - - if (strpos($request->getContentType(), 'application/xrd') === 0) { - $xml = simplexml_load_string($result); - $result = json_encode($xml, JSON_UNESCAPED_SLASHES); - } - - $result = json_decode((string)$result, true); - if (is_array($result)) { - return $result; - } - - throw new RequestResultNotJsonException(); - } - - - /** - * @throws RequestContentException - * @throws RequestNetworkException - * @throws RequestResultSizeException - * @throws RequestServerException - */ - public function doRequest(Request $request): string { - $this->maxDownloadSizeReached = false; - - $ignoreProtocolOnErrors = [7]; - $result = ''; - foreach ($request->getProtocols() as $protocol) { - $request->setUsedProtocol($protocol); - - $curl = $this->initRequest($request); - - $this->initRequestGet($request); - $this->initRequestPost($curl, $request); - $this->initRequestPut($curl, $request); - $this->initRequestDelete($curl, $request); - - $this->initRequestHeaders($curl, $request); - - $result = curl_exec($curl); - - if (in_array(curl_errno($curl), $ignoreProtocolOnErrors)) { - continue; - } - - if ($this->maxDownloadSizeReached === true) { - throw new RequestResultSizeException(); - } - - $this->parseRequestResult($curl, $request); - break; - } - - return $result; - } - - - /** - * @param Request $request - * - * @return resource - */ - private function initRequest(Request $request) { - $curl = $this->generateCurlRequest($request); - - curl_setopt($curl, CURLOPT_USERAGENT, $request->getUserAgent()); - curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $request->getTimeout()); - curl_setopt($curl, CURLOPT_TIMEOUT, $request->getTimeout()); - - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_BINARYTRANSFER, $request->isBinary()); - - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $request->isVerifyPeer()); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $request->isFollowLocation()); - - curl_setopt($curl, CURLOPT_BUFFERSIZE, 128); - curl_setopt($curl, CURLOPT_NOPROGRESS, false); - curl_setopt( - /** - * @param $downloadSize - * @param int $downloaded - * @param $uploadSize - * @param int $uploaded - * - * @return int - */ - $curl, CURLOPT_PROGRESSFUNCTION, - function ($downloadSize, int $downloaded, $uploadSize, int $uploaded) { - if ($downloaded > ($this->maxDownloadSize * (1024 * 1024))) { - $this->maxDownloadSizeReached = true; - - return 1; - } - - return 0; - } - ); - - return $curl; - } - - - /** - * @param Request $request - * - * @return resource - */ - private function generateCurlRequest(Request $request) { - $url = $request->getUsedProtocol() . '://' . $request->getHost() . $request->getParsedUrl(); - if ($request->getType() !== Request::TYPE_GET) { - $curl = curl_init($url); - } else { - $curl = curl_init($url . $request->getQueryString()); - } - - return $curl; - } - - - /** - * @param Request $request - */ - private function initRequestGet(Request $request) { - if ($request->getType() !== Request::TYPE_GET) { - return; - } - } - - - /** - * @param resource $curl - * @param Request $request - */ - private function initRequestPost($curl, Request $request) { - if ($request->getType() !== Request::TYPE_POST) { - return; - } - - curl_setopt($curl, CURLOPT_POST, true); - curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getDataBody()); - } - - - /** - * @param resource $curl - * @param Request $request - */ - private function initRequestPut($curl, Request $request) { - if ($request->getType() !== Request::TYPE_PUT) { - return; - } - - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); - curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getDataBody()); - } - - - /** - * @param resource $curl - * @param Request $request - */ - private function initRequestDelete($curl, Request $request) { - if ($request->getType() !== Request::TYPE_DELETE) { - return; - } - - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getDataBody()); - } - - - /** - * @param resource $curl - * @param Request $request - */ - private function initRequestHeaders($curl, Request $request) { - $headers = $request->getHeaders(); - $headersCurl = []; - foreach ($headers as $name => $value) { - $headersCurl[] = $name . ': ' . $value; - } - - curl_setopt($curl, CURLOPT_HTTPHEADER, $headersCurl); - } - - - /** - * @param resource $curl - * @param Request $request - * - * @throws RequestContentException - * @throws RequestServerException - * @throws RequestNetworkException - */ - private function parseRequestResult($curl, Request $request) { - $this->parseRequestResultCurl($curl, $request); - - $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); - $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE); - $request->setContentType((!is_string($contentType)) ? '' : $contentType); - $request->setResultCode($code); - - $this->parseRequestResultCode301($code, $request); - $this->parseRequestResultCode4xx($code, $request); - $this->parseRequestResultCode5xx($code, $request); - } - - - /** - * @param resource $curl - * @param Request $request - * - * @throws RequestNetworkException - */ - private function parseRequestResultCurl($curl, Request $request) { - $errno = curl_errno($curl); - if ($errno > 0) { - throw new RequestNetworkException( - $errno . ' - ' . curl_error($curl) . ' - ' . json_encode( - $request, JSON_UNESCAPED_SLASHES - ), $errno - ); - } - } - - - /** - * @param int $code - * @param Request $request - * - * @throws RequestContentException - */ - private function parseRequestResultCode301(int $code, Request $request) { - if ($code === 301) { - throw new RequestContentException( - '301 - ' . json_encode($request, JSON_UNESCAPED_SLASHES) - ); - } - } - - - /** - * @param int $code - * @param Request $request - * - * @throws RequestContentException - */ - private function parseRequestResultCode4xx(int $code, Request $request) { - if ($code === 404 || $code === 410) { - throw new RequestContentException( - $code . ' - ' . json_encode($request, JSON_UNESCAPED_SLASHES) - ); - } - } - - - /** - * @param int $code - * @param Request $request - * - * @throws RequestServerException - */ - private function parseRequestResultCode5xx(int $code, Request $request) { - if ($code === 500) { - throw new RequestServerException( - $code . ' - ' . json_encode($request, JSON_UNESCAPED_SLASHES) - ); - } - } -}