HTTP proxy adapter throwing exceptions when called from inside container

My org ran into this issue when trying to set up a containerized Omeka S instance, which is built on top of Laminas, using an OpenShift cluster. In this case, the application keeps throwing exceptions when making requests via the proxy adapter to download data.

I haven’t done much web application development, and I’m looking for some direction about where to look next based on the behavior I’ve encountered.

The first indication of the issue was when Omeka S failed to download oembed links that worked on instances that weren’t containerized. The containerized instance reported the following runtime error:

Laminas\Http\Client\Adapter\Exception\RuntimeException
Unable to connect to HTTPS server through proxy: could not negotiate secure connection.

Details:

Laminas\Http\Client\Adapter\Exception\RuntimeException: Unable to connect to HTTPS server through proxy: could not negotiate secure connection. in /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php:296
Stack trace:
#0 /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php(165): Laminas\Http\Client\Adapter\Proxy->connectHandshake('vimeo.com', 443, '1.1', Array)
#1 /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client.php(1457): Laminas\Http\Client\Adapter\Proxy->write('GET', Object(Laminas\Uri\Http), '1.1', Array, '')
#2 /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client.php(945): Laminas\Http\Client->doRequest(Object(Laminas\Uri\Http), 'GET', true, Array, '')
#3 /opt/app-root/src/omeka-s/application/src/Media/Ingester/OEmbed.php(142): Laminas\Http\Client->send()
#4 /opt/app-root/src/omeka-s/application/src/Media/Ingester/OEmbed.php(73): Omeka\Media\Ingester\OEmbed->makeRequest('https://vimeo.c...', 'OEmbed URL', Object(Omeka\Stdlib\ErrorStore))
#5 /opt/app-root/src/omeka-s/application/src/Api/Adapter/MediaAdapter.php(159): Omeka\Media\Ingester\OEmbed->ingest(Object(Omeka\Entity\Media), Object(Omeka\Api\Request), Object(Omeka\Stdlib\ErrorStore))
#6 /opt/app-root/src/omeka-s/application/src/Api/Adapter/AbstractEntityAdapter.php(590): Omeka\Api\Adapter\MediaAdapter->hydrate(Object(Omeka\Api\Request), Object(Omeka\Entity\Media), Object(Omeka\Stdlib\ErrorStore))
#7 /opt/app-root/src/omeka-s/application/src/Api/Adapter/ItemAdapter.php(240): Omeka\Api\Adapter\AbstractEntityAdapter->hydrateEntity(Object(Omeka\Api\Request), Object(Omeka\Entity\Media), Object(Omeka\Stdlib\ErrorStore))
#8 /opt/app-root/src/omeka-s/application/src/Api/Adapter/AbstractEntityAdapter.php(590): Omeka\Api\Adapter\ItemAdapter->hydrate(Object(Omeka\Api\Request), Object(Omeka\Entity\Item), Object(Omeka\Stdlib\ErrorStore))
#9 /opt/app-root/src/omeka-s/application/src/Api/Adapter/AbstractEntityAdapter.php(318): Omeka\Api\Adapter\AbstractEntityAdapter->hydrateEntity(Object(Omeka\Api\Request), Object(Omeka\Entity\Item), Object(Omeka\Stdlib\ErrorStore))
#10 /opt/app-root/src/omeka-s/application/src/Api/Manager.php(224): Omeka\Api\Adapter\AbstractEntityAdapter->create(Object(Omeka\Api\Request))
#11 /opt/app-root/src/omeka-s/application/src/Api/Manager.php(78): Omeka\Api\Manager->execute(Object(Omeka\Api\Request))
#12 /opt/app-root/src/omeka-s/application/src/Mvc/Controller/Plugin/Api.php(99): Omeka\Api\Manager->create('items', Array, Array, Array)
#13 /opt/app-root/src/omeka-s/application/src/Controller/Admin/ItemController.php(207): Omeka\Mvc\Controller\Plugin\Api->create('items', Array, Array)
#14 /opt/app-root/src/omeka-s/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(77): Omeka\Controller\Admin\ItemController->addAction()
#15 /opt/app-root/src/omeka-s/vendor/laminas/laminas-eventmanager/src/EventManager.php(321): Laminas\Mvc\Controller\AbstractActionController->onDispatch(Object(Laminas\Mvc\MvcEvent))
#16 /opt/app-root/src/omeka-s/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\EventManager\EventManager->triggerListeners(Object(Laminas\Mvc\MvcEvent), Object(Closure))
#17 /opt/app-root/src/omeka-s/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(103): Laminas\EventManager\EventManager->triggerEventUntil(Object(Closure), Object(Laminas\Mvc\MvcEvent))
#18 /opt/app-root/src/omeka-s/vendor/laminas/laminas-mvc/src/DispatchListener.php(139): Laminas\Mvc\Controller\AbstractController->dispatch(Object(Laminas\Http\PhpEnvironment\Request), Object(Laminas\Http\PhpEnvironment\Response))
#19 /opt/app-root/src/omeka-s/vendor/laminas/laminas-eventmanager/src/EventManager.php(321): Laminas\Mvc\DispatchListener->onDispatch(Object(Laminas\Mvc\MvcEvent))
#20 /opt/app-root/src/omeka-s/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\EventManager\EventManager->triggerListeners(Object(Laminas\Mvc\MvcEvent), Object(Closure))
#21 /opt/app-root/src/omeka-s/vendor/laminas/laminas-mvc/src/Application.php(331): Laminas\EventManager\EventManager->triggerEventUntil(Object(Closure), Object(Laminas\Mvc\MvcEvent))
#22 /opt/app-root/src/omeka-s/index.php(21): Laminas\Mvc\Application->run()
#23 {main}

