From 65cebe9bd53daa38d09b94826797de0521a8c1ca Mon Sep 17 00:00:00 2001 From: Zankaria Date: Mon, 12 Feb 2024 22:49:56 +0100 Subject: [PATCH] http-driver: add file size limit and timeout support --- inc/driver/http-driver.php | 74 ++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/inc/driver/http-driver.php b/inc/driver/http-driver.php index 659099ed..55e2f58f 100644 --- a/inc/driver/http-driver.php +++ b/inc/driver/http-driver.php @@ -1,32 +1,54 @@ 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, )); } - function __construct($timeout, $user_agent) { + private function set_size_limit(): 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() { @@ -37,19 +59,17 @@ class HttpDriver { * Execute a GET request. * * @param string $endpoint Uri endpoint. - * @param array|null $data Optional GET parameters. + * @param ?array $data Optional GET parameters. * @return string Returns the body of the response. * @throws Exception Throws on error. */ - public function send_get($endpoint, $data) { + public function send_get(string $endpoint, ?array $data): string { if (is_array($data) && !empty($data)) { $endpoint .= '?' . http_build_query($data); } - $this->reset(); - curl_setopt($this->inner, CURLOPT_URL, $endpoint); + $this->set($endpoint); curl_setopt($this->inner, CURLOPT_RETURNTRANSFER, true); - curl_setopt($this->inner, CURLOPT_TIMEOUT, $this->timeout); $ret = curl_exec($this->inner); if ($ret === false) { @@ -62,19 +82,17 @@ class HttpDriver { * Execute a POST request. * * @param string $endpoint Uri endpoint. - * @param array|null $data Optional POST parameters. + * @param ?array $data Optional POST parameters. * @return string Returns the body of the response. * @throws Exception Throws on error. */ - public function send_post($endpoint, $data) { - $this->reset(); - curl_setopt($this->inner, CURLOPT_URL, $endpoint); + public function send_post(string $endpoint, ?array $data): string { + $this->set($endpoint); curl_setopt($this->inner, CURLOPT_POST, true); if (is_array($data) && !empty($data)) { curl_setopt($this->inner, CURLOPT_POSTFIELDS, http_build_query($data)); } curl_setopt($this->inner, CURLOPT_RETURNTRANSFER, true); - curl_setopt($this->inner, CURLOPT_TIMEOUT, $this->timeout); $ret = curl_exec($this->inner); if ($ret === false) { @@ -87,27 +105,31 @@ class HttpDriver { * Download the url's target with curl. * * @param string $url Url to the file to download. - * @param File $fd File descriptor to save the content to. + * @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 void + * @return bool Returns true on success, false if the file was too large. * @throws Exception Throws on error. */ - public function send_get_into($endpoint, $fd, $timeout = 0) { + public function send_get_into(string $endpoint, mixed $fd, int $timeout = 0): bool { if ($timeout == 0) { $timeout = $this->timeout; } - $this->reset(); - curl_setopt($this->inner, CURLOPT_URL, $endpoint); + $this->set($endpoint); 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); - curl_setopt($this->inner, CURLOPT_TIMEOUT, $timeout); + $this->set_size_limit(); $ret = curl_exec($this->inner); if ($ret === false) { + if (curl_errno($this->inner) === CURLE_ABORTED_BY_CALLBACK) { + return false; + } + throw new Exception(curl_error($this->inner)); } + return true; } }