Блог
Таймаут чтения в классе Core_Http с драйвером сокетов
Для реализации местоположения пользователя на сайте под управлением HostCMS мы обычно используем класс Core_Geoip
, который для определения города обращается к сервису ipgeobase.ru. В начале января этот сервис был недоступен или перегружен, и у клиента наблюдалась проблема с зависанием сайта, которое было вызвано как раз обращением к этому сервису. В таком поведении была некоторая странность, ведь внутри класса на выполнение запроса устанавливается таймаут запроса, который в случае долгого ответа/неответа должен прерывать запрос, но похоже, что он не сработал, почему же?
В классе Core_Geoip
есть такой код, устанавливающий таймаут запроса в 5 секунд:
$oCore_Http = Core_Http::instance()
->url($sUrl)
->port($iPort)
->method('GET')
->timeout(5)
->execute();
В качестве драйвера для HTTP-запросов на сайте используется реализация на сокетах через класс Core_Http_Socket
, идем внутрь него, внимательно смотрим на код и видим, что внутри устанаваливается таймаут создания сокета:
$fp = @fsockopen($socketHost, $this->_port, $errno, $errstr, $this->_timeout);
...и таймаут потока:
if (function_exists('stream_set_timeout'))
{
stream_set_timeout($fp, $this->_timeout);
}
Далее идем в документацию функций stream_set_timeout
и fgets
и читаем, что для определения таймаута потока необходимо анализировать возвращаемый результат либо stream_get_meta_data
, либо fgets
, чего не в коде драйвера Core_Http_Socket
не выполняется:
$datastr = '';
while (!feof($fp))
{
$datastr .= fgets($fp, 65536);
}
fclose($fp);
Получаем, что класс Core_Http_Socket
принимет параметр timeout
и даже пытается его использовать, но делает это некорректно, поэтому выполнение HTTP-запросов через данный класс является небезопасным.
После сообщения в тех. поддержку был получен ответ, что решение этой проблемы появится в обновлении 6.7.8, мы рекомендуем не использовать драйвер сокетов для выполнения HTTP-запросов до установки соответствующего обновления, а использовать вместо него реализацию поверх библиотеки curl, которая также есть в системе.