The runtime error was accompanied by the following php warnings captured in the container logs:

Warning: stream_socket_enable_crypto(): Peer certificate CN=`vimeo.map.fastly.net' did not match expected CN=`<http_config proxy_host value>' in /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php on line 289
Warning: stream_socket_enable_crypto(): SSL/TLS already set-up for this stream in /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php on line 289
Warning: stream_socket_enable_crypto(): SSL/TLS already set-up for this stream in /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php on line 289
Warning: stream_socket_enable_crypto(): SSL/TLS already set-up for this stream in /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php on line 289

The php warning led me to a stackoverflow answer for the Zend Framework. I disabled the ssl peer and peer name verification expecting to see the oembed functionality start working. The initial result was a grab bag of errors that were further down the line, but I was able to successfully download the oembed data after retrying the operation multiple times.

These new errors included the same proxy runtime error as above accompanied by a new php warning:

Warning: stream_socket_enable_crypto(): SSL: Handshake timed out in /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php on line 289

and what appears to be an empty or malformed response from the data destination:

Laminas\Http\Exception\InvalidArgumentException
A valid response status line was not found in the provided string

Details:

Laminas\Http\Exception\InvalidArgumentException: A valid response status line was not found in the provided string in /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Response.php:266
Stack trace:
#0 /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Response.php(204): Laminas\Http\Response->parseStatusLine('')
#1 /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php(271): Laminas\Http\Response::fromString('')
#2 /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client/Adapter/Proxy.php(165): Laminas\Http\Client\Adapter\Proxy->connectHandshake('vimeo.com', 443, '1.1', Array)
#3 /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client.php(1457): Laminas\Http\Client\Adapter\Proxy->write('GET', Object(Laminas\Uri\Http), '1.1', Array, '')
#4 /opt/app-root/src/omeka-s/vendor/laminas/laminas-http/src/Client.php(945): Laminas\Http\Client->doRequest(Object(Laminas\Uri\Http), 'GET', true, Array, '')
#5 /opt/app-root/src/omeka-s/application/src/Media/Ingester/OEmbed.php(142): Laminas\Http\Client->send()
#6 /opt/app-root/src/omeka-s/application/src/Media/Ingester/OEmbed.php(73): Omeka\Media\Ingester\OEmbed->makeRequest('https://vimeo.c...', 'OEmbed URL', Object(Omeka\Stdlib\ErrorStore))
#7 /opt/app-root/src/omeka-s/application/src/Api/Adapter/MediaAdapter.php(159): Omeka\Media\Ingester\OEmbed->ingest(Object(Omeka\Entity\Media), Object(Omeka\Api\Request), Object(Omeka\Stdlib\ErrorStore))
#8 /opt/app-root/src/omeka-s/application/src/Api/Adapter/AbstractEntityAdapter.php(590): Omeka\Api\Adapter\MediaAdapter->hydrate(Object(Omeka\Api\Request), Object(Omeka\Entity\Media), Object(Omeka\Stdlib\ErrorStore))
#9 /opt/app-root/src/omeka-s/application/src/Api/Adapter/ItemAdapter.php(240): Omeka\Api\Adapter\AbstractEntityAdapter->hydrateEntity(Object(Omeka\Api\Request), Object(Omeka\Entity\Media), Object(Omeka\Stdlib\ErrorStore))
#10 /opt/app-root/src/omeka-s/application/src/Api/Adapter/AbstractEntityAdapter.php(590): Omeka\Api\Adapter\ItemAdapter->hydrate(Object(Omeka\Api\Request), Object(Omeka\Entity\Item), Object(Omeka\Stdlib\ErrorStore))
#11 /opt/app-root/src/omeka-s/application/src/Api/Adapter/AbstractEntityAdapter.php(318): Omeka\Api\Adapter\AbstractEntityAdapter->hydrateEntity(Object(Omeka\Api\Request), Object(Omeka\Entity\Item), Object(Omeka\Stdlib\ErrorStore))
#12 /opt/app-root/src/omeka-s/application/src/Api/Manager.php(224): Omeka\Api\Adapter\AbstractEntityAdapter->create(Object(Omeka\Api\Request))
#13 /opt/app-root/src/omeka-s/application/src/Api/Manager.php(78): Omeka\Api\Manager->execute(Object(Omeka\Api\Request))
#14 /opt/app-root/src/omeka-s/application/src/Mvc/Controller/Plugin/Api.php(99): Omeka\Api\Manager->create('items', Array, Array, Array)
#15 /opt/app-root/src/omeka-s/application/src/Controller/Admin/ItemController.php(207): Omeka\Mvc\Controller\Plugin\Api->create('items', Array, Array)
#16 /opt/app-root/src/omeka-s/vendor/laminas/laminas-mvc/src/Controller/AbstractActionController.php(77): Omeka\Controller\Admin\ItemController->addAction()
#17 /opt/app-root/src/omeka-s/vendor/laminas/laminas-eventmanager/src/EventManager.php(321): Laminas\Mvc\Controller\AbstractActionController->onDispatch(Object(Laminas\Mvc\MvcEvent))
#18 /opt/app-root/src/omeka-s/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\EventManager\EventManager->triggerListeners(Object(Laminas\Mvc\MvcEvent), Object(Closure))
#19 /opt/app-root/src/omeka-s/vendor/laminas/laminas-mvc/src/Controller/AbstractController.php(103): Laminas\EventManager\EventManager->triggerEventUntil(Object(Closure), Object(Laminas\Mvc\MvcEvent))
#20 /opt/app-root/src/omeka-s/vendor/laminas/laminas-mvc/src/DispatchListener.php(139): Laminas\Mvc\Controller\AbstractController->dispatch(Object(Laminas\Http\PhpEnvironment\Request), Object(Laminas\Http\PhpEnvironment\Response))
#21 /opt/app-root/src/omeka-s/vendor/laminas/laminas-eventmanager/src/EventManager.php(321): Laminas\Mvc\DispatchListener->onDispatch(Object(Laminas\Mvc\MvcEvent))
#22 /opt/app-root/src/omeka-s/vendor/laminas/laminas-eventmanager/src/EventManager.php(178): Laminas\EventManager\EventManager->triggerListeners(Object(Laminas\Mvc\MvcEvent), Object(Closure))
#23 /opt/app-root/src/omeka-s/vendor/laminas/laminas-mvc/src/Application.php(331): Laminas\EventManager\EventManager->triggerEventUntil(Object(Closure), Object(Laminas\Mvc\MvcEvent))
#24 /opt/app-root/src/omeka-s/index.php(21): Laminas\Mvc\Application->run()
#25 {main}

