diff --git a/composer.json b/composer.json index fccbc1bd..50e11f88 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "require": { "php": ">=5.3.0", "ringcentral/psr7": "^1.2", - "react/socket": "^0.7 || ^0.6 || ^0.5", + "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5", "react/stream": "^0.6 || ^0.5 || ^0.4.4", "react/promise": "^2.0 || ^1.1", "evenement/evenement": "^2.0 || ^1.0" @@ -18,7 +18,7 @@ }, "require-dev": { "phpunit/phpunit": "^4.8.10||^5.0", - "react/socket": "^0.7", + "react/socket": "^1.0 || ^0.8 || ^0.7", "clue/block-react": "^1.1" } } diff --git a/src/RequestHeaderParser.php b/src/RequestHeaderParser.php index d7fdc1d2..0b75ac01 100644 --- a/src/RequestHeaderParser.php +++ b/src/RequestHeaderParser.php @@ -181,6 +181,9 @@ private function parseRequest($data) // set URI components from socket address if not already filled via Host header if ($request->getUri()->getHost() === '') { $parts = parse_url($this->localSocketUri); + if (!isset($parts['host'], $parts['port'])) { + $parts = array('host' => '127.0.0.1', 'port' => 80); + } $request = $request->withUri( $request->getUri()->withScheme('http')->withHost($parts['host'])->withPort($parts['port']), diff --git a/src/Server.php b/src/Server.php index 6fa1b629..61d68953 100644 --- a/src/Server.php +++ b/src/Server.php @@ -111,11 +111,25 @@ public function __construct(SocketServerInterface $io, $callback) /** @internal */ 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) { + // 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( - ($this->isConnectionEncrypted($conn) ? 'https://' : 'http://') . $conn->getLocalAddress(), - 'tcp://' . $conn->getRemoteAddress() - ); + $parser = new RequestHeaderParser($uriLocal, $uriRemote); $listener = array($parser, 'feed'); $parser->on('headers', function (RequestInterface $request, $bodyBuffer) use ($conn, $listener, $parser, $that) { diff --git a/tests/FunctionalServerTest.php b/tests/FunctionalServerTest.php index f34f2bc3..4b807617 100644 --- a/tests/FunctionalServerTest.php +++ b/tests/FunctionalServerTest.php @@ -13,7 +13,7 @@ use React\Http\Response; use React\Socket\SecureServer; -class FunctionServerTest extends TestCase +class FunctionalServerTest extends TestCase { public function testPlainHttpOnRandomPort() { @@ -26,7 +26,7 @@ public function testPlainHttpOnRandomPort() }); $result = $connector->connect($socket->getAddress())->then(function (ConnectionInterface $conn) { - $conn->write("GET / HTTP/1.0\r\nHost: " . $conn->getRemoteAddress() . "\r\n\r\n"); + $conn->write("GET / HTTP/1.0\r\nHost: " . noScheme($conn->getRemoteAddress()) . "\r\n\r\n"); return BufferedSink::createPromise($conn); }); @@ -34,7 +34,7 @@ public function testPlainHttpOnRandomPort() $response = Block\await($result, $loop, 1.0); $this->assertContains("HTTP/1.0 200 OK", $response); - $this->assertContains('http://' . $socket->getAddress() . '/', $response); + $this->assertContains('http://' . noScheme($socket->getAddress()) . '/', $response); $socket->close(); } @@ -58,7 +58,7 @@ public function testPlainHttpOnRandomPortWithoutHostHeaderUsesSocketUri() $response = Block\await($result, $loop, 1.0); $this->assertContains("HTTP/1.0 200 OK", $response); - $this->assertContains('http://' . $socket->getAddress() . '/', $response); + $this->assertContains('http://' . noScheme($socket->getAddress()) . '/', $response); $socket->close(); } @@ -106,8 +106,8 @@ public function testSecureHttpsOnRandomPort() return new Response(200, array(), (string)$request->getUri()); }); - $result = $connector->connect('tls://' . $socket->getAddress())->then(function (ConnectionInterface $conn) { - $conn->write("GET / HTTP/1.0\r\nHost: " . $conn->getRemoteAddress() . "\r\n\r\n"); + $result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) { + $conn->write("GET / HTTP/1.0\r\nHost: " . noScheme($conn->getRemoteAddress()) . "\r\n\r\n"); return BufferedSink::createPromise($conn); }); @@ -115,7 +115,7 @@ public function testSecureHttpsOnRandomPort() $response = Block\await($result, $loop, 1.0); $this->assertContains("HTTP/1.0 200 OK", $response); - $this->assertContains('https://' . $socket->getAddress() . '/', $response); + $this->assertContains('https://' . noScheme($socket->getAddress()) . '/', $response); $socket->close(); } @@ -139,7 +139,7 @@ public function testSecureHttpsOnRandomPortWithoutHostHeaderUsesSocketUri() return new Response(200, array(), (string)$request->getUri()); }); - $result = $connector->connect('tls://' . $socket->getAddress())->then(function (ConnectionInterface $conn) { + $result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) { $conn->write("GET / HTTP/1.0\r\n\r\n"); return BufferedSink::createPromise($conn); @@ -148,7 +148,7 @@ public function testSecureHttpsOnRandomPortWithoutHostHeaderUsesSocketUri() $response = Block\await($result, $loop, 1.0); $this->assertContains("HTTP/1.0 200 OK", $response); - $this->assertContains('https://' . $socket->getAddress() . '/', $response); + $this->assertContains('https://' . noScheme($socket->getAddress()) . '/', $response); $socket->close(); } @@ -232,7 +232,7 @@ public function testSecureHttpsOnStandardPortReturnsUriWithNoPort() return new Response(200, array(), (string)$request->getUri()); }); - $result = $connector->connect('tls://' . $socket->getAddress())->then(function (ConnectionInterface $conn) { + $result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) { $conn->write("GET / HTTP/1.0\r\nHost: 127.0.0.1\r\n\r\n"); return BufferedSink::createPromise($conn); @@ -269,7 +269,7 @@ public function testSecureHttpsOnStandardPortWithoutHostHeaderUsesSocketUri() return new Response(200, array(), (string)$request->getUri()); }); - $result = $connector->connect('tls://' . $socket->getAddress())->then(function (ConnectionInterface $conn) { + $result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) { $conn->write("GET / HTTP/1.0\r\n\r\n"); return BufferedSink::createPromise($conn); @@ -298,7 +298,7 @@ public function testPlainHttpOnHttpsStandardPortReturnsUriWithPort() }); $result = $connector->connect($socket->getAddress())->then(function (ConnectionInterface $conn) { - $conn->write("GET / HTTP/1.0\r\nHost: " . $conn->getRemoteAddress() . "\r\n\r\n"); + $conn->write("GET / HTTP/1.0\r\nHost: " . noScheme($conn->getRemoteAddress()) . "\r\n\r\n"); return BufferedSink::createPromise($conn); }); @@ -334,8 +334,8 @@ public function testSecureHttpsOnHttpStandardPortReturnsUriWithPort() return new Response(200, array(), (string)$request->getUri() . 'x' . $request->getHeaderLine('Host')); }); - $result = $connector->connect('tls://' . $socket->getAddress())->then(function (ConnectionInterface $conn) { - $conn->write("GET / HTTP/1.0\r\nHost: " . $conn->getRemoteAddress() . "\r\n\r\n"); + $result = $connector->connect('tls://' . noScheme($socket->getAddress()))->then(function (ConnectionInterface $conn) { + $conn->write("GET / HTTP/1.0\r\nHost: " . noScheme($conn->getRemoteAddress()) . "\r\n\r\n"); return BufferedSink::createPromise($conn); }); @@ -348,3 +348,12 @@ public function testSecureHttpsOnHttpStandardPortReturnsUriWithPort() $socket->close(); } } + +function noScheme($uri) +{ + $pos = strpos($uri, '://'); + if ($pos !== false) { + $uri = substr($uri, $pos + 3); + } + return $uri; +}