16use Psr\Http\Message\RequestInterface;
17use Psr\Http\Message\ResponseInterface;
18use Psr\Http\Client\ClientInterface;
19use Psr\Http\Client\ClientExceptionInterface;
20use Psr\Http\Client\NetworkExceptionInterface;
21use Http\Promise\Promise as PromiseInterface;
25 use Log\LoggerAwareTrait;
101 if ($options ===
null)
106 $defaultOptions = Configuration::getValue(
'http_client_options');
107 if ($defaultOptions !==
null)
109 $options += $defaultOptions;
112 if (!empty($options))
114 if (isset($options[
'redirect']))
116 $this->
setRedirect($options[
"redirect"], $options[
"redirectMax"] ??
null);
118 if (isset($options[
'waitResponse']))
122 if (isset($options[
'socketTimeout']))
126 if (isset($options[
'streamTimeout']))
130 if (isset($options[
'version']))
134 if (isset($options[
'proxyHost']))
136 $this->
setProxy($options[
'proxyHost'], $options[
'proxyPort'] ??
null, $options[
'proxyUser'] ??
null, $options[
'proxyPassword'] ??
null);
138 if (isset($options[
'compress']))
142 if (isset($options[
'charset']))
146 if (isset($options[
'disableSslVerification']) && $options[
'disableSslVerification'] ===
true)
150 if (isset($options[
'bodyLengthMax']))
154 if (isset($options[
'privateIp']))
158 if (isset($options[
'debugLevel']))
162 if (isset($options[
'cookies']))
166 if (isset($options[
'headers']))
170 if (isset($options[
'useCurl']))
172 $this->useCurl = (bool)$options[
'useCurl'];
174 if (isset($options[
'curlLogFile']))
176 $this->curlLogFile = $options[
'curlLogFile'];
180 if ($this->useCurl && !function_exists(
'curl_init'))
182 $this->useCurl =
false;
192 public function get($url)
194 if ($this->
query(Http\Method::GET, $url))
209 if ($this->
query(Http\Method::HEAD, $url))
224 public function post($url, $postData =
null, $multipart =
false)
229 if ($postData ===
false)
235 if ($this->
query(Http\Method::POST, $url, $postData))
251 if (is_array($postData))
256 $this->
setHeader(
'Content-type',
'multipart/form-data; boundary=' . $data->getBoundary());
262 $this->
addError(
'MULTIPART', $e->getMessage(),
true);
278 public function query($method, $url, $entityBody =
null)
280 $this->effectiveUrl = $url;
281 $this->effectiveIp =
null;
284 if (is_array($entityBody))
289 if ($entityBody instanceof Http\Stream)
293 elseif (is_resource($entityBody))
300 $body->write($entityBody ??
'');
303 $this->redirectCount = 0;
309 $uri =
new Uri($this->effectiveUrl);
312 $request =
new Http\Request($method, $uri, $this->headers->getHeaders(), $body, $this->version);
319 catch (ClientExceptionInterface $e)
322 if ($e instanceof NetworkExceptionInterface)
324 $this->
addError(
'NETWORK', $e->getMessage());
334 if ($this->redirect && ($location = $this->
getHeaders()->
get(
'Location')) !==
null && $location !=
'')
336 if ($this->redirectCount < $this->redirectMax)
339 $this->headers->delete(
'Host');
340 $this->effectiveUrl = $location;
343 if ($status == 302 || $status == 303)
345 $method = Http\Method::GET;
348 $this->redirectCount++;
352 $this->
addError(
'REDIRECT',
"Maximum number of redirects ({$this->redirectMax}) has been reached at URL {$url}",
true);
371 public function setHeader($name, $value, $replace =
true)
373 if ($replace || !$this->headers->has($name))
375 $this->headers->set($name, $value);
388 foreach (
$headers as $name => $value)
404 return $this->request->getHeadersCollection();
414 $this->headers->clear();
425 if (!empty($cookies))
442 $this->
setHeader(
'Authorization',
'Basic ' . base64_encode($user .
':' . $pass));
455 $this->redirect = (bool)$value;
458 $this->redirectMax = intval($max);
488 $this->socketTimeout = intval($value);
500 $this->streamTimeout = intval($value);
512 $this->version = $value;
526 $this->compress = (bool)$value;
538 $this->requestCharset = $value;
549 $this->sslVerify =
false;
561 $this->privateIp = (bool)$value;
598 $this->outputStream = $handler;
623 $result = $this->
query(Http\Method::GET, $url);
625 if ($result && ($status = $this->
getStatus()) >= 200 && $status < 300)
642 $dir = IO\Path::getDirectory($filePath);
643 IO\Directory::createDirectory($dir);
645 $file =
new IO\File($filePath);
646 $handler = $file->open(
'w+');
672 $this->contextOptions = array_replace_recursive($this->contextOptions, $options);
685 return $this->response->getHeadersCollection();
709 return $this->response->getStatusCode();
724 $body = $this->response->getBody();
726 if ($this->outputStream ===
null)
728 $result = (string)$body;
732 $body->copyTo($this->outputStream);
785 if ($this->effectiveIp)
792 protected function addError($code, $message, $triggerWarning =
false)
794 $this->error[$code] = $message;
798 trigger_error($message, E_USER_WARNING);
808 $punyUri =
new Uri(
'http://' . $uri->getHost());
809 if (($punyHost = $punyUri->convertToPunycode()) != $uri->getHost())
811 $uri = $uri->withHost($punyHost);
819 if (!
$request->hasHeader(
'Connection'))
827 if (!
$request->hasHeader(
'Accept-Language'))
835 if (($userInfo = $uri->getUserInfo()) !=
'')
837 $request =
$request->withHeader(
'Authorization',
'Basic ' . base64_encode($userInfo));
839 if ($this->proxyHost !=
'' && $this->proxyUser !=
'')
841 $request =
$request->withHeader(
'Proxy-Authorization',
'Basic ' . base64_encode($this->proxyUser .
':' . $this->proxyPassword));
850 if ($method == Http\Method::POST)
853 if (!
$request->hasHeader(
'Content-Type'))
855 $contentType =
'application/x-www-form-urlencoded';
856 if ($this->requestCharset !=
'')
864 $size = $body->getSize();
866 if ($size > 0 || $method == Http\Method::POST || $method == Http\Method::PUT)
869 if (!
$request->hasHeader(
'Content-Length'))
871 $request =
$request->withHeader(
'Content-Length', $size ?? strlen((
string)$body));
879 foreach ($event->getResults() as $eventResult)
881 $request = $eventResult->getRequest();
891 $scheme = $uri->getScheme();
892 if ($scheme !==
'http' && $scheme !==
'https')
894 $this->
addError(
'URI_SCHEME',
'Only http and https shemes are supported.');
898 if ($uri->getHost() ==
'')
900 $this->
addError(
'URI_HOST',
'Incorrect host in URI.');
904 $punyUri =
new Uri(
'http://' . $uri->getHost());
905 $error = $punyUri->convertToPunycode();
908 $this->
addError(
'URI_PUNICODE',
"Error converting hostname to punycode: {$error->getMessage()}");
912 if (!$this->privateIp)
915 if ($ip->isPrivate())
917 $this->
addError(
'PRIVATE_IP',
"Resolved IP is incorrect or private: {$ip->get()}");
920 $this->effectiveIp = $ip;
946 $this->response = $promise->wait();
965 if ($this->queue ===
null)
974 $this->queue->add($promise);
986 if ($this->sslVerify ===
false)
988 $this->contextOptions[
'ssl'][
'verify_peer_name'] =
false;
989 $this->contextOptions[
'ssl'][
'verify_peer'] =
false;
990 $this->contextOptions[
'ssl'][
'allow_self_signed'] =
true;
1017 if ($this->logger !==
null)
1019 $handler->setLogger($this->logger);
1020 $handler->setDebugLevel($this->debugLevel);
1069 foreach ($this->queue->wait() as $promise)
1071 $responses[$promise->getId()] = $promise->wait();
setProxy($proxyHost, $proxyPort=null, $proxyUser=null, $proxyPassword=null)
post($url, $postData=null, $multipart=false)
shouldFetchBody(callable $callback)
setCookies(array $cookies)
setOutputStream($handler)
__construct(array $options=null)
prepareMultipart($postData)
checkRequest(RequestInterface $request)
const DEFAULT_SOCKET_TIMEOUT
const DEFAULT_STREAM_TIMEOUT_NO_WAIT
setContextOptions(array $options)
sendRequest(RequestInterface $request)
createHandler(RequestInterface $request, bool $async=false)
createQueue(bool $backgroundJob=true)
download($url, $filePath)
setHeader($name, $value, $replace=true)
setAuthorization($user, $pass)
createPromise($handler, Http\Queue $queue)
query($method, $url, $entityBody=null)
setRedirect($value, $max=null)
sendAsyncRequest(RequestInterface $request)
const DEFAULT_STREAM_TIMEOUT
setBodyLengthMax($bodyLengthMax)
setHeaders(array $headers)
addError($code, $message, $triggerWarning=false)
buildRequest(RequestInterface $request)
static createByUri(UriInterface $uri)
setDebugLevel(int $debugLevel)
trait DebugInterfaceTrait