Hello and welcome to our forums! :smiley:

Unfortunately, I don’t know the “Omeka S”, so I can’t give any instructions.

The proxy adapter from laminas-http is used and I hope this is correct in your use case. The next step is that you must check your configuration in Omeka S that all required parameters are set for this adapter.

If your level of knowledge allows it, you can also try to create a connection only with laminas-http and the proxy adapter.
The description with code example can be found in the documentation of laminas-http. The installation is done via Composer.

I hope it helps to find a solution or at least a way to a solution.

Thanks for responding.

The application was intentionally configured to use the proxy adapter. I don’t have any reason to believe that it’s not correctly configuring the laminas client at this time.

Some updates after working with the person who set up the container.

Other programs within the container that make http requests through the same proxy server work as expected. Examples include yum, curl, and the smtp program used to send emails from the application.

After seeing the curl command line tool work, I wrote a small test program using php’s curl library. None of the command line or test program requests failed, but I did see a variable response time. Some requests took 40+ seconds to complete.

The application almost completely worked after switching to the laminas-http curl adapter. I could use the application without any runtime exceptions occurring.

It seemed the operations that used the laminas client exhibited the same runtime variability as the command line tests. Some operations returned 504 timeout response codes, but the application still reported the operation as a success and produced the expected artifacts when I navigated back to the app admin panel manually.

I’m not sure which server (container apache, proxy, destination) is generating the 504 error codes.

'http_client' => [
        'adapter'    => \Laminas\Http\Client\Adapter\Curl::class,
        'proxy_host' => ‘<proxy.address>’,
        'proxy_port' => <proxy port>,
    ],

It seems the issue is specific to the interaction between the laminas proxy adapter, the proxy server, and/or the destination server.

Next step I’m considering is to try to replicate the errors by routing a non-containerized instance through the proxy to determine whether the issue is dependent on being inside a container.

I’ll be taking your advice about writing a simpler test program using the laminas client to try to replicate the errors and debug once I establish where it can be run.

Tangential, is editing the original post disabled for new users or for all users?