Improve HTTP client to raise exceptions
This commit is contained in:
parent
94ed32dedf
commit
ebe04e672c
|
|
@ -8,7 +8,7 @@ use Kanboard\Job\HttpAsyncJob;
|
||||||
/**
|
/**
|
||||||
* HTTP client
|
* HTTP client
|
||||||
*
|
*
|
||||||
* @package http
|
* @package Kanboard\Core\Http
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
*/
|
*/
|
||||||
class Client extends Base
|
class Client extends Base
|
||||||
|
|
@ -18,7 +18,7 @@ class Client extends Base
|
||||||
*
|
*
|
||||||
* @var integer
|
* @var integer
|
||||||
*/
|
*/
|
||||||
const HTTP_TIMEOUT = 5;
|
const HTTP_TIMEOUT = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of maximum redirections for the HTTP client
|
* Number of maximum redirections for the HTTP client
|
||||||
|
|
@ -40,11 +40,12 @@ class Client extends Base
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param string[] $headers
|
* @param string[] $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function get($url, array $headers = array())
|
public function get($url, array $headers = [], $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
return $this->doRequest('GET', $url, '', $headers);
|
return $this->doRequest('GET', $url, '', $headers, $raiseForErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -53,12 +54,13 @@ class Client extends Base
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param string[] $headers
|
* @param string[] $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getJson($url, array $headers = array())
|
public function getJson($url, array $headers = [], $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
$response = $this->doRequest('GET', $url, '', array_merge(array('Accept: application/json'), $headers));
|
$response = $this->doRequest('GET', $url, '', array_merge(['Accept: application/json'], $headers), $raiseForErrors);
|
||||||
return json_decode($response, true) ?: array();
|
return json_decode($response, true) ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,15 +70,17 @@ class Client extends Base
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param string[] $headers
|
* @param string[] $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function postJson($url, array $data, array $headers = array())
|
public function postJson($url, array $data, array $headers = [], $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
return $this->doRequest(
|
return $this->doRequest(
|
||||||
'POST',
|
'POST',
|
||||||
$url,
|
$url,
|
||||||
json_encode($data),
|
json_encode($data),
|
||||||
array_merge(array('Content-type: application/json'), $headers)
|
array_merge(['Content-type: application/json'], $headers),
|
||||||
|
$raiseForErrors
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,14 +91,16 @@ class Client extends Base
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param string[] $headers
|
* @param string[] $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
*/
|
*/
|
||||||
public function postJsonAsync($url, array $data, array $headers = array())
|
public function postJsonAsync($url, array $data, array $headers = [], $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
$this->queueManager->push(HttpAsyncJob::getInstance($this->container)->withParams(
|
$this->queueManager->push(HttpAsyncJob::getInstance($this->container)->withParams(
|
||||||
'POST',
|
'POST',
|
||||||
$url,
|
$url,
|
||||||
json_encode($data),
|
json_encode($data),
|
||||||
array_merge(array('Content-type: application/json'), $headers)
|
array_merge(['Content-type: application/json'], $headers),
|
||||||
|
$raiseForErrors
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,15 +111,17 @@ class Client extends Base
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param string[] $headers
|
* @param string[] $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function postForm($url, array $data, array $headers = array())
|
public function postForm($url, array $data, array $headers = [], $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
return $this->doRequest(
|
return $this->doRequest(
|
||||||
'POST',
|
'POST',
|
||||||
$url,
|
$url,
|
||||||
http_build_query($data),
|
http_build_query($data),
|
||||||
array_merge(array('Content-type: application/x-www-form-urlencoded'), $headers)
|
array_merge(['Content-type: application/x-www-form-urlencoded'], $headers),
|
||||||
|
$raiseForErrors
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,14 +132,16 @@ class Client extends Base
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param string[] $headers
|
* @param string[] $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
*/
|
*/
|
||||||
public function postFormAsync($url, array $data, array $headers = array())
|
public function postFormAsync($url, array $data, array $headers = [], $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
$this->queueManager->push(HttpAsyncJob::getInstance($this->container)->withParams(
|
$this->queueManager->push(HttpAsyncJob::getInstance($this->container)->withParams(
|
||||||
'POST',
|
'POST',
|
||||||
$url,
|
$url,
|
||||||
http_build_query($data),
|
http_build_query($data),
|
||||||
array_merge(array('Content-type: application/x-www-form-urlencoded'), $headers)
|
array_merge(['Content-type: application/x-www-form-urlencoded'], $headers),
|
||||||
|
$raiseForErrors
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,29 +153,45 @@ class Client extends Base
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param string $content
|
* @param string $content
|
||||||
* @param string[] $headers
|
* @param string[] $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function doRequest($method, $url, $content, array $headers)
|
public function doRequest($method, $url, $content, array $headers, $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
if (empty($url)) {
|
if (empty($url)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$startTime = microtime(true);
|
$startTime = microtime(true);
|
||||||
$stream = @fopen(trim($url), 'r', false, stream_context_create($this->getContext($method, $content, $headers)));
|
$stream = @fopen(trim($url), 'r', false, stream_context_create($this->getContext($method, $content, $headers, $raiseForErrors)));
|
||||||
$response = '';
|
$response = '';
|
||||||
|
|
||||||
if (is_resource($stream)) {
|
if (! is_resource($stream)) {
|
||||||
$response = stream_get_contents($stream);
|
$this->logger->error('HttpClient: request failed ('.$url.')');
|
||||||
} else {
|
|
||||||
$this->logger->error('HttpClient: request failed');
|
if ($raiseForErrors) {
|
||||||
|
throw new ClientException('Unreachable URL: '.$url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = stream_get_contents($stream);
|
||||||
|
$metadata = stream_get_meta_data($stream);
|
||||||
|
|
||||||
|
if ($raiseForErrors && array_key_exists('wrapper_data', $metadata)) {
|
||||||
|
$statusCode = $this->getStatusCode($metadata['wrapper_data']);
|
||||||
|
|
||||||
|
if ($statusCode >= 400) {
|
||||||
|
throw new InvalidStatusException('Request failed with status code '.$statusCode, $statusCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
$this->logger->debug('HttpClient: url='.$url);
|
$this->logger->debug('HttpClient: url='.$url);
|
||||||
$this->logger->debug('HttpClient: headers='.var_export($headers, true));
|
$this->logger->debug('HttpClient: headers='.var_export($headers, true));
|
||||||
$this->logger->debug('HttpClient: payload='.$content);
|
$this->logger->debug('HttpClient: payload='.$content);
|
||||||
$this->logger->debug('HttpClient: metadata='.var_export(@stream_get_meta_data($stream), true));
|
$this->logger->debug('HttpClient: metadata='.var_export($metadata, true));
|
||||||
$this->logger->debug('HttpClient: response='.$response);
|
$this->logger->debug('HttpClient: response='.$response);
|
||||||
$this->logger->debug('HttpClient: executionTime='.(microtime(true) - $startTime));
|
$this->logger->debug('HttpClient: executionTime='.(microtime(true) - $startTime));
|
||||||
}
|
}
|
||||||
|
|
@ -180,14 +206,15 @@ class Client extends Base
|
||||||
* @param string $method
|
* @param string $method
|
||||||
* @param string $content
|
* @param string $content
|
||||||
* @param string[] $headers
|
* @param string[] $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function getContext($method, $content, array $headers)
|
private function getContext($method, $content, array $headers, $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
$default_headers = array(
|
$default_headers = [
|
||||||
'User-Agent: '.self::HTTP_USER_AGENT,
|
'User-Agent: '.self::HTTP_USER_AGENT,
|
||||||
'Connection: close',
|
'Connection: close',
|
||||||
);
|
];
|
||||||
|
|
||||||
if (HTTP_PROXY_USERNAME) {
|
if (HTTP_PROXY_USERNAME) {
|
||||||
$default_headers[] = 'Proxy-Authorization: Basic '.base64_encode(HTTP_PROXY_USERNAME.':'.HTTP_PROXY_PASSWORD);
|
$default_headers[] = 'Proxy-Authorization: Basic '.base64_encode(HTTP_PROXY_USERNAME.':'.HTTP_PROXY_PASSWORD);
|
||||||
|
|
@ -195,16 +222,17 @@ class Client extends Base
|
||||||
|
|
||||||
$headers = array_merge($default_headers, $headers);
|
$headers = array_merge($default_headers, $headers);
|
||||||
|
|
||||||
$context = array(
|
$context = [
|
||||||
'http' => array(
|
'http' => [
|
||||||
'method' => $method,
|
'method' => $method,
|
||||||
'protocol_version' => 1.1,
|
'protocol_version' => 1.1,
|
||||||
'timeout' => self::HTTP_TIMEOUT,
|
'timeout' => self::HTTP_TIMEOUT,
|
||||||
'max_redirects' => self::HTTP_MAX_REDIRECTS,
|
'max_redirects' => self::HTTP_MAX_REDIRECTS,
|
||||||
'header' => implode("\r\n", $headers),
|
'header' => implode("\r\n", $headers),
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
)
|
'ignore_errors' => $raiseForErrors,
|
||||||
);
|
]
|
||||||
|
];
|
||||||
|
|
||||||
if (HTTP_PROXY_HOSTNAME) {
|
if (HTTP_PROXY_HOSTNAME) {
|
||||||
$context['http']['proxy'] = 'tcp://'.HTTP_PROXY_HOSTNAME.':'.HTTP_PROXY_PORT;
|
$context['http']['proxy'] = 'tcp://'.HTTP_PROXY_HOSTNAME.':'.HTTP_PROXY_PORT;
|
||||||
|
|
@ -212,13 +240,26 @@ class Client extends Base
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HTTP_VERIFY_SSL_CERTIFICATE === false) {
|
if (HTTP_VERIFY_SSL_CERTIFICATE === false) {
|
||||||
$context['ssl'] = array(
|
$context['ssl'] = [
|
||||||
'verify_peer' => false,
|
'verify_peer' => false,
|
||||||
'verify_peer_name' => false,
|
'verify_peer_name' => false,
|
||||||
'allow_self_signed' => true,
|
'allow_self_signed' => true,
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $context;
|
return $context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getStatusCode(array $lines)
|
||||||
|
{
|
||||||
|
$status = 200;
|
||||||
|
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (strpos($line, 'HTTP/1') === 0) {
|
||||||
|
$status = (int) substr($line, 9, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kanboard\Core\Http;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class ClientException extends Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kanboard\Core\Http;
|
||||||
|
|
||||||
|
class InvalidStatusException extends ClientException
|
||||||
|
{
|
||||||
|
protected $statusCode = 0;
|
||||||
|
|
||||||
|
public function __construct($message, $statusCode)
|
||||||
|
{
|
||||||
|
parent::__construct($message);
|
||||||
|
$this->statusCode = $statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatusCode()
|
||||||
|
{
|
||||||
|
return $this->statusCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,11 +18,12 @@ class HttpAsyncJob extends BaseJob
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param string $content
|
* @param string $content
|
||||||
* @param array $headers
|
* @param array $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function withParams($method, $url, $content, array $headers)
|
public function withParams($method, $url, $content, array $headers, $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
$this->jobParams = array($method, $url, $content, $headers);
|
$this->jobParams = array($method, $url, $content, $headers, $raiseForErrors);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,9 +35,10 @@ class HttpAsyncJob extends BaseJob
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param string $content
|
* @param string $content
|
||||||
* @param array $headers
|
* @param array $headers
|
||||||
|
* @param bool $raiseForErrors
|
||||||
*/
|
*/
|
||||||
public function execute($method, $url, $content, array $headers)
|
public function execute($method, $url, $content, array $headers, $raiseForErrors = false)
|
||||||
{
|
{
|
||||||
$this->httpClient->doRequest($method, $url, $content, $headers);
|
$this->httpClient->doRequest($method, $url, $content, $headers, $raiseForErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,8 @@ return array(
|
||||||
'Kanboard\\Core\\Group\\GroupProviderInterface' => $baseDir . '/app/Core/Group/GroupProviderInterface.php',
|
'Kanboard\\Core\\Group\\GroupProviderInterface' => $baseDir . '/app/Core/Group/GroupProviderInterface.php',
|
||||||
'Kanboard\\Core\\Helper' => $baseDir . '/app/Core/Helper.php',
|
'Kanboard\\Core\\Helper' => $baseDir . '/app/Core/Helper.php',
|
||||||
'Kanboard\\Core\\Http\\Client' => $baseDir . '/app/Core/Http/Client.php',
|
'Kanboard\\Core\\Http\\Client' => $baseDir . '/app/Core/Http/Client.php',
|
||||||
|
'Kanboard\\Core\\Http\\ClientException' => $baseDir . '/app/Core/Http/ClientException.php',
|
||||||
|
'Kanboard\\Core\\Http\\InvalidStatusException' => $baseDir . '/app/Core/Http/InvalidStatusException.php',
|
||||||
'Kanboard\\Core\\Http\\OAuth2' => $baseDir . '/app/Core/Http/OAuth2.php',
|
'Kanboard\\Core\\Http\\OAuth2' => $baseDir . '/app/Core/Http/OAuth2.php',
|
||||||
'Kanboard\\Core\\Http\\RememberMeCookie' => $baseDir . '/app/Core/Http/RememberMeCookie.php',
|
'Kanboard\\Core\\Http\\RememberMeCookie' => $baseDir . '/app/Core/Http/RememberMeCookie.php',
|
||||||
'Kanboard\\Core\\Http\\Request' => $baseDir . '/app/Core/Http/Request.php',
|
'Kanboard\\Core\\Http\\Request' => $baseDir . '/app/Core/Http/Request.php',
|
||||||
|
|
|
||||||
|
|
@ -436,6 +436,8 @@ class ComposerStaticInitbdc3716ceecc7570f8ff9a8407f0ca0e
|
||||||
'Kanboard\\Core\\Group\\GroupProviderInterface' => __DIR__ . '/../..' . '/app/Core/Group/GroupProviderInterface.php',
|
'Kanboard\\Core\\Group\\GroupProviderInterface' => __DIR__ . '/../..' . '/app/Core/Group/GroupProviderInterface.php',
|
||||||
'Kanboard\\Core\\Helper' => __DIR__ . '/../..' . '/app/Core/Helper.php',
|
'Kanboard\\Core\\Helper' => __DIR__ . '/../..' . '/app/Core/Helper.php',
|
||||||
'Kanboard\\Core\\Http\\Client' => __DIR__ . '/../..' . '/app/Core/Http/Client.php',
|
'Kanboard\\Core\\Http\\Client' => __DIR__ . '/../..' . '/app/Core/Http/Client.php',
|
||||||
|
'Kanboard\\Core\\Http\\ClientException' => __DIR__ . '/../..' . '/app/Core/Http/ClientException.php',
|
||||||
|
'Kanboard\\Core\\Http\\InvalidStatusException' => __DIR__ . '/../..' . '/app/Core/Http/InvalidStatusException.php',
|
||||||
'Kanboard\\Core\\Http\\OAuth2' => __DIR__ . '/../..' . '/app/Core/Http/OAuth2.php',
|
'Kanboard\\Core\\Http\\OAuth2' => __DIR__ . '/../..' . '/app/Core/Http/OAuth2.php',
|
||||||
'Kanboard\\Core\\Http\\RememberMeCookie' => __DIR__ . '/../..' . '/app/Core/Http/RememberMeCookie.php',
|
'Kanboard\\Core\\Http\\RememberMeCookie' => __DIR__ . '/../..' . '/app/Core/Http/RememberMeCookie.php',
|
||||||
'Kanboard\\Core\\Http\\Request' => __DIR__ . '/../..' . '/app/Core/Http/Request.php',
|
'Kanboard\\Core\\Http\\Request' => __DIR__ . '/../..' . '/app/Core/Http/Request.php',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue