Merge pull request #820 from Zankaria/http-refactor

Move HttpDriver to Data
This commit is contained in:
Lorenzo Yario 2024-10-04 00:00:06 -07:00 committed by GitHub
commit c97c61aeca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 136 additions and 156 deletions

View File

@ -38,7 +38,6 @@
"inc/functions/num.php", "inc/functions/num.php",
"inc/functions/format.php", "inc/functions/format.php",
"inc/functions/theme.php", "inc/functions/theme.php",
"inc/driver/http-driver.php",
"inc/driver/log-driver.php", "inc/driver/log-driver.php",
"inc/service/captcha-queries.php", "inc/service/captcha-queries.php",
"inc/context.php" "inc/context.php"

View File

@ -0,0 +1,131 @@
<?php
namespace Vichan\Data\Driver;
defined('TINYBOARD') or exit;
/**
* Honestly this is just a wrapper for cURL. Still useful to mock it and have an OOP API on PHP 7.
*/
class HttpDriver {
private mixed $inner;
private int $timeout;
private int $max_file_size;
private function resetTowards(string $url, int $timeout): void {
\curl_reset($this->inner);
\curl_setopt_array($this->inner, [
\CURLOPT_URL => $url,
\CURLOPT_TIMEOUT => $timeout,
\CURLOPT_USERAGENT => 'Tinyboard',
\CURLOPT_PROTOCOLS => \CURLPROTO_HTTP | \CURLPROTO_HTTPS,
]);
}
public function __construct(int $timeout, int $max_file_size) {
$this->inner = \curl_init();
$this->timeout = $timeout;
$this->max_file_size = $max_file_size;
}
public function __destruct() {
\curl_close($this->inner);
}
/**
* Execute a GET request.
*
* @param string $endpoint Uri endpoint.
* @param ?array $data Optional GET parameters.
* @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
* @return string Returns the body of the response.
* @throws RuntimeException Throws on IO error.
*/
public function requestGet(string $endpoint, ?array $data, int $timeout = 0): string {
if (!empty($data)) {
$endpoint .= '?' . \http_build_query($data);
}
if ($timeout == 0) {
$timeout = $this->timeout;
}
$this->resetTowards($endpoint, $timeout);
\curl_setopt($this->inner, \CURLOPT_RETURNTRANSFER, true);
$ret = \curl_exec($this->inner);
if ($ret === false) {
throw new \RuntimeException(\curl_error($this->inner));
}
return $ret;
}
/**
* Execute a POST request.
*
* @param string $endpoint Uri endpoint.
* @param ?array $data Optional POST parameters.
* @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
* @return string Returns the body of the response.
* @throws RuntimeException Throws on IO error.
*/
public function requestPost(string $endpoint, ?array $data, int $timeout = 0): string {
if ($timeout == 0) {
$timeout = $this->timeout;
}
$this->resetTowards($endpoint, $timeout);
\curl_setopt($this->inner, \CURLOPT_POST, true);
if (!empty($data)) {
\curl_setopt($this->inner, \CURLOPT_POSTFIELDS, \http_build_query($data));
}
\curl_setopt($this->inner, \CURLOPT_RETURNTRANSFER, true);
$ret = \curl_exec($this->inner);
if ($ret === false) {
throw new \RuntimeException(\curl_error($this->inner));
}
return $ret;
}
/**
* Download the url's target with curl.
*
* @param string $url Url to the file to download.
* @param ?array $data Optional GET parameters.
* @param resource $fd File descriptor to save the content to.
* @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
* @return bool Returns true on success, false if the file was too large.
* @throws RuntimeException Throws on IO error.
*/
public function requestGetInto(string $endpoint, ?array $data, mixed $fd, int $timeout = 0): bool {
if (!empty($data)) {
$endpoint .= '?' . \http_build_query($data);
}
if ($timeout == 0) {
$timeout = $this->timeout;
}
$this->resetTowards($endpoint, $timeout);
// Adapted from: https://stackoverflow.com/a/17642638
$opt = (\PHP_MAJOR_VERSION >= 8 && \PHP_MINOR_VERSION >= 2) ? \CURLOPT_XFERINFOFUNCTION : \CURLOPT_PROGRESSFUNCTION;
\curl_setopt_array($this->inner, [
\CURLOPT_NOPROGRESS => false,
$opt => fn($res, $next_dl, $dl, $next_up, $up) => (int)($dl <= $this->max_file_size),
\CURLOPT_FAILONERROR => true,
\CURLOPT_FOLLOWLOCATION => false,
\CURLOPT_FILE => $fd,
\CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
]);
$ret = \curl_exec($this->inner);
if ($ret === false) {
if (\curl_errno($this->inner) === CURLE_ABORTED_BY_CALLBACK) {
return false;
}
throw new \RuntimeException(\curl_error($this->inner));
}
return true;
}
}

View File

