Skip to content

Commit 8e4a4cd

Browse files
authored
Set a default ssl.peer_name context in StreamHandler (#2988)
* 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
1 parent be834db commit 8e4a4cd

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

src/Handler/StreamHandler.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,9 @@ private function getDefaultContext(RequestInterface $request): array
381381
'ignore_errors' => true,
382382
'follow_location' => 0,
383383
],
384+
'ssl' => [
385+
'peer_name' => $request->getUri()->getHost(),
386+
],
384387
];
385388

386389
$body = (string) $request->getBody();
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
namespace GuzzleHttp\Test\Handler\Network;
4+
5+
use GuzzleHttp\Client;
6+
use GuzzleHttp\Handler\StreamHandler;
7+
use GuzzleHttp\HandlerStack;
8+
use GuzzleHttp\Psr7\Request;
9+
use GuzzleHttp\RequestOptions;
10+
use PHPUnit\Framework\TestCase;
11+
12+
/**
13+
* @covers \GuzzleHttp\Handler\StreamHandler
14+
*/
15+
class StreamHandlerTest extends TestCase
16+
{
17+
public function setUp(): void
18+
{
19+
if (!($_SERVER['GUZZLE_TEST_ALLOW_NETWORK'] ?? false)) {
20+
self::markTestSkipped("This test requires the GUZZLE_TEST_ALLOW_NETWORK environment variable.");
21+
}
22+
}
23+
24+
public function testSslRequestWorks()
25+
{
26+
$handler = new StreamHandler();
27+
28+
$response = $handler(
29+
new Request('GET', 'https://www.example.com/'),
30+
[
31+
RequestOptions::STREAM => true,
32+
]
33+
)->wait();
34+
35+
self::assertSame(200, $response->getStatusCode());
36+
self::assertStringContainsString('<h1>Example Domain</h1>', (string)$response->getBody());
37+
}
38+
39+
public function testSslRequestWorksWithForceIpResolve()
40+
{
41+
$handler = new StreamHandler();
42+
43+
$response = $handler(
44+
new Request('GET', 'https://www.example.com/'),
45+
[
46+
RequestOptions::STREAM => true,
47+
'force_ip_resolve' => 'v4',
48+
]
49+
)->wait();
50+
51+
self::assertSame(200, $response->getStatusCode());
52+
self::assertStringContainsString('<h1>Example Domain</h1>', (string)$response->getBody());
53+
}
54+
55+
public function testSslRequestWorksWithForceIpResolveAfterRedirect()
56+
{
57+
$client = new Client(['handler' => HandlerStack::create(new StreamHandler())]);
58+
59+
$response = $client->send(
60+
// Redirects to https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
61+
new Request('GET', 'https://git.io/JvXDl'),
62+
[
63+
RequestOptions::STREAM => true,
64+
'force_ip_resolve' => 'v4',
65+
]
66+
);
67+
68+
self::assertSame(200, $response->getStatusCode());
69+
self::assertStringContainsString('jobsjob_idstepsrun', (string)$response->getBody());
70+
}
71+
}

0 commit comments

Comments
 (0)