Skip to content

Commit eafa574

Browse files
committed
Add support for stream context options with connectors
1 parent a1f4e7c commit eafa574

File tree

3 files changed

+50
-13
lines changed

3 files changed

+50
-13
lines changed

src/Connector.php

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,69 @@ class Connector implements ConnectorInterface
1212
{
1313
private $loop;
1414
private $resolver;
15+
private $peerNameCtxKey;
1516

1617
public function __construct(LoopInterface $loop, Resolver $resolver)
1718
{
1819
$this->loop = $loop;
1920
$this->resolver = $resolver;
21+
$this->peerNameCtxKey = PHP_VERSION_ID < 50600 ? 'CN_match' : 'peer_name';
2022
}
2123

22-
public function create($host, $port)
24+
/**
25+
* We need to set various context options related to the expected SSL certificate name here even though we
26+
* don't know whether we are creating an SSL connection or not, for compatibility with PHP<5.6.
27+
*
28+
* We don't specifically enable verify_peer or verify_peer_name here because these may require additional
29+
* options such as specifying a cafile etc, which means the user will need to do this manually.
30+
*
31+
* @param array $contextOpts
32+
* @param string $host
33+
* @return array
34+
*/
35+
private function normalizeSSLContextOptions(array $contextOpts, $host)
36+
{
37+
// Allow the user to override the certificate peer name with the context option, unless it's a wildcard
38+
if (isset($contextOpts['ssl']['peer_name']) && false === strpos($contextOpts['ssl']['peer_name'], '*')) {
39+
$host = $contextOpts['ssl']['peer_name'];
40+
} else if ($contextOpts['ssl']['CN_match'] && false === strpos($contextOpts['ssl']['CN_match'], '*')) {
41+
$host = $contextOpts['ssl']['CN_match'];
42+
}
43+
44+
// Make sure that SNI requests the correct certificate name
45+
if (!isset($contextOpts['ssl']['SNI_enabled'])
46+
|| $contextOpts['ssl']['SNI_enabled'] && !isset($contextOpts['ssl']['SNI_server_name'])) {
47+
$contextOpts['ssl']['SNI_enabled'] = true;
48+
$contextOpts['ssl']['SNI_server_name'] = $host;
49+
}
50+
51+
// Make sure PHP verifies the certificate name against the requested name
52+
if (!isset($contextOpts['ssl'][$this->peerNameCtxKey])) {
53+
$contextOpts['ssl'][$this->peerNameCtxKey] = $host;
54+
}
55+
56+
// Disable TLS compression by default
57+
if (!isset($contextOpts['ssl']['disable_compression'])) {
58+
$contextOpts['ssl']['disable_compression'] = true;
59+
}
60+
61+
return $contextOpts;
62+
}
63+
64+
public function create($host, $port, array $contextOpts = [])
2365
{
2466
return $this
2567
->resolveHostname($host)
26-
->then(function ($address) use ($port, $host) {
27-
return $this->createSocketForAddress($address, $port, $host);
68+
->then(function ($address) use ($port, $host, $contextOpts) {
69+
$contextOpts = $this->normalizeSSLContextOptions($contextOpts, $host);
70+
return $this->createSocketForAddress($address, $port, $contextOpts);
2871
});
2972
}
3073

31-
public function createSocketForAddress($address, $port, $hostName = null)
74+
public function createSocketForAddress($address, $port, array $contextOpts = [])
3275
{
3376
$url = $this->getSocketUrl($address, $port);
3477

35-
$contextOpts = array();
36-
if ($hostName !== null) {
37-
$contextOpts['ssl']['SNI_enabled'] = true;
38-
$contextOpts['ssl']['SNI_server_name'] = $hostName;
39-
}
40-
4178
$flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
4279
$context = stream_context_create($contextOpts);
4380
$socket = stream_socket_client($url, $errno, $errstr, 0, $flags, $context);

src/ConnectorInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44

55
interface ConnectorInterface
66
{
7-
public function create($host, $port);
7+
public function create($host, $port, array $contextOpts = []);
88
}

src/SecureConnector.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public function __construct(ConnectorInterface $connector, LoopInterface $loop)
1616
$this->streamEncryption = new StreamEncryption($loop);
1717
}
1818

19-
public function create($host, $port)
19+
public function create($host, $port, array $contextOpts = [])
2020
{
21-
return $this->connector->create($host, $port)->then(function (Stream $stream) {
21+
return $this->connector->create($host, $port, $contextOpts)->then(function (Stream $stream) {
2222
// (unencrypted) connection succeeded => try to enable encryption
2323
return $this->streamEncryption->enable($stream)->then(null, function ($error) use ($stream) {
2424
// establishing encryption failed => close invalid connection and return error

0 commit comments

Comments
 (0)