From dd2ec6df0d03dbfc3a107603b3780178e5c1b5de Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Tue, 23 Jul 2019 21:11:07 +0200 Subject: [PATCH 1/6] Add reserved private constant for aborting requests for future use --- src/Sockets/Socket.php | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Sockets/Socket.php b/src/Sockets/Socket.php index 65a68c7..a798e12 100644 --- a/src/Sockets/Socket.php +++ b/src/Sockets/Socket.php @@ -59,31 +59,33 @@ final class Socket { - private const BEGIN_REQUEST = 1; + private const BEGIN_REQUEST = 1; - private const END_REQUEST = 3; + private const ABORT_REQUEST = 2; - private const PARAMS = 4; + private const END_REQUEST = 3; - private const STDIN = 5; + private const PARAMS = 4; - private const STDOUT = 6; + private const STDIN = 5; - private const STDERR = 7; + private const STDOUT = 6; - private const RESPONDER = 1; + private const STDERR = 7; - private const REQUEST_COMPLETE = 0; + private const RESPONDER = 1; - private const CANT_MPX_CONN = 1; + private const REQUEST_COMPLETE = 0; - private const OVERLOADED = 2; + private const CANT_MPX_CONN = 1; - private const UNKNOWN_ROLE = 3; + private const OVERLOADED = 2; - private const HEADER_LEN = 8; + private const UNKNOWN_ROLE = 3; - private const SOCK_STATE_INIT = 1; + private const HEADER_LEN = 8; + + private const SOCK_STATE_INIT = 1; private const SOCK_STATE_BUSY = 2; From 8dbcbec72f1d33eefc62f2be61ba3a27fb4b292b Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Tue, 23 Jul 2019 21:12:02 +0200 Subject: [PATCH 2/6] Add root NS import for function --- tests/Integration/NetworkSocketTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Integration/NetworkSocketTest.php b/tests/Integration/NetworkSocketTest.php index 1637093..3a9297e 100644 --- a/tests/Integration/NetworkSocketTest.php +++ b/tests/Integration/NetworkSocketTest.php @@ -43,6 +43,7 @@ use RuntimeException; use SebastianBergmann\RecursionContext\InvalidArgumentException; use Throwable; +use function http_build_query; final class NetworkSocketTest extends TestCase { From 7691ce982757986c18cd2b4767074e8dce0cfd57 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Tue, 23 Jul 2019 21:48:50 +0200 Subject: [PATCH 3/6] Add socket ID type incl. tests --- src/Sockets/SocketId.php | 69 ++++++++++++++++++ tests/Unit/Sockets/SocketIdTest.php | 106 ++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 src/Sockets/SocketId.php create mode 100644 tests/Unit/Sockets/SocketIdTest.php diff --git a/src/Sockets/SocketId.php b/src/Sockets/SocketId.php new file mode 100644 index 0000000..ae8045c --- /dev/null +++ b/src/Sockets/SocketId.php @@ -0,0 +1,69 @@ +guardValueIsValid( $id ); + + $this->id = $id; + } + + /** + * @param int $value + * + * @throws InvalidArgumentException + */ + private function guardValueIsValid( int $value ) : void + { + if ( $value < 1 || $value > ((1 << 16) - 1) ) + { + throw new InvalidArgumentException( 'Invalid socket ID (out of range): ' . $value ); + } + } + + /** + * @return SocketId + * @throws InvalidArgumentException + * @throws Exception + */ + public static function new() : self + { + return new self( random_int( 1, (1 << 16) - 1 ) ); + } + + /** + * @param int $id + * + * @return SocketId + * @throws InvalidArgumentException + */ + public static function fromInt( int $id ) : self + { + return new self( $id ); + } + + public function getValue() : int + { + return $this->id; + } + + public function equals( SocketId $other ) : bool + { + return $this->id === $other->id; + } +} \ No newline at end of file diff --git a/tests/Unit/Sockets/SocketIdTest.php b/tests/Unit/Sockets/SocketIdTest.php new file mode 100644 index 0000000..0ab804c --- /dev/null +++ b/tests/Unit/Sockets/SocketIdTest.php @@ -0,0 +1,106 @@ +assertGreaterThanOrEqual( 1, $socketId->getValue() ); + $this->assertLessThanOrEqual( (1 << 16) - 1, $socketId->getValue() ); + } + } + + /** + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws \InvalidArgumentException + */ + public function testGetValue() : void + { + $socketId = SocketId::new(); + + $this->assertGreaterThanOrEqual( 1, $socketId->getValue() ); + $this->assertLessThanOrEqual( (1 << 16) - 1, $socketId->getValue() ); + } + + /** + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws \InvalidArgumentException + */ + public function testCanGetNewInstanceFromInt() : void + { + for ( $i = 1; $i < 10; $i++ ) + { + $socketId = SocketId::fromInt( $i ); + + $this->assertSame( $i, $socketId->getValue() ); + } + } + + /** + * @param int $socketIdValue + * + * @throws \InvalidArgumentException + * @dataProvider outOfRangeSocketIdValueProvider + */ + public function testThrowsExceptionIfSocketIdValueIsOutOfRange( int $socketIdValue ) : void + { + $this->expectException( \InvalidArgumentException::class ); + $this->expectExceptionMessage( 'Invalid socket ID (out of range): ' . $socketIdValue ); + + SocketId::fromInt( $socketIdValue ); + } + + public function outOfRangeSocketIdValueProvider() : array + { + return [ + [ + 'socketIdValue' => 0, + ], + [ + 'socketIdValue' => 1 << 16, + ], + ]; + } + + /** + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @throws \InvalidArgumentException + */ + public function testEquals() : void + { + $socketId = SocketId::fromInt( 123 ); + $otherEquals = SocketId::fromInt( 123 ); + $otherEqualsNot = SocketId::fromInt( 321 ); + + $this->assertNotSame( $socketId, $otherEquals ); + $this->assertNotSame( $socketId, $otherEqualsNot ); + $this->assertNotSame( $otherEquals, $otherEqualsNot ); + + $this->assertTrue( $socketId->equals( $otherEquals ) ); + $this->assertTrue( $otherEquals->equals( $socketId ) ); + + $this->assertFalse( $socketId->equals( $otherEqualsNot ) ); + $this->assertFalse( $otherEqualsNot->equals( $socketId ) ); + + $this->assertFalse( $otherEquals->equals( $otherEqualsNot ) ); + $this->assertFalse( $otherEqualsNot->equals( $otherEquals ) ); + } +} From af8e0665c23e3c4c9968f82109027af5a2403596 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Tue, 23 Jul 2019 21:53:12 +0200 Subject: [PATCH 4/6] Inject socket ID to socket via constructor Let the socket collection create a new socket ID and instantiate the socket class with it instead of creating it internally on creation. This is a preparation to replace the socket ID as the actual request ID for the fastCGI packages with a dedicated request ID for each request. This change is needed to allow multiplexing requests. (Multiple requests on one stream) --- src/Sockets/Socket.php | 55 +++++++++++++++++-------------- src/Sockets/SocketCollection.php | 13 +++++--- tests/Unit/Sockets/SocketTest.php | 5 ++- 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/Sockets/Socket.php b/src/Sockets/Socket.php index a798e12..2faf884 100644 --- a/src/Sockets/Socket.php +++ b/src/Sockets/Socket.php @@ -46,7 +46,6 @@ use function is_resource; use function microtime; use function ord; -use function random_int; use function str_repeat; use function stream_get_meta_data; use function stream_select; @@ -59,33 +58,33 @@ final class Socket { - private const BEGIN_REQUEST = 1; + private const BEGIN_REQUEST = 1; - private const ABORT_REQUEST = 2; + private const ABORT_REQUEST = 2; - private const END_REQUEST = 3; + private const END_REQUEST = 3; - private const PARAMS = 4; + private const PARAMS = 4; - private const STDIN = 5; + private const STDIN = 5; - private const STDOUT = 6; + private const STDOUT = 6; - private const STDERR = 7; + private const STDERR = 7; - private const RESPONDER = 1; + private const RESPONDER = 1; - private const REQUEST_COMPLETE = 0; + private const REQUEST_COMPLETE = 0; - private const CANT_MPX_CONN = 1; + private const CANT_MPX_CONN = 1; - private const OVERLOADED = 2; + private const OVERLOADED = 2; - private const UNKNOWN_ROLE = 3; + private const UNKNOWN_ROLE = 3; - private const HEADER_LEN = 8; + private const HEADER_LEN = 8; - private const SOCK_STATE_INIT = 1; + private const SOCK_STATE_INIT = 1; private const SOCK_STATE_BUSY = 2; @@ -95,7 +94,7 @@ final class Socket public const STREAM_SELECT_USEC = 200000; - /** @var int */ + /** @var SocketId */ private $id; /** @var ConfiguresSocketConnection */ @@ -129,6 +128,7 @@ final class Socket private $status; /** + * @param SocketId $socketId * @param ConfiguresSocketConnection $connection * @param EncodesPacket $packetEncoder * @param EncodesNameValuePair $nameValuePairEncoder @@ -136,12 +136,13 @@ final class Socket * @throws Exception */ public function __construct( + SocketId $socketId, ConfiguresSocketConnection $connection, EncodesPacket $packetEncoder, EncodesNameValuePair $nameValuePairEncoder ) { - $this->id = random_int( 1, (1 << 16) - 1 ); + $this->id = $socketId; $this->connection = $connection; $this->packetEncoder = $packetEncoder; $this->nameValuePairEncoder = $nameValuePairEncoder; @@ -153,7 +154,7 @@ public function __construct( public function getId() : int { - return $this->id; + return $this->id->getValue(); } public function usesConnection( ConfiguresSocketConnection $connection ) : bool @@ -333,17 +334,21 @@ private function getRequestPackets( ProvidesRequestData $request ) : string $requestPackets = $this->packetEncoder->encodePacket( self::BEGIN_REQUEST, chr( 0 ) . chr( self::RESPONDER ) . chr( 1 ) . str_repeat( chr( 0 ), 5 ), - $this->id + $this->id->getValue() ); $paramsRequest = $this->nameValuePairEncoder->encodePairs( $request->getParams() ); if ( $paramsRequest ) { - $requestPackets .= $this->packetEncoder->encodePacket( self::PARAMS, $paramsRequest, $this->id ); + $requestPackets .= $this->packetEncoder->encodePacket( + self::PARAMS, + $paramsRequest, + $this->id->getValue() + ); } - $requestPackets .= $this->packetEncoder->encodePacket( self::PARAMS, '', $this->id ); + $requestPackets .= $this->packetEncoder->encodePacket( self::PARAMS, '', $this->id->getValue() ); if ( $request->getContent() ) { @@ -357,14 +362,14 @@ private function getRequestPackets( ProvidesRequestData $request ) : string $offset, self::REQ_MAX_CONTENT_SIZE ), - $this->id + $this->id->getValue() ); $offset += self::REQ_MAX_CONTENT_SIZE; } while ( $offset < $request->getContentLength() ); } - $requestPackets .= $this->packetEncoder->encodePacket( self::STDIN, '', $this->id ); + $requestPackets .= $this->packetEncoder->encodePacket( self::STDIN, '', $this->id->getValue() ); return $requestPackets; } @@ -431,7 +436,7 @@ public function fetchResponse( ?int $timeoutMs = null ) : ProvidesResponseData continue; } - if ( self::END_REQUEST === $packetType && $packet['requestId'] === $this->id ) + if ( self::END_REQUEST === $packetType && $packet['requestId'] === $this->id->getValue() ) { break; } @@ -579,7 +584,7 @@ public function collectResource( array &$resources ) : void { if ( null !== $this->resource ) { - $resources[ (string)$this->id ] = $this->resource; + $resources[ (string)$this->id->getValue() ] = $this->resource; } } } diff --git a/src/Sockets/SocketCollection.php b/src/Sockets/SocketCollection.php index 50b305b..6442ac8 100644 --- a/src/Sockets/SocketCollection.php +++ b/src/Sockets/SocketCollection.php @@ -34,16 +34,21 @@ public function new( { for ( $i = 0; $i < 10; $i++ ) { - $socket = new Socket( $connection, $packetEncoder, $nameValuePairEncoder ); + $socketId = SocketId::new(); - if ( $this->exists( $socket->getId() ) ) + if ( $this->exists( $socketId->getValue() ) ) { continue; } - $this->sockets[ $socket->getId() ] = $socket; + $this->sockets[ $socketId->getValue() ] = new Socket( + $socketId, + $connection, + $packetEncoder, + $nameValuePairEncoder + ); - return $socket; + return $this->sockets[ $socketId->getValue() ]; } throw new WriteFailedException( 'Could not allocate a new socket ID' ); diff --git a/tests/Unit/Sockets/SocketTest.php b/tests/Unit/Sockets/SocketTest.php index 306c658..f39bf62 100644 --- a/tests/Unit/Sockets/SocketTest.php +++ b/tests/Unit/Sockets/SocketTest.php @@ -36,6 +36,7 @@ use hollodotme\FastCGI\SocketConnections\NetworkSocket; use hollodotme\FastCGI\SocketConnections\UnixDomainSocket; use hollodotme\FastCGI\Sockets\Socket; +use hollodotme\FastCGI\Sockets\SocketId; use hollodotme\FastCGI\Tests\Traits\SocketDataProviding; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\ExpectationFailedException; @@ -83,7 +84,7 @@ private function getSocket( $readWriteTimeout ); - return new Socket( $connection, $packetEncoder, $nameValuePairEncoder ); + return new Socket( SocketId::new(), $connection, $packetEncoder, $nameValuePairEncoder ); } /** @@ -352,12 +353,14 @@ public function testCanCheckIfSocketUsesConnection() : void $nameValuePairEncoder = new NameValuePairEncoder(); $unixDomainSocket = new Socket( + SocketId::new(), $unixDomainConnection, $packetEncoder, $nameValuePairEncoder ); $networkSocket = new Socket( + SocketId::new(), $networkConnection, $packetEncoder, $nameValuePairEncoder From a6677a5ff9e11c9cde455f2187dbce22559f5ccd Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Mon, 29 Jul 2019 10:34:08 +0200 Subject: [PATCH 5/6] Update documentation --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e66f808..64b8367 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Please see the following links for earlier releases: * PHP >= 7.0 (EOL) [v1.0.0], [v1.0.1], [v1.1.0], [v1.2.0], [v1.3.0], [v1.4.0], [v1.4.1], [v1.4.2] * PHP >= 7.1 [v2.0.0], [v2.0.1], [v2.1.0], [v2.2.0], [v2.3.0], [v2.4.0], [v2.4.1], [v2.4.2], [v2.4.3], [v2.5.0], [v2.6.0], [v2.7.0], [v2.7.1], - [v2.7.2], [v3.0.0-alpha] + [v2.7.2], [v3.0.0-alpha], [v3.0.0-beta] Read more about the journey to and changes in `v2.6.0` in [this blog post](https://hollo.me/php/background-info-fast-cgi-client-v2.6.0.html). @@ -777,6 +777,7 @@ Run a call through a Unix Domain Socket This shows the response of the php-fpm status page. +[v3.0.0-beta]: https://github.com/hollodotme/fast-cgi-client/blob/v3.0.0-beta/README.md [v3.0.0-alpha]: https://github.com/hollodotme/fast-cgi-client/blob/v3.0.0-alpha/README.md [v2.7.2]: https://github.com/hollodotme/fast-cgi-client/blob/v2.7.2/README.md [v2.7.1]: https://github.com/hollodotme/fast-cgi-client/blob/v2.7.1/README.md From f2b0403ae5e58242384bcb6314dfd3cd5b88a550 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Mon, 29 Jul 2019 10:34:20 +0200 Subject: [PATCH 6/6] Update changelog, bumping v3.0.0 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdecdb7..c9db770 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a CHANGELOG](http://keepachangelog.com). +## [3.0.0] - 2019-07-29 + +**Please take notice of the backwards incompatible changes (BC breaks) documented below +in the changelog of [3.0.0-alpha](#300-alpha---2019-04-30) & [3.0.0-beta](#300-beta---2019-06-24).** + +### Added + +* Reserved private constant for ABORT_REQUEST instruction for future use +* Socket ID is now represented and generated by a proper type class + +### Improved + +* Import of root namespace functions +* Dependency injection for socket implementation + ## [3.0.0-beta] - 2019-06-24 ### Backwards incompatible changes (BC breaks) @@ -276,6 +291,7 @@ Based on [Pierrick Charron](https://github.com/adoy)'s [PHP-FastCGI-Client](http * Getters/Setters for connect timeout, read/write timeout, keep alive, socket persistence from `Client` (now part of the socket connection) * Method `Client->getValues()` +[3.0.0]: https://github.com/hollodotme/fast-cgi-client/compare/v3.0.0-beta...v3.0.0 [3.0.0-beta]: https://github.com/hollodotme/fast-cgi-client/compare/v3.0.0-alpha...v3.0.0-beta [3.0.0-alpha]: https://github.com/hollodotme/fast-cgi-client/compare/v2.7.2...v3.0.0-alpha [2.7.2]: https://github.com/hollodotme/fast-cgi-client/compare/v2.7.1...v2.7.2