Skip to content

Update Socket to work around sending secure HTTPS responses with PHP < 7.1.4 #244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"require": {
"php": ">=5.3.0",
"ringcentral/psr7": "^1.2",
"react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5",
"react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.6",
"react/socket": "^1.0 || ^0.8.3",
"react/stream": "^1.0 || ^0.7.1",
"react/promise": "^2.3 || ^1.2.1",
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"react/promise-stream": "^0.1.1"
Expand All @@ -19,7 +19,6 @@
},
"require-dev": {
"phpunit/phpunit": "^4.8.10||^5.0",
"react/socket": "^1.0 || ^0.8 || ^0.7",
"clue/block-react": "^1.1"
}
}
34 changes: 1 addition & 33 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,12 @@ public function listen(ServerInterface $socket)
public function handleConnection(ConnectionInterface $conn)
{
$uriLocal = $conn->getLocalAddress();
if ($uriLocal !== null && strpos($uriLocal, '://') === false) {
// local URI known but does not contain a scheme. Should only happen for old Socket < 0.8
// try to detect transport encryption and assume default application scheme
$uriLocal = ($this->isConnectionEncrypted($conn) ? 'https://' : 'http://') . $uriLocal;
} elseif ($uriLocal !== null) {
if ($uriLocal !== null) {
// local URI known, so translate transport scheme to application scheme
$uriLocal = strtr($uriLocal, array('tcp://' => 'http://', 'tls://' => 'https://'));
}

$uriRemote = $conn->getRemoteAddress();
if ($uriRemote !== null && strpos($uriRemote, '://') === false) {
// local URI known but does not contain a scheme. Should only happen for old Socket < 0.8
// actual scheme is not evaluated but required for parsing URI
$uriRemote = 'unused://' . $uriRemote;
}

$that = $this;
$parser = new RequestHeaderParser($uriLocal, $uriRemote);
Expand Down Expand Up @@ -422,27 +413,4 @@ private function handleResponseBody(ResponseInterface $response, ConnectionInter
$connection->end();
}
}

/**
* @param ConnectionInterface $conn
* @return bool
* @codeCoverageIgnore
*/
private function isConnectionEncrypted(ConnectionInterface $conn)
{
// Legacy PHP < 7 does not offer any direct access to check crypto parameters
// We work around by accessing the context options and assume that only
// secure connections *SHOULD* set the "ssl" context options by default.
if (PHP_VERSION_ID < 70000) {
$context = isset($conn->stream) ? stream_context_get_options($conn->stream) : array();

return (isset($context['ssl']) && $context['ssl']);
}

// Modern PHP 7+ offers more reliable access to check crypto parameters
// by checking stream crypto meta data that is only then made available.
$meta = isset($conn->stream) ? stream_get_meta_data($conn->stream) : array();

return (isset($meta['crypto']) && $meta['crypto']);
}
}
53 changes: 46 additions & 7 deletions tests/FunctionalServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
use Clue\React\Block;
use React\Http\Response;
use React\Socket\SecureServer;
use React\Stream\ReadableStreamInterface;
use React\Promise\Promise;
use React\Promise\PromiseInterface;
use React\Promise\Stream;
use React\Stream\ThroughStream;

Expand Down Expand Up @@ -172,7 +170,7 @@ public function testSecureHttpsOnRandomPort()
));
$server->listen($socket);

$result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) {
$result = $connector->connect($socket->getAddress())->then(function (ConnectionInterface $conn) {
$conn->write("GET / HTTP/1.0\r\nHost: " . noScheme($conn->getRemoteAddress()) . "\r\n\r\n");

return Stream\buffer($conn);
Expand All @@ -186,6 +184,47 @@ public function testSecureHttpsOnRandomPort()
$socket->close();
}

public function testSecureHttpsReturnsData()
{
if (!function_exists('stream_socket_enable_crypto')) {
$this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
}

$loop = Factory::create();

$server = new Server(function (RequestInterface $request) {
return new Response(
200,
array(),
str_repeat('.', 33000)
);
});

$socket = new Socket(0, $loop);
$socket = new SecureServer($socket, $loop, array(
'local_cert' => __DIR__ . '/../examples/localhost.pem'
));
$server->listen($socket);

$connector = new Connector($loop, array(
'tls' => array('verify_peer' => false)
));

$result = $connector->connect($socket->getAddress())->then(function (ConnectionInterface $conn) {
$conn->write("GET / HTTP/1.0\r\nHost: " . noScheme($conn->getRemoteAddress()) . "\r\n\r\n");

return Stream\buffer($conn);
});

$response = Block\await($result, $loop, 1.0);

$this->assertContains("HTTP/1.0 200 OK", $response);
$this->assertContains("\r\nContent-Length: 33000\r\n", $response);
$this->assertStringEndsWith("\r\n". str_repeat('.', 33000), $response);

$socket->close();
}

public function testSecureHttpsOnRandomPortWithoutHostHeaderUsesSocketUri()
{
if (!function_exists('stream_socket_enable_crypto')) {
Expand All @@ -207,7 +246,7 @@ public function testSecureHttpsOnRandomPortWithoutHostHeaderUsesSocketUri()
));
$server->listen($socket);

$result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) {
$result = $connector->connect($socket->getAddress())->then(function (ConnectionInterface $conn) {
$conn->write("GET / HTTP/1.0\r\n\r\n");

return Stream\buffer($conn);
Expand Down Expand Up @@ -306,7 +345,7 @@ public function testSecureHttpsOnStandardPortReturnsUriWithNoPort()

$server->listen($socket);

$result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) {
$result = $connector->connect($socket->getAddress())->then(function (ConnectionInterface $conn) {
$conn->write("GET / HTTP/1.0\r\nHost: 127.0.0.1\r\n\r\n");

return Stream\buffer($conn);
Expand Down Expand Up @@ -345,7 +384,7 @@ public function testSecureHttpsOnStandardPortWithoutHostHeaderUsesSocketUri()

$server->listen($socket);

$result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) {
$result = $connector->connect($socket->getAddress())->then(function (ConnectionInterface $conn) {
$conn->write("GET / HTTP/1.0\r\n\r\n");

return Stream\buffer($conn);
Expand Down Expand Up @@ -414,7 +453,7 @@ public function testSecureHttpsOnHttpStandardPortReturnsUriWithPort()

$server->listen($socket);

$result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) {
$result = $connector->connect($socket->getAddress())->then(function (ConnectionInterface $conn) {
$conn->write("GET / HTTP/1.0\r\nHost: " . noScheme($conn->getRemoteAddress()) . "\r\n\r\n");

return Stream\buffer($conn);
Expand Down