Update
This commit is contained in:
109
core/bitrix/modules/main/lib/web/http/range.php
Normal file
109
core/bitrix/modules/main/lib/web/http/range.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Bitrix Framework
|
||||
* @package bitrix
|
||||
* @subpackage main
|
||||
* @copyright 2001-2025 Bitrix
|
||||
*/
|
||||
|
||||
namespace Bitrix\Main\Web\Http;
|
||||
|
||||
use Bitrix\Main\ArgumentException;
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Range
|
||||
*/
|
||||
class Range
|
||||
{
|
||||
protected int $start;
|
||||
protected int $end;
|
||||
|
||||
public function __construct(?int $start, ?int $end, int $size)
|
||||
{
|
||||
if ($start === null && $end !== null)
|
||||
{
|
||||
// Returning requested suffix of the file
|
||||
$this->start = $size - $end;
|
||||
$this->end = $size - 1;
|
||||
}
|
||||
elseif ($start !== null && $end === null)
|
||||
{
|
||||
// Returning all bytes with offset
|
||||
$this->start = $start;
|
||||
$this->end = $size - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->start = (int)$start;
|
||||
$this->end = (int)$end;
|
||||
}
|
||||
|
||||
if ($this->start > $this->end || $this->start >= $size)
|
||||
{
|
||||
throw new ArgumentException('Requested Range Not Satisfiable');
|
||||
}
|
||||
|
||||
if ($this->end >= $size)
|
||||
{
|
||||
$this->end = $size - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function getStart(): int
|
||||
{
|
||||
return $this->start;
|
||||
}
|
||||
|
||||
public function getEnd(): int
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $header
|
||||
* @param int $size
|
||||
* @return Range[] | null
|
||||
*/
|
||||
public static function createFromString(string $header, int $size): ?array
|
||||
{
|
||||
if (str_contains($header, '='))
|
||||
{
|
||||
[$unit, $value] = explode("=", $header);
|
||||
if (strtolower(trim($unit)) === 'bytes')
|
||||
{
|
||||
$ranges = [];
|
||||
foreach (explode(',', $value) as $range)
|
||||
{
|
||||
if (str_contains($range, '-'))
|
||||
{
|
||||
[$start, $end] = explode('-', $range);
|
||||
|
||||
if (!is_numeric($start))
|
||||
{
|
||||
$start = null;
|
||||
}
|
||||
if (!is_numeric($end))
|
||||
{
|
||||
$end = null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$ranges[] = new Range($start, $end, $size);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// all ranges must be correct
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ranges ?: null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ class HttpClient implements Log\LoggerAwareInterface, ClientInterface, Http\Debu
|
||||
protected $useCurl = false;
|
||||
protected $curlLogFile = null;
|
||||
protected $shouldFetchBody = null;
|
||||
protected $sendEvents = true;
|
||||
protected Http\ResponseBuilderInterface $responseBuilder;
|
||||
|
||||
protected HttpHeaders $headers;
|
||||
@@ -93,6 +94,7 @@ class HttpClient implements Log\LoggerAwareInterface, ClientInterface, Http\Debu
|
||||
* "headers" array of headers for HTTP request.
|
||||
* "useCurl" bool Enable CURL (default false).
|
||||
* "curlLogFile" string Full path to CURL log file.
|
||||
* "sendEvents" bool Send events (default true).
|
||||
* "responseBuilder" Http\ResponseBuilderInterface Response builder.
|
||||
* Almost all options can be set separately with setters.
|
||||
*/
|
||||
@@ -177,6 +179,10 @@ class HttpClient implements Log\LoggerAwareInterface, ClientInterface, Http\Debu
|
||||
{
|
||||
$this->curlLogFile = $options['curlLogFile'];
|
||||
}
|
||||
if (isset($options['sendEvents']))
|
||||
{
|
||||
$this->sendEvents = (bool)$options['sendEvents'];
|
||||
}
|
||||
if (isset($options['responseBuilder']))
|
||||
{
|
||||
$this->setResponseBuilder($options['responseBuilder']);
|
||||
@@ -880,13 +886,16 @@ class HttpClient implements Log\LoggerAwareInterface, ClientInterface, Http\Debu
|
||||
}
|
||||
}
|
||||
|
||||
// Here's the chance to tune up the client and to rebuild the request.
|
||||
$event = new Http\RequestEvent($this, $request, 'OnHttpClientBuildRequest');
|
||||
$event->send();
|
||||
|
||||
foreach ($event->getResults() as $eventResult)
|
||||
if ($this->sendEvents)
|
||||
{
|
||||
$request = $eventResult->getRequest();
|
||||
// Here's the chance to tune up the client and to rebuild the request.
|
||||
$event = new Http\RequestEvent($this, $request, 'OnHttpClientBuildRequest');
|
||||
$event->send();
|
||||
|
||||
foreach ($event->getResults() as $eventResult)
|
||||
{
|
||||
$request = $eventResult->getRequest();
|
||||
}
|
||||
}
|
||||
|
||||
return new Http\Request(
|
||||
|
||||
@@ -63,13 +63,16 @@ class Json
|
||||
|
||||
public static function validate(string $data): bool
|
||||
{
|
||||
if (function_exists('json_validate'))
|
||||
{
|
||||
return json_validate($data);
|
||||
}
|
||||
// On PHP 8.3 replace to
|
||||
// return json_validate($data);
|
||||
|
||||
try
|
||||
{
|
||||
if ($data === 'null') // consistency with json_validate
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return json_decode(json: $data, associative: true, flags: JSON_THROW_ON_ERROR) !== null;
|
||||
}
|
||||
catch (JsonException)
|
||||
|
||||
@@ -73,12 +73,27 @@ class Uri implements \JsonSerializable, UriInterface
|
||||
}
|
||||
|
||||
$authority = $this->getAuthority();
|
||||
$path = $this->getPath();
|
||||
if ($authority != '')
|
||||
{
|
||||
$uri .= '//' . $authority;
|
||||
|
||||
if (!str_starts_with($path, '/'))
|
||||
{
|
||||
$uri .= '/';
|
||||
}
|
||||
$uri .= $path;
|
||||
}
|
||||
else
|
||||
{
|
||||
$uri .= $path;
|
||||
}
|
||||
|
||||
$uri .= $this->getPathQuery();
|
||||
$query = $this->getQuery();
|
||||
if ($query != '')
|
||||
{
|
||||
$uri .= '?' . $query;
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
@@ -168,7 +183,7 @@ class Uri implements \JsonSerializable, UriInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path with the query.
|
||||
* Returns the path with the query. Empty path is treated as a root (/)
|
||||
* @return string
|
||||
*/
|
||||
public function getPathQuery()
|
||||
|
||||
123
core/bitrix/modules/main/lib/web/useragent/platform.php
Normal file
123
core/bitrix/modules/main/lib/web/useragent/platform.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Bitrix\Main\Web\UserAgent;
|
||||
|
||||
enum Platform: string
|
||||
{
|
||||
case Android = 'Android';
|
||||
case Ios = 'iOS';
|
||||
case Windows = 'Windows';
|
||||
case Macos = 'macOS';
|
||||
case LinuxRpm = 'Linux RPM';
|
||||
case LinuxDeb = 'Linux DEB';
|
||||
case Unknown = 'Unknown';
|
||||
|
||||
public function isMobile(): bool
|
||||
{
|
||||
return in_array($this, [self::Android, self::Ios], true);
|
||||
}
|
||||
|
||||
public function isDesktop(): bool
|
||||
{
|
||||
return in_array($this, [self::Windows, self::Macos, self::LinuxRpm, self::LinuxDeb], true);
|
||||
}
|
||||
|
||||
public function isLinux(): bool
|
||||
{
|
||||
return in_array($this, [self::LinuxRpm, self::LinuxDeb], true);
|
||||
}
|
||||
|
||||
public static function fromUserAgent(string $userAgent): self
|
||||
{
|
||||
$userAgent = strtolower($userAgent);
|
||||
|
||||
if (str_contains($userAgent, 'bitrixmobile') || str_contains($userAgent, 'bitrix24/'))
|
||||
{
|
||||
if (
|
||||
str_contains($userAgent, 'iphone')
|
||||
|| str_contains($userAgent, 'ipad')
|
||||
|| str_contains($userAgent, 'darwin')
|
||||
)
|
||||
{
|
||||
return self::Ios;
|
||||
}
|
||||
|
||||
return self::Android;
|
||||
}
|
||||
|
||||
if (self::isWindowsByUserAgent($userAgent))
|
||||
{
|
||||
return self::Windows;
|
||||
}
|
||||
|
||||
if (self::isMacosByUserAgent($userAgent))
|
||||
{
|
||||
return self::Macos;
|
||||
}
|
||||
|
||||
if (self::isLinuxByUserAgent($userAgent))
|
||||
{
|
||||
return self::getLinuxPlatform($userAgent);
|
||||
}
|
||||
|
||||
if (self::isIosByUserAgent($userAgent))
|
||||
{
|
||||
return self::Ios;
|
||||
}
|
||||
|
||||
if (self::isAndroidByUserAgent($userAgent) || str_contains($userAgent, 'carddavbitrix24'))
|
||||
{
|
||||
return self::Android;
|
||||
}
|
||||
|
||||
return self::Unknown;
|
||||
}
|
||||
|
||||
private static function isWindowsByUserAgent(string $userAgent): bool
|
||||
{
|
||||
return str_contains($userAgent, 'windows')
|
||||
|| str_contains($userAgent, 'win32')
|
||||
|| str_contains($userAgent, 'win64');
|
||||
}
|
||||
|
||||
private static function isLinuxByUserAgent(string $userAgent): bool
|
||||
{
|
||||
return str_contains($userAgent, 'linux') && !self::isAndroidByUserAgent($userAgent);
|
||||
}
|
||||
|
||||
private static function isIosByUserAgent(string $userAgent): bool
|
||||
{
|
||||
return str_contains($userAgent, 'iphone')
|
||||
|| str_contains($userAgent, 'ipad')
|
||||
|| str_contains($userAgent, 'ipod')
|
||||
|| (str_contains($userAgent, 'ios') && !str_contains($userAgent, 'windows'));
|
||||
}
|
||||
|
||||
private static function isMacosByUserAgent(string $userAgent): bool
|
||||
{
|
||||
return (str_contains($userAgent, 'mac os') && !str_contains($userAgent, 'like mac os x'))
|
||||
|| str_contains($userAgent, 'macintosh');
|
||||
}
|
||||
|
||||
private static function isAndroidByUserAgent(string $userAgent): bool
|
||||
{
|
||||
return str_contains($userAgent, 'android');
|
||||
}
|
||||
|
||||
private static function getLinuxPlatform(string $userAgent): self
|
||||
{
|
||||
if (
|
||||
str_contains($userAgent, 'rpm')
|
||||
|| str_contains($userAgent, 'fedora')
|
||||
|| str_contains($userAgent, 'centos')
|
||||
|| str_contains($userAgent, 'rhel')
|
||||
)
|
||||
{
|
||||
return self::LinuxRpm;
|
||||
}
|
||||
|
||||
return self::LinuxDeb;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user