From 9f2dd73b7aef5a9653f8162fd0a1c0ff9f552976 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Roland=20H=C3=A4der?= Date: Sun, 21 May 2017 14:39:43 +0200 Subject: [PATCH] Continued a bit: - translateSocketErrorCodeToName() is now private, no need to have it publicly available - added field socketProtocol - used StorableSocket (interface) as 'instanceof' test, not a class - sorted parameters in SocketContainer's factory method - sorted members MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Roland Häder --- .../socket/class_SocketContainer.php | 191 +++++++++++------- .../factories/socket/class_SocketFactory.php | 62 +++++- .../ipv4/class_BaseIpV4ConnectionHelper.php | 28 +-- .../ipv4/tcp/class_TcpConnectionHelper.php | 44 +--- .../registry/socket/class_SocketRegistry.php | 8 +- .../container/socket/class_StorableSocket.php | 16 +- 6 files changed, 189 insertions(+), 160 deletions(-) diff --git a/application/hub/classes/container/socket/class_SocketContainer.php b/application/hub/classes/container/socket/class_SocketContainer.php index d23b46f6e..1ae9936a2 100644 --- a/application/hub/classes/container/socket/class_SocketContainer.php +++ b/application/hub/classes/container/socket/class_SocketContainer.php @@ -46,6 +46,13 @@ use CoreFramework\Visitor\Visitor; * along with this program. If not, see . */ class SocketContainer extends BaseContainer implements StorableSocket, Visitable, Registerable { + /** + * Socket protocol: + * - 'tcp' for TCP/IPv4 connections + * - 'file' for Unix* file-based sockets + */ + private $socketProtocol = 'invalid'; + /** * Protected constructor * @@ -60,14 +67,18 @@ class SocketContainer extends BaseContainer implements StorableSocket, Visitable * Creates an instance of this Container class and prepares it for usage * * @param $socketResource A valid socket resource - * @param $infoInstance An instance of a ShareableInfo class + * @param $socketProtocol Socket protocol * @param $packageData Raw package data - * @return $socketInstance An instance of this Container class + * @param $infoInstance An instance of a ShareableInfo class + * @return $socketInstance An instance of this Container class */ - public static final function createSocketContainer ($socketResource, ShareableInfo $infoInstance = NULL, array $packageData = array()) { + public static final function createSocketContainer ($socketResource, $socketProtocol, array $packageData, ShareableInfo $infoInstance = NULL) { // Get a new instance $socketInstance = new SocketContainer(); + // Set protocol + $socketInstance->setSocketProtocol($socketProtocol); + // Remove unneeded entries unset($packageData[NetworkPackage::PACKAGE_DATA_CONTENT]); unset($packageData[NetworkPackage::PACKAGE_DATA_HASH]); @@ -275,6 +286,16 @@ class SocketContainer extends BaseContainer implements StorableSocket, Visitable return $result; } + /** + * Getter for socket procotol field + * + * @return $socketProtocol Socket protocol + */ + public function getSocketProtocol () { + // Return it + return $this->socketProtocol; + } + /** * Some "getter" for socket type of stored socket * @@ -519,81 +540,6 @@ class SocketContainer extends BaseContainer implements StorableSocket, Visitable $this->clearLastSocketError(); } - /** - * Translates socket error codes into our own internal names which can be - * used for call-backs. - * - * @param $errorCode The error code from socket_last_error() to be translated - * @return $errorName The translated name (all lower-case, with underlines) - */ - public function translateSocketErrorCodeToName ($errorCode) { - // Nothing bad happened by default - $errorName = StorableSocket::SOCKET_CONNECTED; - - // Is the code a number, then we have to change it - switch ($errorCode) { - case 0: // Silently ignored, the socket is connected - break; - - case 11: // "Resource temporary unavailable" - $errorName = StorableSocket::SOCKET_ERROR_RESOURCE_UNAVAILABLE; - break; - - case 13: // "Permission denied" - $errorName = StorableSocket::SOCKET_ERROR_PERMISSION_DENIED; - break; - - case 32: // "Broken pipe" - $errorName = StorableSocket::SOCKET_ERROR_BROKEN_PIPE; - break; - - case 95: // "Operation not supported" - $errorName = StorableSocket::SOCKET_ERROR_OPERATION_NOT_SUPPORTED; - break; - - case 104: // "Connection reset by peer" - $errorName = StorableSocket::SOCKET_ERROR_CONNECTION_RESET_BY_PEER; - break; - - case 107: // "Transport end-point not connected" - case 134: // On some (?) systems for 'transport end-point not connected' - // @TODO On some systems it is 134, on some 107? - $errorName = StorableSocket::SOCKET_ERROR_TRANSPORT_ENDPOINT; - break; - - case 110: // "Connection timed out" - $errorName = StorableSocket::SOCKET_ERROR_CONNECTION_TIMED_OUT; - break; - - case 111: // "Connection refused" - $errorName = StorableSocket::SOCKET_ERROR_CONNECTION_REFUSED; - break; - - case 113: // "No route to host" - $errorName = StorableSocket::SOCKET_ERROR_NO_ROUTE_TO_HOST; - break; - - case 114: // "Operation already in progress" - $errorName = StorableSocket::SOCKET_ERROR_OPERATION_ALREADY_PROGRESS; - break; - - case 115: // "Operation now in progress" - $errorName = StorableSocket::SOCKET_ERROR_OPERATION_IN_PROGRESS; - break; - - default: // Everything else <> 0 - // Unhandled error code detected, so first debug it because we may want to handle it like the others - self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf(strtoupper($this->getSocketProtocol()) . '-SOCKET: Unsupported errorCode=%d,message=%s', $errorCode, socket_strerror($errorCode))); - - // Change it only in this class - $errorName = StorableSocket::SOCKET_ERROR_UNKNOWN; - break; - } // END - switch - - // Return translated name - return $errorName; - } - /** * Constructs a callable method name from given socket error code. If the * method is not found, a generic one is used. @@ -808,4 +754,93 @@ class SocketContainer extends BaseContainer implements StorableSocket, Visitable self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CONNECTION-HELPER: Operation is now in progress, this is usual for non-blocking connections and is no bug.'); } + /************************************************************************ + * Private methods follow * + ************************************************************************/ + + /** + * Translates socket error codes into our own internal names which can be + * used for call-backs. + * + * @param $errorCode The error code from socket_last_error() to be translated + * @return $errorName The translated name (all lower-case, with underlines) + */ + private function translateSocketErrorCodeToName ($errorCode) { + // Nothing bad happened by default + $errorName = StorableSocket::SOCKET_CONNECTED; + + // Is the code a number, then we have to change it + switch ($errorCode) { + case 0: // Silently ignored, the socket is connected + break; + + case 11: // "Resource temporary unavailable" + $errorName = StorableSocket::SOCKET_ERROR_RESOURCE_UNAVAILABLE; + break; + + case 13: // "Permission denied" + $errorName = StorableSocket::SOCKET_ERROR_PERMISSION_DENIED; + break; + + case 32: // "Broken pipe" + $errorName = StorableSocket::SOCKET_ERROR_BROKEN_PIPE; + break; + + case 95: // "Operation not supported" + $errorName = StorableSocket::SOCKET_ERROR_OPERATION_NOT_SUPPORTED; + break; + + case 104: // "Connection reset by peer" + $errorName = StorableSocket::SOCKET_ERROR_CONNECTION_RESET_BY_PEER; + break; + + case 107: // "Transport end-point not connected" + case 134: // On some (?) systems for 'transport end-point not connected' + // @TODO On some systems it is 134, on some 107? + $errorName = StorableSocket::SOCKET_ERROR_TRANSPORT_ENDPOINT; + break; + + case 110: // "Connection timed out" + $errorName = StorableSocket::SOCKET_ERROR_CONNECTION_TIMED_OUT; + break; + + case 111: // "Connection refused" + $errorName = StorableSocket::SOCKET_ERROR_CONNECTION_REFUSED; + break; + + case 113: // "No route to host" + $errorName = StorableSocket::SOCKET_ERROR_NO_ROUTE_TO_HOST; + break; + + case 114: // "Operation already in progress" + $errorName = StorableSocket::SOCKET_ERROR_OPERATION_ALREADY_PROGRESS; + break; + + case 115: // "Operation now in progress" + $errorName = StorableSocket::SOCKET_ERROR_OPERATION_IN_PROGRESS; + break; + + default: // Everything else <> 0 + // Unhandled error code detected, so first debug it because we may want to handle it like the others + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf(strtoupper($this->getSocketProtocol()) . '-SOCKET: Unsupported errorCode=%d,message=%s', $errorCode, socket_strerror($errorCode))); + + // Change it only in this class + $errorName = StorableSocket::SOCKET_ERROR_UNKNOWN; + break; + } // END - switch + + // Return translated name + return $errorName; + } + + /** + * Setter for socket protocol field + * + * @param $socketProtocol Socket protocol + * @return void + */ + private function setSocketProtocol ($socketProtocol) { + $this->socketProtocol = $socketProtocol; + } + } diff --git a/application/hub/classes/factories/socket/class_SocketFactory.php b/application/hub/classes/factories/socket/class_SocketFactory.php index 54dd00e32..df5577c58 100644 --- a/application/hub/classes/factories/socket/class_SocketFactory.php +++ b/application/hub/classes/factories/socket/class_SocketFactory.php @@ -121,7 +121,8 @@ class SocketFactory extends ObjectFactory { $socketResource = socket_create(AF_UNIX, SOCK_STREAM, 0); // Get container from it - $socketInstance = self::createObjectByConfiguredName('socket_container_class', array($socketResource, NULL, $packageData)); + // @TODO Somehow handle $infoInstance to this factory + $socketInstance = self::createObjectByConfiguredName('socket_container_class', array($socketResource, 'file', $packageData, NULL)); // Debug message /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('SOCKET-FILE_LISTENER: socketInstance[]=%s', gettype($socketInstance))); @@ -180,4 +181,63 @@ class SocketFactory extends ObjectFactory { return $socketInstance; } + /** + * Creates a socket instance from given package data. The socket then + * can be used to make outgoing TCP/IPv4/6 connections. + * + * @param $packageData Valid package data + * @return $socketInstance An instance of a StorableSocket class + * @throws SocketCreationException If the socket cannot be created + * @todo Rewrite this to also handle IPv6 addresses and sockets + */ + public static final function createTcpOutgoingSocketFromPackageData (array $packageData) { + // Create a socket instance + $socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + + // Debug message + //*NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-FACTORY: Created socket ' . $socketResource . '(' . gettype($socketResource) . ') from class ' . $className . '.'); + + // Construct container class, this won't be reached if an exception is thrown + // @TODO Somehow handle $infoInstance to this factory + $socketInstance = ObjectFactory::createObjectByConfiguredName('socket_container_class', array($socketResource, 'tcp', $packageData, NULL)); + + // Is the socket resource valid? + if (!$socketInstance->isValidSocket()) { + /* + * Something bad happened, calling handleSocketError() is not + * possible here because that method would throw an + * InvalidSocketException back. + */ + throw new SocketCreationException(array($factoryInstance, $socketInstance->getSocketResource()), StorableSocket::EXCEPTION_SOCKET_CREATION_FAILED); + } // END - if + + // Get socket error code for verification + $socketError = $socketInstance->getLastSocketError(); + + // Check if there was an error else + if ($socketError > 0) { + // Handle this socket error with a faked recipientData array + $helperInstance->handleSocketError(__METHOD__, __LINE__, $socketInstance, array('0.0.0.0', '0')); + } // END - if + + // Set the option to reuse the port + if (!$socketInstance->enableReuseAddress()) { + // Handle this socket error with a faked recipientData array + $socketInstance->handleSocketError(__METHOD__, __LINE__, array('0.0.0.0', '0')); + } // END - if + + /* + * Set socket to non-blocking mode before trying to establish a link to + * it. This is now the default behaviour for all connection helpers who + * call initConnection(); . + */ + if (!$socketInstance->enableSocketNonBlocking()) { + // Handle this socket error with a faked recipientData array + $socketInstance->handleSocketError(__METHOD__, __LINE__, array('0.0.0.0', '0')); + } // END - if + + // Return it + return $socketInstance; + } + } diff --git a/application/hub/classes/helper/connection/ipv4/class_BaseIpV4ConnectionHelper.php b/application/hub/classes/helper/connection/ipv4/class_BaseIpV4ConnectionHelper.php index f906434f5..0af6dbc8a 100644 --- a/application/hub/classes/helper/connection/ipv4/class_BaseIpV4ConnectionHelper.php +++ b/application/hub/classes/helper/connection/ipv4/class_BaseIpV4ConnectionHelper.php @@ -66,32 +66,6 @@ class BaseIpV4ConnectionHelper extends BaseConnectionHelper { $this->connectionPort = $connectionPort; } - /** - * Initializes the current connection - * - * @return void - */ - protected function initConnection () { - // Set the option to reuse the port - if (!$this->getSocketInstance()->enableReuseAddress()) { - // Handle this socket error with a faked recipientData array - $this->getSocketInstance()->handleSocketError(__METHOD__, __LINE__, array('0.0.0.0', '0')); - } // END - if - - /* - * Set socket to non-blocking mode before trying to establish a link to - * it. This is now the default behaviour for all connection helpers who - * call initConnection(); . - */ - if (!$this->getSocketInstance()->enableSocketNonBlocking()) { - // Handle this socket error with a faked recipientData array - $this->getSocketInstance()->handleSocketError(__METHOD__, __LINE__, array('0.0.0.0', '0')); - } // END - if - - // Last step: mark connection as initialized - $this->setIsInitialized(TRUE); - } - /** * Attempts to connect to a peer by given IP number and port from an UNL * instance with currently configured timeout. @@ -102,7 +76,7 @@ class BaseIpV4ConnectionHelper extends BaseConnectionHelper { * @todo Rewrite the while() loop to a iterator to not let the software stay very long here */ protected function connectToPeerByUnlInstance (LocateableNode $unlInstance) { - // Only call this if the connection is initialized by initConnection() + // Only call this if the connection is fully initialized assert($this->isInitialized()); // Is the UNL data complete? diff --git a/application/hub/classes/helper/connection/ipv4/tcp/class_TcpConnectionHelper.php b/application/hub/classes/helper/connection/ipv4/tcp/class_TcpConnectionHelper.php index 212d97205..cd6bf3fd0 100644 --- a/application/hub/classes/helper/connection/ipv4/tcp/class_TcpConnectionHelper.php +++ b/application/hub/classes/helper/connection/ipv4/tcp/class_TcpConnectionHelper.php @@ -3,13 +3,13 @@ namespace Hub\Helper\Connection\Tcp; // Import application-specific stuff +use Hub\Factory\Socket\SocketFactory; use Hub\Helper\Connection\ConnectionHelper; use Hub\Locator\Node\LocateableNode; use Hub\Network\Package\NetworkPackage; use Hub\Tools\HubTools; // Import framework stuff -use CoreFramework\Factory\ObjectFactory; /** * A TCP connection helper class @@ -53,56 +53,18 @@ class TcpConnectionHelper extends BaseIpV4ConnectionHelper implements Connection * connect to the other node. * * @param $packageData Raw package data - * @return $socketResource Socket resource - * @throws SocketCreationException If the socket could not be created - * @throws SocketOptionException If a socket option could not be set - * @throws SocketConnectionException If a connection could not be opened - * @todo $errorCode/-Message are now in handleSocketError()'s call-back methods + * @return $socketInstance An instance of a StorableSocket class */ public static function createConnectionFromPackageData (array $packageData) { // Create an instance $helperInstance = new TcpConnectionHelper(); - // Create a socket instance - $socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); - - // Debug message - //*NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-FACTORY: Created socket ' . $socketResource . '(' . gettype($socketResource) . ') from class ' . $className . '.'); - // Construct container class, this won't be reached if an exception is thrown - $socketInstance = ObjectFactory::createObjectByConfiguredName('socket_container_class', array($socketResource, NULL, $packageData)); - - // Is the socket resource valid? - if (!$socketInstance->isValidSocket()) { - /* - * Something bad happened, calling handleSocketError() is not - * possible here because that method would throw an - * InvalidSocketException back. - */ - throw new SocketCreationException(array($helperInstance, gettype($socketResource)), StorableSocket::EXCEPTION_SOCKET_CREATION_FAILED); - } // END - if - - // Get socket error code for verification - $socketError = $socketInstance->getLastSocketError(); - - // Check if there was an error else - if ($socketError > 0) { - // Handle this socket error with a faked recipientData array - $helperInstance->handleSocketError(__METHOD__, __LINE__, $socketInstance, array('0.0.0.0', '0')); - - // Then throw again - throw new SocketCreationException(array($helperInstance, gettype($socketResource), $socketError, socket_strerror($socketError)), StorableSocket::EXCEPTION_SOCKET_CREATION_FAILED); - } // END - if - - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('HELPER: Setting socket instance ... (' . $socketInstance->__toString() . ')'); + $socketInstance = SocketFactory::createTcpOutgoingSocketFromPackageData($packageData)); // Set the resource $helperInstance->setSocketInstance($socketInstance); - // Init connection - $helperInstance->initConnection(); - // @TODO The whole resolving part should be moved out and made more configurable // Init recipient data $unlInstance = NULL; diff --git a/application/hub/classes/registry/socket/class_SocketRegistry.php b/application/hub/classes/registry/socket/class_SocketRegistry.php index c9a44967a..82ab76481 100644 --- a/application/hub/classes/registry/socket/class_SocketRegistry.php +++ b/application/hub/classes/registry/socket/class_SocketRegistry.php @@ -190,8 +190,8 @@ class SocketRegistry extends BaseRegistry implements Register, RegisterableSocke // Get the instance $registeredInstance = $registryInstance->getInstance($socketKey); - // Is it SocketContainer and same socket? - $isRegistered = (($registeredInstance instanceof SocketContainer) && ($registeredInstance->equals($socketInstance))); + // Is it StorableSocket and same socket? + $isRegistered = (($registeredInstance instanceof StorableSocket) && ($registeredInstance->equals($socketInstance))); // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY: Final result: isRegistered(' . $socketInstance->getSocketResource() . ')=' . intval($isRegistered)); @@ -301,8 +301,8 @@ class SocketRegistry extends BaseRegistry implements Register, RegisterableSocke // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY: key=' . $key . ',subKey=' . $subKey . ',containerInstance=' . $containerInstance->__toString()); - // Is this a SocketContainer instance and is the address the same? - if (($containerInstance instanceof SocketContainer) && ($containerInstance->ifAddressMatches($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]))) { + // Is this a StorableSocket instance and is the address the same? + if (($containerInstance instanceof StorableSocket) && ($containerInstance->ifAddressMatches($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]))) { // Debug die //* DEBUG-DIE: */ die(__METHOD__ . ': containerInstance=' . print_r($containerInstance, TRUE)); diff --git a/application/hub/interfaces/container/socket/class_StorableSocket.php b/application/hub/interfaces/container/socket/class_StorableSocket.php index 5b2b9ab46..45945c85a 100644 --- a/application/hub/interfaces/container/socket/class_StorableSocket.php +++ b/application/hub/interfaces/container/socket/class_StorableSocket.php @@ -89,6 +89,13 @@ interface StorableSocket extends FrameworkInterface { */ function getSocketType (); + /** + * Getter for socket procotol field + * + * @return $socketProtocol Socket protocol (e.g. 'tcp' for TCP/IPv4, 'file' for Uni* file sockets) + */ + function getSocketProtocol (); + /** * Checks whether the given Universal Node Locator matches with the one from package data * @@ -167,15 +174,6 @@ interface StorableSocket extends FrameworkInterface { */ function handleSocketError ($method, $line, array $socketData); - /** - * Translates socket error codes into our own internal names which can be - * used for call-backs. - * - * @param $errorCode The error code from socket_last_error() to be translated - * @return $errorName The translated name (all lower-case, with underlines) - */ - function translateSocketErrorCodeToName ($errorCode); - /** * Do the shutdown sequence for this connection helper * -- 2.39.5