Skip to content

Commit

Permalink
Set a default ssl.peer_name context in StreamHandler (#2988)
Browse files Browse the repository at this point in the history
* Set a default ssl.peer_name context in StreamHandler

This is required when using the `force_ip_resolve` option with the stream
handler:

As that option will cause the StreamHandler to manually resolve the hostname
and then replace the hostname with the resolved IP address in the URI, PHP
will use that IP address by default in the SNI of the TLS handshake.

Set an explicit ssl.peer_name within the stream's context based on the hostname
in the URL to fix this.

Setting a proper SNI is independent from TLS certificate validation, thus this
value must not be dependent on the `verify` option.

A test cannot be added, due to a lack of TLS support with the current testing
infrastructure. TLS support cannot easily be added, because it would require a
separate port and also certificates that would need to be commited to the
repository. However correctness can be verified by setting `force_ip_resolve`
to `v4` and attempting to make a request to `https://www.example.com/`. It will
fail without this commit and work with.

* Add tests/Handler/Network/StreamHandlerTest.php
  • Loading branch information
TimWolla committed Mar 20, 2022
1 parent be834db commit 8e4a4cd
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/Handler/StreamHandler.php
Expand Up @@ -381,6 +381,9 @@ private function getDefaultContext(RequestInterface $request): array
'ignore_errors' => true,
'follow_location' => 0,
],
'ssl' => [
'peer_name' => $request->getUri()->getHost(),
],
];

$body = (string) $request->getBody();
Expand Down
71 changes: 71 additions & 0 deletions tests/Handler/Network/StreamHandlerTest.php
@@ -0,0 +1,71 @@
<?php

namespace GuzzleHttp\Test\Handler\Network;

use GuzzleHttp\Client;
use GuzzleHttp\Handler\StreamHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
use PHPUnit\Framework\TestCase;

/**
* @covers \GuzzleHttp\Handler\StreamHandler
*/
class StreamHandlerTest extends TestCase
{
public function setUp(): void
{
if (!($_SERVER['GUZZLE_TEST_ALLOW_NETWORK'] ?? false)) {
self::markTestSkipped("This test requires the GUZZLE_TEST_ALLOW_NETWORK environment variable.");
}
}

public function testSslRequestWorks()
{
$handler = new StreamHandler();

$response = $handler(
new Request('GET', 'https://www.example.com/'),
[
RequestOptions::STREAM => true,
]
)->wait();

self::assertSame(200, $response->getStatusCode());
self::assertStringContainsString('<h1>Example Domain</h1>', (string)$response->getBody());
}

public function testSslRequestWorksWithForceIpResolve()
{
$handler = new StreamHandler();

$response = $handler(
new Request('GET', 'https://www.example.com/'),
[
RequestOptions::STREAM => true,
'force_ip_resolve' => 'v4',
]
)->wait();

self::assertSame(200, $response->getStatusCode());
self::assertStringContainsString('<h1>Example Domain</h1>', (string)$response->getBody());
}

public function testSslRequestWorksWithForceIpResolveAfterRedirect()
{
$client = new Client(['handler' => HandlerStack::create(new StreamHandler())]);

$response = $client->send(
// Redirects to https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
new Request('GET', 'https://git.io/JvXDl'),
[
RequestOptions::STREAM => true,
'force_ip_resolve' => 'v4',
]
);

self::assertSame(200, $response->getStatusCode());
self::assertStringContainsString('jobsjob_idstepsrun', (string)$response->getBody());
}
}

0 comments on commit 8e4a4cd

Please sign in to comment.