diff options
author | mildis <mildis@users.noreply.github.com> | 2019-06-04 04:51:47 +0200 |
---|---|---|
committer | Frédéric Guillot <fred@kanboard.net> | 2019-06-03 20:00:49 -0700 |
commit | b26776e529a61666dcb941ad00ac85c1d90b03da (patch) | |
tree | e3c86ffb3d897a6dd249cb944e71627200298bf1 /app/Core/Http | |
parent | f76c6c7a2a62deb5c4985067f796b447ae144178 (diff) |
Add cURL support to HTTP Client
- Add HTTP_PROXY_EXCLUDE option when cURL is used
- Show HTTP client backend in about page
- Fallback to legacy Stream Contexts if cURL extension is not available
Diffstat (limited to 'app/Core/Http')
-rw-r--r-- | app/Core/Http/Client.php | 130 |
1 files changed, 127 insertions, 3 deletions
diff --git a/app/Core/Http/Client.php b/app/Core/Http/Client.php index 84099a23..230a5958 100644 --- a/app/Core/Http/Client.php +++ b/app/Core/Http/Client.php @@ -132,7 +132,7 @@ class Client extends Base } /** - * Make the HTTP request + * Make the HTTP request with cURL if detected, socket otherwise * * @access public * @param string $method @@ -144,10 +144,38 @@ class Client extends Base */ public function doRequest($method, $url, $content, array $headers, $raiseForErrors = false) { - if (empty($url)) { - return ''; + $requestBody = ''; + + if (! empty($url)) { + if (function_exists('curl_version')) { + if (DEBUG) { + $this->logger->debug('HttpClient::doRequest: cURL detected'); + } + $requestBody = $this->doRequestWithCurl($method, $url, $content, $headers, $raiseForErrors); + } else { + if (DEBUG) { + $this->logger->debug('HttpClient::doRequest: using socket'); + } + $requestBody = $this->doRequestWithSocket($method, $url, $content, $headers, $raiseForErrors); + } } + return $requestBody; + } + + /** + * Make the HTTP request with socket + * + * @access private + * @param string $method + * @param string $url + * @param string $content + * @param string[] $headers + * @param bool $raiseForErrors + * @return string + */ + private function doRequestWithSocket($method, $url, $content, array $headers, $raiseForErrors = false) + { $startTime = microtime(true); $stream = @fopen(trim($url), 'r', false, stream_context_create($this->getContext($method, $content, $headers, $raiseForErrors))); @@ -184,6 +212,91 @@ class Client extends Base return $body; } + + /** + * Make the HTTP request with cURL + * + * @access private + * @param string $method + * @param string $url + * @param string $content + * @param string[] $headers + * @param bool $raiseForErrors + * @return string + */ + private function doRequestWithCurl($method, $url, $content, array $headers, $raiseForErrors = false) + { + $startTime = microtime(true); + $curlSession = @curl_init(); + + curl_setopt($curlSession, CURLOPT_URL, trim($url)); + curl_setopt($curlSession, CURLOPT_USERAGENT, self::HTTP_USER_AGENT); + curl_setopt($curlSession, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($curlSession, CURLOPT_TIMEOUT, HTTP_TIMEOUT); + curl_setopt($curlSession, CURLOPT_FORBID_REUSE, true); + curl_setopt($curlSession, CURLOPT_MAXREDIRS, HTTP_MAX_REDIRECTS); + curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curlSession, CURLOPT_FOLLOWLOCATION, true); + + if ('POST' === $method) { + curl_setopt($curlSession, CURLOPT_POST, true); + curl_setopt($curlSession, CURLOPT_POSTFIELDS, $content); + } + + if (! empty($headers)) { + curl_setopt($curlSession, CURLOPT_HTTPHEADER, $headers); + } + + if (HTTP_VERIFY_SSL_CERTIFICATE === false) { + curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, false); + } + + if (HTTP_PROXY_HOSTNAME) { + curl_setopt($curlSession, CURLOPT_PROXY, HTTP_PROXY_HOSTNAME); + curl_setopt($curlSession, CURLOPT_PROXYPORT, HTTP_PROXY_PORT); + curl_setopt($curlSession, CURLOPT_NOPROXY, HTTP_PROXY_EXCLUDE); + } + + if (HTTP_PROXY_USERNAME) { + curl_setopt($curlSession, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); + curl_setopt($curlSession, CURLOPT_PROXYUSERPWD, HTTP_PROXY_USERNAME.':'.HTTP_PROXY_PASSWORD); + } + + $body = curl_exec($curlSession); + + if (! $body) { + $this->logger->error('HttpClient: request failed ('.$url.')'); + + if ($raiseForErrors) { + throw new ClientException('Unreachable URL: '.$url); + } + + curl_close($curlSession); + return ''; + } + + if ($raiseForErrors) { + $statusCode = curl_getinfo($curlSession, CURLINFO_RESPONSE_CODE); + + if ($statusCode >= 400) { + throw new InvalidStatusException('Request failed with status code '.$statusCode, $statusCode, $body); + } + } + + if (DEBUG) { + $this->logger->debug('HttpClient: url='.$url); + $this->logger->debug('HttpClient: headers='.var_export($headers, true)); + $this->logger->debug('HttpClient: payload='.$content); + $this->logger->debug('HttpClient: metadata='.var_export(curl_getinfo($curlSession), true)); + $this->logger->debug('HttpClient: body='.$body); + $this->logger->debug('HttpClient: executionTime='.(microtime(true) - $startTime)); + } + + curl_close($curlSession); + return $body; + } + /** * Get stream context * @@ -247,4 +360,15 @@ class Client extends Base return $status; } + + /** + * Get backend used for making HTTP connections + * + * @access public + * @return string + */ + public static function backend() + { + return function_exists('curl_version') ? 'cURL' : 'socket'; + } } |