/** {@inheritdoc} */ protected function handleOperation(RequestDescriptor $descriptor, RequestExecutorInterface $executor, EventHandlerInterface $eventHandler) { $operation = $descriptor->getOperation(); $socket = $descriptor->getSocket(); $meta = $executor->socketBag()->getSocketMetaData($socket); $context = $meta[RequestExecutorInterface::META_USER_CONTEXT]; try { /** @var ReadOperation $operation */ $response = $socket->read($operation->getFramePicker()); switch (true) { case $response instanceof PartialFrame: return $operation; case $response instanceof AcceptedFrame: $event = new AcceptEvent($executor, $socket, $context, $response->getClientSocket(), $response->getRemoteAddress()); $eventHandler->invokeEvent($event); return new ReadOperation(); default: $event = new ReadEvent($executor, $socket, $context, $response, false); $eventHandler->invokeEvent($event); return $event->getNextOperation(); } } catch (AcceptException $e) { return new ReadOperation(); } }
/** * Set start or finish time in metadata of the socket * * @param RequestDescriptor $requestDescriptor Socket meta data * @param string $key Metadata key to set * * @return void * @throws \InvalidArgumentException */ protected function setSocketOperationTime(RequestDescriptor $requestDescriptor, $key) { $meta = $requestDescriptor->getMetadata(); $table = [RequestExecutorInterface::META_CONNECTION_START_TIME => $meta[RequestExecutorInterface::META_CONNECTION_START_TIME] === null, RequestExecutorInterface::META_CONNECTION_FINISH_TIME => $meta[RequestExecutorInterface::META_CONNECTION_FINISH_TIME] === null, RequestExecutorInterface::META_LAST_IO_START_TIME => $meta[RequestExecutorInterface::META_CONNECTION_FINISH_TIME] !== null]; if (isset($table[$key]) && $table[$key]) { $requestDescriptor->setMetadata($key, microtime(true)); } }
/** * Check whether socket waiting is finished * * @param RequestDescriptor $descriptor Request descriptor to test * * @return bool True if delay is complete, false otherwise */ private function checkDelayIsFinished(RequestDescriptor $descriptor) { /** @var DelayedOperation $socketOperation */ $socketOperation = $descriptor->getOperation(); $arguments = $socketOperation->getArguments(); array_unshift($arguments, $descriptor->getSocket(), $this->executor); return !call_user_func_array($socketOperation->getCallable(), $arguments); }
/** * Update data inside descriptor to make one more attempt * * @param RequestDescriptor $descriptor Operation descriptor * @param string $when When Timeout occurerd, one of TimeoutEvent::DURING_* consts * * @return void */ private function updateMetadataForAttempt(RequestDescriptor $descriptor, $when) { switch ($when) { case TimeoutEvent::DURING_IO: $descriptor->setMetadata(RequestExecutorInterface::META_LAST_IO_START_TIME, null); break; case TimeoutEvent::DURING_CONNECTION: $descriptor->setRunning(false); $descriptor->setMetadata([RequestExecutorInterface::META_LAST_IO_START_TIME => null, RequestExecutorInterface::META_CONNECTION_START_TIME => null, RequestExecutorInterface::META_CONNECTION_FINISH_TIME => null]); break; } }
/** * Handle OOB data * * @param RequestDescriptor $descriptor Request descriptor * @param RequestExecutorInterface $executor Executor, processing operation * @param EventHandlerInterface $eventHandler Event handler for this operation * * @return OperationInterface|null Operation to return to user or null to continue normal processing */ private function handleOobData(RequestDescriptor $descriptor, RequestExecutorInterface $executor, EventHandlerInterface $eventHandler) { if (!$descriptor->hasState(RequestDescriptor::RDS_OOB)) { return null; } $descriptor->clearState(RequestDescriptor::RDS_OOB); $picker = new RawFramePicker(); $socket = $descriptor->getSocket(); $meta = $descriptor->getMetadata(); $frame = $socket->read($picker, true); $event = new ReadEvent($executor, $socket, $meta[RequestExecutorInterface::META_USER_CONTEXT], $frame, true); $eventHandler->invokeEvent($event); return $event->getNextOperation(); }
/** {@inheritdoc} */ protected function handleOperation(RequestDescriptor $descriptor, RequestExecutorInterface $executor, EventHandlerInterface $eventHandler) { $operation = $descriptor->getOperation(); $socket = $descriptor->getSocket(); /** @var SslHandshakeOperation $operation */ $resource = $descriptor->getSocket()->getStreamResource(); $result = stream_socket_enable_crypto($resource, true, $operation->getCipher()); if ($result === true) { return $operation->getNextOperation(); } elseif ($result === false) { throw new SslHandshakeException($socket, 'SSL handshake failed.'); } return $operation; }
/** * Disconnects given socket descriptor * * @param RequestDescriptor $descriptor Socket descriptor * * @return void */ public function disconnect(RequestDescriptor $descriptor) { $meta = $descriptor->getMetadata(); $socket = $descriptor->getSocket(); $descriptor->setMetadata(RequestExecutorInterface::META_REQUEST_COMPLETE, true); try { $socket->close(); if ($meta[RequestExecutorInterface::META_CONNECTION_FINISH_TIME] !== null) { $this->callSocketSubscribers($descriptor, $this->createEvent($descriptor, EventType::DISCONNECTED)); } } catch (SocketException $e) { $this->callExceptionSubscribers($descriptor, $e); } $this->callSocketSubscribers($descriptor, $this->createEvent($descriptor, EventType::FINALIZE)); $this->removeOperationsFromSelector($descriptor); }
/** {@inheritdoc} */ protected function handleOperation(RequestDescriptor $descriptor, RequestExecutorInterface $executor, EventHandlerInterface $eventHandler) { $operation = $descriptor->getOperation(); $socket = $descriptor->getSocket(); /** @var WriteOperation $operation */ $fireEvent = !$operation instanceof InProgressWriteOperation; if ($fireEvent) { $meta = $executor->socketBag()->getSocketMetaData($socket); $event = new WriteEvent($operation, $executor, $socket, $meta[RequestExecutorInterface::META_USER_CONTEXT]); $eventHandler->invokeEvent($event); $nextOperation = $event->getNextOperation(); } else { $nextOperation = $operation; } return $this->writeDataToSocket($operation, $socket, $nextOperation); }
/** * Resolves timeout for setting up event * * @param RequestDescriptor $descriptor Descriptor object * * @return int */ private function resolveTimeout(RequestDescriptor $descriptor) { $meta = $descriptor->getMetadata(); $key = spl_object_hash($descriptor); $result = $meta[RequestExecutorInterface::META_IO_TIMEOUT]; if (!isset($this->connectedDescriptors[$key])) { $result = $meta[RequestExecutorInterface::META_CONNECTION_TIMEOUT]; $this->connectedDescriptors[$key] = true; } return $result; }
/** * Start connecting process * * @param RequestDescriptor $descriptor Socket operation data * * @return bool True if successfully connected, false otherwise */ private function connectSocket(RequestDescriptor $descriptor) { $descriptor->initialize(); $socket = $descriptor->getSocket(); $event = $this->createEvent($descriptor, EventType::INITIALIZE); try { $this->callSocketSubscribers($descriptor, $event); $this->setSocketOperationTime($descriptor, RequestExecutorInterface::META_CONNECTION_START_TIME); $meta = $descriptor->getMetadata(); $socket->open($meta[RequestExecutorInterface::META_ADDRESS], $this->getStreamContextFromMetaData($meta)); $descriptor->setRunning(true); $result = true; } catch (SocketException $e) { $descriptor->setMetadata(RequestExecutorInterface::META_REQUEST_COMPLETE, true); $this->callExceptionSubscribers($descriptor, $e); $result = false; } return $result; }
/** * Notify handlers about exception * * @param RequestDescriptor $requestDescriptor Socket operation object * @param SocketException $exception Thrown exception * * @return void */ public function callExceptionSubscribers(RequestDescriptor $requestDescriptor, SocketException $exception) { if ($exception instanceof StopSocketOperationException) { return; } $meta = $requestDescriptor->getMetadata(); $exceptionEvent = new SocketExceptionEvent($exception, $this->executor, $requestDescriptor->getSocket(), $meta[RequestExecutorInterface::META_USER_CONTEXT]); $this->callSocketSubscribers($requestDescriptor, $exceptionEvent); }
/** * Set connection finish time and fire socket if it was not connected * * @param RequestDescriptor $requestDescriptor * * @return bool True, if there was no error, false if operation should be stopped */ private function setConnectionFinishTime(RequestDescriptor $requestDescriptor) { $meta = $requestDescriptor->getMetadata(); $wasConnected = $meta[RequestExecutorInterface::META_CONNECTION_FINISH_TIME] !== null; $this->setSocketOperationTime($requestDescriptor, RequestExecutorInterface::META_CONNECTION_FINISH_TIME); if (!$wasConnected) { $event = $this->createEvent($requestDescriptor, EventType::CONNECTED); try { $this->callSocketSubscribers($requestDescriptor, $event); } catch (SocketException $e) { $this->callExceptionSubscribers($requestDescriptor, $e); return false; } } return true; }
/** * Notify client about unhandled data in socket * * @param RequestDescriptor $descriptor Socket operation descriptor * @param int $attempt Current attempt number from 1 * @param int $totalAttempts Total attempts * * @return void */ private function notifyDataAlert(RequestDescriptor $descriptor, $attempt, $totalAttempts) { $socket = $descriptor->getSocket(); $meta = $this->executor->socketBag()->getSocketMetaData($socket); $event = new DataAlertEvent($this->executor, $socket, $meta[RequestExecutorInterface::META_USER_CONTEXT], $attempt, $totalAttempts); $this->callSocketSubscribers($descriptor, $event); $operation = $event->getNextOperation(); if ($operation) { $descriptor->setOperation($operation); } }
/** * Check whether given descriptor is active * * @param RequestDescriptor $descriptor * * @return bool */ private function isDescriptorActive(RequestDescriptor $descriptor) { $meta = $descriptor->getMetadata(); return !$meta[RequestExecutorInterface::META_REQUEST_COMPLETE] && $descriptor->isRunning() && !$descriptor->isPostponed(); }
/** * Create simple event * * @param RequestDescriptor $operation Operation item * @param string $eventName Event name for object * * @return Event */ protected function createEvent(RequestDescriptor $operation, $eventName) { $meta = $operation->getMetadata(); return new Event($this->executor, $operation->getSocket(), $meta[RequestExecutorInterface::META_USER_CONTEXT], $eventName); }
/** {@inheritdoc} */ public function isSatisfiedBy(RequestDescriptor $requestDescriptor) { return $requestDescriptor->getSocket() instanceof UdpClientSocket && $requestDescriptor->getOperation() instanceof ReadOperation; }