@ -2,7 +2,8 @@
namespace Vichan; namespace Vichan;
use RuntimeException; use RuntimeException;
use Vichan\Driver\{HttpDriver, HttpDrivers, Log, LogDrivers}; use Vichan\Driver\{Log, LogDrivers};
use Vichan\Data\Driver\HttpDriver;
use Vichan\Service\HCaptchaQuery; use Vichan\Service\HCaptchaQuery;
use Vichan\Service\NativeCaptchaQuery; use Vichan\Service\NativeCaptchaQuery;
use Vichan\Service\ReCaptchaQuery; use Vichan\Service\ReCaptchaQuery;
@ -56,7 +57,7 @@ function build_context(array $config): Context {
}, },
HttpDriver::class => function($c) { HttpDriver::class => function($c) {
$config = $c->get('config'); $config = $c->get('config');
return HttpDrivers::getHttpDriver($config['upload_by_url_timeout'], $config['max_filesize']); return new HttpDriver($config['upload_by_url_timeout'], $config['max_filesize']);
}, },
RemoteCaptchaQuery::class => function($c) { RemoteCaptchaQuery::class => function($c) {
$config = $c->get('config'); $config = $c->get('config');

View File

@ -1,151 +0,0 @@
<?php // Honestly this is just a wrapper for cURL. Still useful to mock it and have an OOP API on PHP 7.
namespace Vichan\Driver;
use RuntimeException;
defined('TINYBOARD') or exit;
class HttpDrivers {
private const DEFAULT_USER_AGENT = 'Tinyboard';
public static function getHttpDriver(int $timeout, int $max_file_size): HttpDriver {
return new HttpDriver($timeout, self::DEFAULT_USER_AGENT, $max_file_size);
}
}
class HttpDriver {
private mixed $inner;
private int $timeout;
private string $user_agent;
private int $max_file_size;
private function resetTowards(string $url, int $timeout): void {
curl_reset($this->inner);
curl_setopt_array($this->inner, array(
CURLOPT_URL => $url,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_USERAGENT => $this->user_agent,
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
));
}
private function setSizeLimit(): void {
// Adapted from: https://stackoverflow.com/a/17642638
curl_setopt($this->inner, CURLOPT_NOPROGRESS, false);
if (PHP_MAJOR_VERSION >= 8 && PHP_MINOR_VERSION >= 2) {
curl_setopt($this->inner, CURLOPT_XFERINFOFUNCTION, function($res, $next_dl, $dl, $next_up, $up) {
return (int)($dl <= $this->max_file_size);
});
} else {
curl_setopt($this->inner, CURLOPT_PROGRESSFUNCTION, function($res, $next_dl, $dl, $next_up, $up) {
return (int)($dl <= $this->max_file_size);
});
}
}
function __construct($timeout, $user_agent, $max_file_size) {
$this->inner = curl_init();
$this->timeout = $timeout;
$this->user_agent = $user_agent;
$this->max_file_size = $max_file_size;
}
function __destruct() {
curl_close($this->inner);
}
/**
* Execute a GET request.
*
* @param string $endpoint Uri endpoint.
* @param ?array $data Optional GET parameters.
* @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
* @return string Returns the body of the response.
* @throws RuntimeException Throws on IO error.
*/
public function requestGet(string $endpoint, ?array $data, int $timeout = 0): string {
if (!empty($data)) {
$endpoint .= '?' . http_build_query($data);
}
if ($timeout == 0) {
$timeout = $this->timeout;
}
$this->resetTowards($endpoint, $timeout);
curl_setopt($this->inner, CURLOPT_RETURNTRANSFER, true);
$ret = curl_exec($this->inner);
if ($ret === false) {
throw new \RuntimeException(curl_error($this->inner));
}
return $ret;
}
/**
* Execute a POST request.
*
* @param string $endpoint Uri endpoint.
* @param ?array $data Optional POST parameters.
* @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
* @return string Returns the body of the response.
* @throws RuntimeException Throws on IO error.
*/
public function requestPost(string $endpoint, ?array $data, int $timeout = 0): string {
if ($timeout == 0) {
$timeout = $this->timeout;
}
$this->resetTowards($endpoint, $timeout);
curl_setopt($this->inner, CURLOPT_POST, true);
if (!empty($data)) {
curl_setopt($this->inner, CURLOPT_POSTFIELDS, http_build_query($data));
}
curl_setopt($this->inner, CURLOPT_RETURNTRANSFER, true);
$ret = curl_exec($this->inner);
if ($ret === false) {
throw new \RuntimeException(curl_error($this->inner));
}
return $ret;
}
/**
* Download the url's target with curl.
*
* @param string $url Url to the file to download.
* @param ?array $data Optional GET parameters.
* @param resource $fd File descriptor to save the content to.
* @param int $timeout Optional request timeout in seconds. Use the default timeout if 0.
* @return bool Returns true on success, false if the file was too large.
* @throws RuntimeException Throws on IO error.
*/
public function requestGetInto(string $endpoint, ?array $data, mixed $fd, int $timeout = 0): bool {
if (!empty($data)) {
$endpoint .= '?' . http_build_query($data);
}
if ($timeout == 0) {
$timeout = $this->timeout;
}
$this->resetTowards($endpoint, $timeout);
curl_setopt($this->inner, CURLOPT_FAILONERROR, true);
curl_setopt($this->inner, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($this->inner, CURLOPT_FILE, $fd);
curl_setopt($this->inner, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
$this->setSizeLimit();
$ret = curl_exec($this->inner);
if ($ret === false) {
if (curl_errno($this->inner) === CURLE_ABORTED_BY_CALLBACK) {
return false;
}
throw new \RuntimeException(curl_error($this->inner));
}
return true;
}
}

View File

@ -1,7 +1,7 @@
<?php // Verify captchas server side. <?php // Verify captchas server side.
namespace Vichan\Service; namespace Vichan\Service;
use Vichan\Driver\HttpDriver; use Vichan\Data\Driver\HttpDriver;
defined('TINYBOARD') or exit; defined('TINYBOARD') or exit;

View File

@ -6,7 +6,7 @@
require_once 'inc/bootstrap.php'; require_once 'inc/bootstrap.php';
use Vichan\{Context, WebDependencyFactory}; use Vichan\{Context, WebDependencyFactory};
use Vichan\Driver\{HttpDriver, Log}; use Vichan\Data\Driver\{HttpDriver, Log};
use Vichan\Service\{RemoteCaptchaQuery, NativeCaptchaQuery}; use Vichan\Service\{RemoteCaptchaQuery, NativeCaptchaQuery};
use Vichan\Functions\Format; use Vichan\Functions\Format;