Merge pull request #1537 from nextcloud/curl-and-request

curl and request
pull/1538/head
Maxence Lange 2022-11-23 10:13:26 -01:00 zatwierdzone przez GitHub
commit 8875e12e9a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
10 zmienionych plików z 311 dodań i 451 usunięć

Wyświetl plik

@ -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');
}
}

Wyświetl plik

@ -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)) {

Wyświetl plik

@ -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);

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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 '<request token not needed>';
@ -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;
}

Wyświetl plik

@ -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)
);
}
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -1,342 +0,0 @@
<?php
declare(strict_types=1);
/**
* Some tools for myself.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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)
);
}
}
}