uawdijnntqw1x1x1
IP : 18.188.100.179
Hostname : axolotl
Kernel : Linux axolotl 4.9.0-13-amd64 #1 SMP Debian 4.9.228-1 (2020-07-05) x86_64
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,
OS : Linux
PATH:
/
var
/
www
/
axolotl
/
data
/
www
/
msk.axolotls.ru
/
bitrix
/
modules
/
main
/
lib
/
web
/
httpclient.php
/
/
<?php /** * Bitrix Framework * @package bitrix * @subpackage main * @copyright 2001-2014 Bitrix */ namespace Bitrix\Main\Web; use Bitrix\Main\Text\BinaryString; use Bitrix\Main\IO; use Bitrix\Main\Config\Configuration; class HttpClient { const HTTP_1_0 = "1.0"; const HTTP_1_1 = "1.1"; const HTTP_GET = "GET"; const HTTP_POST = "POST"; const HTTP_PUT = "PUT"; const HTTP_HEAD = "HEAD"; const HTTP_PATCH = "PATCH"; const HTTP_DELETE = "DELETE"; const DEFAULT_SOCKET_TIMEOUT = 30; const DEFAULT_STREAM_TIMEOUT = 60; const DEFAULT_STREAM_TIMEOUT_NO_WAIT = 1; const BUF_READ_LEN = 16384; const BUF_POST_LEN = 131072; protected $proxyHost; protected $proxyPort; protected $proxyUser; protected $proxyPassword; protected $resource; protected $socketTimeout = self::DEFAULT_SOCKET_TIMEOUT; protected $streamTimeout = self::DEFAULT_STREAM_TIMEOUT; protected $error = array(); protected $peerSocketName; /** @var HttpHeaders */ protected $requestHeaders; /** @var HttpCookies */ protected $requestCookies; protected $waitResponse = true; protected $redirect = true; protected $redirectMax = 5; protected $redirectCount = 0; protected $compress = false; protected $version = self::HTTP_1_0; protected $requestCharset = ''; protected $sslVerify = true; protected $bodyLengthMax = 0; protected $privateIp = true; protected $status = 0; /** @var HttpHeaders */ protected $responseHeaders; /** @var HttpCookies */ protected $responseCookies; protected $result = ''; protected $outputStream; /** @var IpAddress */ protected $effectiveIp; protected $effectiveUrl; protected $receivedBytesLength = 0; protected $contextOptions = []; /** * @param array $options Optional array with options: * "redirect" bool Follow redirects (default true) * "redirectMax" int Maximum number of redirects (default 5) * "waitResponse" bool Read the body or disconnect just after reading headers (default true) * "socketTimeout" int Connection timeout in seconds (default 30) * "streamTimeout" int Stream reading timeout in seconds (default 60 for waitResponse == true and 1 for waitResponse == false) * "version" string HTTP version (HttpClient::HTTP_1_0, HttpClient::HTTP_1_1) (default "1.0") * "proxyHost" string Proxy host name/address * "proxyPort" int Proxy port number * "proxyUser" string Proxy username * "proxyPassword" string Proxy password * "compress" bool Accept gzip encoding (default false) * "charset" string Charset for body in POST and PUT * "disableSslVerification" bool Pass true to disable ssl check * "bodyLengthMax" int Maximum length of the body. * "privateIp" bool Enable or disable requests to private IPs (default true). * All the options can be set separately with setters. */ public function __construct(array $options = null) { $this->requestHeaders = new HttpHeaders(); $this->responseHeaders = new HttpHeaders(); $this->requestCookies = new HttpCookies(); $this->responseCookies = new HttpCookies(); if($options === null) { $options = array(); } $defaultOptions = Configuration::getValue("http_client_options"); if($defaultOptions !== null) { $options += $defaultOptions; } if(!empty($options)) { if(isset($options["redirect"])) { $this->setRedirect($options["redirect"], $options["redirectMax"]); } if(isset($options["waitResponse"])) { $this->waitResponse($options["waitResponse"]); } if(isset($options["socketTimeout"])) { $this->setTimeout($options["socketTimeout"]); } if(isset($options["streamTimeout"])) { $this->setStreamTimeout($options["streamTimeout"]); } if(isset($options["version"])) { $this->setVersion($options["version"]); } if(isset($options["proxyHost"])) { $this->setProxy($options["proxyHost"], $options["proxyPort"], $options["proxyUser"], $options["proxyPassword"]); } if(isset($options["compress"])) { $this->setCompress($options["compress"]); } if(isset($options["charset"])) { $this->setCharset($options["charset"]); } if(isset($options["disableSslVerification"]) && $options["disableSslVerification"] === true) { $this->disableSslVerification(); } if(isset($options["bodyLengthMax"])) { $this->setBodyLengthMax($options["bodyLengthMax"]); } if(isset($options["privateIp"])) { $this->setPrivateIp($options["privateIp"]); } } } /** * Closes the connection on the object destruction. */ public function __destruct() { $this->disconnect(); } /** * Performs GET request. * * @param string $url Absolute URI eg. "http://user:pass @ host:port/path/?query". * @return string|bool Response entity string or false on error. Note, it's empty string if outputStream is set. */ public function get($url) { if($this->query(self::HTTP_GET, $url)) { return $this->getResult(); } return false; } /** * Performs HEAD request. * * @param string $url Absolute URI eg. "http://user:pass @ host:port/path/?query" * @return HttpHeaders|bool Response headers or false on error. */ public function head($url) { if($this->query(self::HTTP_HEAD, $url)) { return $this->getHeaders(); } return false; } /** * Performs POST request. * * @param string $url Absolute URI eg. "http://user:pass @ host:port/path/?query". * @param array|string|resource $postData Entity of POST/PUT request. If it's resource handler then data will be read directly from the stream. * @param boolean $multipart Whether or not to use multipart/form-data encoding. If true, method accepts file as a resource or as an array with keys 'resource' (or 'content') and optionally 'filename' and 'contentType' * @return string|bool Response entity string or false on error. Note, it's empty string if outputStream is set. */ public function post($url, $postData = null, $multipart = false) { if ($multipart) { $postData = $this->prepareMultipart($postData); if($postData === false) { return false; } } if($this->query(self::HTTP_POST, $url, $postData)) { return $this->getResult(); } return false; } /** * Performs multipart/form-data encoding. * Accepts file as a resource or as an array with keys 'resource' (or 'content') and optionally 'filename' and 'contentType' * * @param array|string|resource $postData Entity of POST/PUT request * @return string|bool False on error */ protected function prepareMultipart($postData) { if (is_array($postData)) { $boundary = 'BXC'.md5(rand().time()); $data = ''; foreach ($postData as $k => $v) { $data .= '--'.$boundary."\r\n"; if ((is_resource($v) && get_resource_type($v) === 'stream') || is_array($v)) { $filename = $k; $contentType = 'application/octet-stream'; if (is_array($v)) { if (isset($v['resource']) && is_resource($v['resource']) && get_resource_type($v['resource']) === 'stream') { $resource = $v['resource']; $content = stream_get_contents($resource); } else { if (isset($v['content'])) { $content = $v['content']; } else { $this->error["MULTIPART"] = "File `{$k}` not found for multipart upload"; trigger_error($this->error["MULTIPART"], E_USER_WARNING); return false; } } if (isset($v['filename'])) { $filename = $v['filename']; } if (isset($v['contentType'])) { $contentType = $v['contentType']; } } else { $content = stream_get_contents($v); } $data .= 'Content-Disposition: form-data; name="'.$k.'"; filename="'.$filename.'"'."\r\n"; $data .= 'Content-Type: '.$contentType."\r\n\r\n"; $data .= $content."\r\n"; } else { $data .= 'Content-Disposition: form-data; name="'.$k.'"'."\r\n\r\n"; $data .= $v."\r\n"; } } $data .= '--'.$boundary."--\r\n"; $postData = $data; $this->setHeader('Content-type', 'multipart/form-data; boundary='.$boundary); } return $postData; } /** * Perfoms HTTP request. * * @param string $method HTTP method (GET, POST, etc.). Note, it must be in UPPERCASE. * @param string $url Absolute URI eg. "http://user:pass @ host:port/path/?query". * @param array|string|resource $entityBody Entity body of the request. If it's resource handler then data will be read directly from the stream. * @return bool Query result (true or false). Response entity string can be get via getResult() method. Note, it's empty string if outputStream is set. */ public function query($method, $url, $entityBody = null) { $queryMethod = $method; $this->effectiveUrl = $url; $this->effectiveIp = null; $this->error = []; if(is_array($entityBody)) { $entityBody = http_build_query($entityBody, "", "&"); } $this->redirectCount = 0; while(true) { //Only absoluteURI is accepted //Location response-header field must be absoluteURI either $parsedUrl = new Uri($this->effectiveUrl); if($parsedUrl->getHost() == '') { $this->error["URI"] = "Incorrect URI: ".$this->effectiveUrl; return false; } $error = $parsedUrl->convertToPunycode(); if($error instanceof \Bitrix\Main\Error) { $this->error["URI"] = "Error converting hostname to punycode: ".$error->getMessage(); return false; } if($this->privateIp == false) { $ip = IpAddress::createByUri($parsedUrl); if($ip->isPrivate()) { $this->error["PRIVATE_IP"] = "Resolved IP is incorrect or private: ".$ip->get(); return false; } $this->effectiveIp = $ip; } //just in case of serial queries $this->disconnect(); if($this->connect($parsedUrl) === false) { return false; } $this->sendRequest($queryMethod, $parsedUrl, $entityBody); if(!$this->readHeaders()) { $this->disconnect(); return false; } if(!$this->waitResponse) { $this->disconnect(); return true; } if($this->redirect && ($location = $this->responseHeaders->get("Location")) !== null && $location <> '') { //we don't need a body on redirect $this->disconnect(); if($this->redirectCount < $this->redirectMax) { $this->effectiveUrl = $location; if($this->status == 302 || $this->status == 303) { $queryMethod = self::HTTP_GET; } $this->redirectCount++; } else { $this->error["REDIRECT"] = "Maximum number of redirects (".$this->redirectMax.") has been reached at URL ".$url; trigger_error($this->error["REDIRECT"], E_USER_WARNING); return false; } } else { //the connection is still active to read the response body break; } } return true; } /** * Sets an HTTP request header field. * * @param string $name Name of the header field. * @param string $value Value of the field. * @param bool $replace Replace existing header field with the same name or add one more. * @return $this */ public function setHeader($name, $value, $replace = true) { if($replace == true || $this->requestHeaders->get($name) === null) { $this->requestHeaders->set($name, $value); } return $this; } /** * Clears all HTTP request header fields. */ public function clearHeaders() { $this->requestHeaders->clear(); } /** * Sets an array of cookies for HTTP request. * * @param array $cookies Array of cookie_name => value pairs. * @return $this */ public function setCookies(array $cookies) { $this->requestCookies->set($cookies); return $this; } /** * Sets Basic Authorization request header field. * * @param string $user Username. * @param string $pass Password. * @return $this */ public function setAuthorization($user, $pass) { $this->setHeader("Authorization", "Basic ".base64_encode($user.":".$pass)); return $this; } /** * Sets redirect options. * * @param bool $value If true, do redirect (default true). * @param null|int $max Maximum allowed redirect count. * @return $this */ public function setRedirect($value, $max = null) { $this->redirect = ($value? true : false); if($max !== null) { $this->redirectMax = intval($max); } return $this; } /** * Sets response body waiting option. * * @param bool $value If true, wait for response body. If false, disconnect just after reading headers (default true). * @return $this */ public function waitResponse($value) { $this->waitResponse = (bool)$value; if(!$this->waitResponse) { $this->setStreamTimeout(self::DEFAULT_STREAM_TIMEOUT_NO_WAIT); } return $this; } /** * Sets connection timeout. * * @param int $value Connection timeout in seconds (default 30). * @return $this */ public function setTimeout($value) { $this->socketTimeout = intval($value); return $this; } /** * Sets socket stream reading timeout. * * @param int $value Stream reading timeout in seconds; "0" means no timeout (default 60). * @return $this */ public function setStreamTimeout($value) { $this->streamTimeout = intval($value); return $this; } /** * Sets HTTP protocol version. In version 1.1 chunked response is possible. * * @param string $value Version "1.0" or "1.1" (default "1.0"). * @return $this */ public function setVersion($value) { $this->version = $value; return $this; } /** * Sets compression option. * Consider not to use the "compress" option with the output stream if a content can be large. * Note, that compressed response is processed anyway if Content-Encoding response header field is set * * @param bool $value If true, "Accept-Encoding: gzip" will be sent. * @return $this */ public function setCompress($value) { $this->compress = (bool)$value; return $this; } /** * Sets charset for entity-body (used in the Content-Type request header field for POST and PUT) * * @param string $value Charset. * @return $this */ public function setCharset($value) { $this->requestCharset = $value; return $this; } /** * Disables ssl certificate verification. * * @return $this */ public function disableSslVerification() { $this->sslVerify = false; return $this; } /** * Enables or disables requests to private IPs. * * @param bool $value * @return $this */ public function setPrivateIp($value) { $this->privateIp = (bool)$value; return $this; } /** * Sets HTTP proxy for request. * * @param string $proxyHost Proxy host name or address (without "http://"). * @param null|int $proxyPort Proxy port number. * @param null|string $proxyUser Proxy username. * @param null|string $proxyPassword Proxy password. * @return $this */ public function setProxy($proxyHost, $proxyPort = null, $proxyUser = null, $proxyPassword = null) { $this->proxyHost = $proxyHost; $this->proxyPort = intval($proxyPort); if($this->proxyPort <= 0) { $this->proxyPort = 80; } $this->proxyUser = $proxyUser; $this->proxyPassword = $proxyPassword; return $this; } /** * Sets the response output to the stream instead of the string result. Useful for large responses. * Note, the stream must be readable/writable to support a compressed response. * Note, in this mode the result string is empty. * * @param resource $handler File or stream handler. * @return $this */ public function setOutputStream($handler) { $this->outputStream = $handler; return $this; } /** * Sets the maximum body length that will be received in $this->readBody(). * * @param int $bodyLengthMax * @return $this */ public function setBodyLengthMax($bodyLengthMax) { $this->bodyLengthMax = intval($bodyLengthMax); return $this; } /** * Downloads and saves a file. * * @param string $url URI to download. * @param string $filePath Absolute file path. * @return bool */ public function download($url, $filePath) { $dir = IO\Path::getDirectory($filePath); IO\Directory::createDirectory($dir); $file = new IO\File($filePath); $handler = $file->open("w+"); if($handler !== false) { $this->setOutputStream($handler); $res = $this->query(self::HTTP_GET, $url); if($res) { $res = $this->readBody(); } $this->disconnect(); fclose($handler); return $res; } return false; } /** * Returns URL of the last redirect if request was redirected, or initial URL if request was not redirected. * @return string */ public function getEffectiveUrl() { return $this->effectiveUrl; } /** * Sets context options and parameters. * * @param array $options Context options and parameters * @return $this */ public function setContextOptions(array $options) { $this->contextOptions = array_replace_recursive($this->contextOptions, $options); return $this; } protected function connect(Uri $url) { if($this->proxyHost <> '') { $proto = ""; $host = $this->proxyHost; $port = $this->proxyPort; } else { $proto = ($url->getScheme() == "https"? "ssl://" : ""); $host = $url->getHost(); $port = $url->getPort(); if($this->effectiveIp !== null) { //set original host to match a sertificate $this->setContextOptions(["ssl" => ["peer_name" => $host]]); //resolved in query() if private IPs were disabled $host = $this->effectiveIp->get(); } } $context = $this->createContext(); //$context can be FALSE if($context) { $res = stream_socket_client($proto.$host.":".$port, $errno, $errstr, $this->socketTimeout, STREAM_CLIENT_CONNECT, $context); } else { $res = stream_socket_client($proto.$host.":".$port, $errno, $errstr, $this->socketTimeout); } if(is_resource($res)) { $this->resource = $res; $this->peerSocketName = stream_socket_get_name($this->resource, true); if($this->streamTimeout > 0) { stream_set_timeout($this->resource, $this->streamTimeout); } return true; } if(intval($errno) > 0) { $this->error["CONNECTION"] = "[".$errno."] ".$errstr; } else { $this->error["SOCKET"] = "Socket connection error."; } return false; } protected function createContext() { if ($this->sslVerify === false) { $this->contextOptions["ssl"]["verify_peer_name"] = false; $this->contextOptions["ssl"]["verify_peer"] = false; $this->contextOptions["ssl"]["allow_self_signed"] = true; } return stream_context_create($this->contextOptions); } protected function disconnect() { if($this->resource) { fclose($this->resource); $this->resource = null; } } protected function send($data) { return fwrite($this->resource, $data); } protected function receive($bufLength = null) { if($bufLength === null) { $bufLength = self::BUF_READ_LEN; } $buf = stream_get_contents($this->resource, $bufLength); if($buf !== false) { if(is_resource($this->outputStream)) { //we can write response directly to stream (file, etc.) to minimize memory usage fwrite($this->outputStream, $buf); fflush($this->outputStream); } else { $this->result .= $buf; } } return $buf; } protected function sendRequest($method, Uri $url, $entityBody = null) { $this->status = 0; $this->result = ''; $this->responseHeaders->clear(); $this->responseCookies->clear(); $this->receivedBytesLength = 0; if($this->proxyHost <> '') { $path = $url->getLocator(); if($this->proxyUser <> '') { $this->setHeader("Proxy-Authorization", "Basic ".base64_encode($this->proxyUser.":".$this->proxyPassword)); } } else { $path = $url->getPathQuery(); } $request = $method." ".$path." HTTP/".$this->version."\r\n"; $this->setHeader("Host", $url->getHost()); $this->setHeader("Connection", "close", false); $this->setHeader("Accept", "*/*", false); $this->setHeader("Accept-Language", "en", false); if(($user = $url->getUser()) <> '') { $this->setAuthorization($user, $url->getPass()); } $cookies = $this->requestCookies->toString(); if($cookies <> '') { $this->setHeader("Cookie", $cookies); } if($this->compress) { $this->setHeader("Accept-Encoding", "gzip"); } if(!is_resource($entityBody)) { if($method == self::HTTP_POST) { //special processing for POST requests if($this->requestHeaders->get("Content-Type") === null) { $contentType = "application/x-www-form-urlencoded"; if($this->requestCharset <> '') { $contentType .= "; charset=".$this->requestCharset; } $this->setHeader("Content-Type", $contentType); } } if($entityBody <> '' || $method == self::HTTP_POST) { //HTTP/1.0 requires Content-Length for POST if($this->requestHeaders->get("Content-Length") === null) { $this->setHeader("Content-Length", BinaryString::getLength($entityBody)); } } } $request .= $this->requestHeaders->toString(); $request .= "\r\n"; $this->send($request); if(is_resource($entityBody)) { //PUT data can be a file resource while(!feof($entityBody)) { $this->send(fread($entityBody, self::BUF_POST_LEN)); } } elseif($entityBody <> '') { $this->send($entityBody); } } protected function readHeaders() { $headers = ""; while(!feof($this->resource)) { $line = fgets($this->resource, self::BUF_READ_LEN); if($line == "\r\n") { break; } if(!$this->checkErrors($line)) { return false; } $headers .= $line; } $this->parseHeaders($headers); return true; } protected function readBody() { if($this->responseHeaders->get("Transfer-Encoding") == "chunked") { while(!feof($this->resource)) { /* chunk = chunk-size [ chunk-extension ] CRLF chunk-data CRLF chunk-size = 1*HEX chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) */ $line = fgets($this->resource, self::BUF_READ_LEN); if($line == "\r\n") { continue; } if(($pos = strpos($line, ";")) !== false) { $line = substr($line, 0, $pos); } $length = hexdec($line); if(!$this->receiveBytes($length)) { return false; } } } elseif(($length = $this->responseHeaders->get("Content-Length")) !== null) { //we'll read exact length of the content if(!$this->receiveBytes($length)) { return false; } } else { //we don't know the length of the content - hope we'll reach the stream's end while(!feof($this->resource)) { $buf = $this->receive(); $this->receivedBytesLength += BinaryString::getLength($buf); if(!$this->checkErrors($buf)) { return false; } } } if($this->responseHeaders->get("Content-Encoding") == "gzip") { $this->decompress(); } return true; } protected function receiveBytes($length) { while($length > 0) { $count = ($length > self::BUF_READ_LEN? self::BUF_READ_LEN : $length); $buf = $this->receive($count); $receivedBytesLength = BinaryString::getLength($buf); $this->receivedBytesLength += $receivedBytesLength; if(!$this->checkErrors($buf)) { return false; } $length -= $receivedBytesLength; } return true; } protected function checkErrors($buf) { if($this->streamTimeout > 0) { $info = stream_get_meta_data($this->resource); if($info['timed_out']) { $this->error['STREAM_TIMEOUT'] = "Stream reading timeout of ".$this->streamTimeout." second(s) has been reached"; return false; } } if($buf === false) { $this->error['STREAM_READING'] = "Stream reading error"; return false; } if($this->bodyLengthMax > 0 && $this->receivedBytesLength > $this->bodyLengthMax) { $this->error['STREAM_LENGTH'] = "Maximum content length has been reached. Break reading"; return false; } return true; } protected function decompress() { if(is_resource($this->outputStream)) { $compressed = stream_get_contents($this->outputStream, -1, 10); $compressed = BinaryString::getSubstring($compressed, 0, -8); if($compressed <> '') { $uncompressed = gzinflate($compressed); rewind($this->outputStream); $len = fwrite($this->outputStream, $uncompressed); ftruncate($this->outputStream, $len); } } else { $compressed = BinaryString::getSubstring($this->result, 10, -8); if($compressed <> '') { $this->result = gzinflate($compressed); } } } protected function parseHeaders($headers) { foreach (explode("\n", $headers) as $k => $header) { if($k == 0) { if(preg_match('#HTTP\S+ (\d+)#', $header, $find)) { $this->status = intval($find[1]); } } elseif(strpos($header, ':') !== false) { list($headerName, $headerValue) = explode(':', $header, 2); if(strtolower($headerName) == 'set-cookie') { $this->responseCookies->addFromString($headerValue); } $this->responseHeaders->add($headerName, trim($headerValue)); } } } /** * Returns parsed HTTP response headers * * @return HttpHeaders */ public function getHeaders() { return $this->responseHeaders; } /** * Returns parsed HTTP response cookies * * @return HttpCookies */ public function getCookies() { return $this->responseCookies; } /** * Returns HTTP response status code * * @return int */ public function getStatus() { return $this->status; } /** * Returns HTTP response entity string. Note, if outputStream is set, the result will be empty string. * * @return string */ public function getResult() { if($this->waitResponse && $this->resource) { $this->readBody(); $this->disconnect(); } return $this->result; } /** * Returns array of errors on failure * * @return array Array with "error_code" => "error_message" pair */ public function getError() { return $this->error; } /** * Returns response content type * * @return string */ public function getContentType() { return $this->responseHeaders->getContentType(); } /** * Returns response content encoding * * @return string */ public function getCharset() { return $this->responseHeaders->getCharset(); } /** * Returns remote peer socket name (usually in form ip:port) * * @return string */ public function getPeerSocketName() { return $this->peerSocketName ?: ''; } /** * Returns remote peer ip address. * @return string|false */ public function getPeerAddress() { if(!preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+):(\d+)$/', $this->peerSocketName, $matches)) return false; return sprintf('%d.%d.%d.%d', $matches[1], $matches[2], $matches[3], $matches[4]); } /** * Returns remote peer ip address. * @return int|false */ public function getPeerPort() { if(!preg_match('/^(\d+)\.(\d+)\.(\d+)\.(\d+):(\d+)$/', $this->peerSocketName, $matches)) return false; return (int)$matches[5]; } }
/var/www/axolotl/data/www/msk.axolotls.ru/bitrix/modules/main/lib/web/httpclient.php