/** * 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)); } }
/** * 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); }
/** * Fire timeout event and processes user response * * @param RequestDescriptor $descriptor * * @return bool True if we may do one more attempt, false otherwise */ public function handleTimeoutOnDescriptor(RequestDescriptor $descriptor) { $meta = $descriptor->getMetadata(); $event = new TimeoutEvent($this->executor, $descriptor->getSocket(), $meta[RequestExecutorInterface::META_USER_CONTEXT], $meta[RequestExecutorInterface::META_CONNECTION_FINISH_TIME] !== null && !$descriptor->getSocket() instanceof ServerSocket ? TimeoutEvent::DURING_IO : TimeoutEvent::DURING_CONNECTION); try { $this->callSocketSubscribers($descriptor, $event); $result = $event->isNextAttemptEnabled(); } catch (SocketException $e) { $this->callExceptionSubscribers($descriptor, $e); $result = false; } if ($result) { $this->updateMetadataForAttempt($descriptor, $event->when()); } return $result; }
/** * Decide how to process given operation * * @param RequestDescriptor $requestDescriptor Operation to decide * @param int $totalItems Total amount of pending requestDescriptors * * @return int One of LimitationSolverInterface::DECISION_* consts */ private function decide(RequestDescriptor $requestDescriptor, $totalItems) { $meta = $requestDescriptor->getMetadata(); if ($requestDescriptor->isRunning()) { return LimitationSolverInterface::DECISION_SKIP_CURRENT; } $isSkippingThis = $meta[RequestExecutorInterface::META_CONNECTION_START_TIME] !== null; if ($isSkippingThis) { return LimitationSolverInterface::DECISION_SKIP_CURRENT; } $decision = $this->decider->decide($this->executor, $requestDescriptor->getSocket(), $totalItems); if ($decision !== LimitationSolverInterface::DECISION_OK) { return $decision; } return LimitationSolverInterface::DECISION_OK; }
/** * 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(); }
/** * 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; }
/** * 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; }
/** * Check if this socket can be a zombie * * @param RequestDescriptor $descriptor Descriptor object * * @return bool */ private function isZombieCandidate(RequestDescriptor $descriptor) { $metadata = $descriptor->getMetadata(); if ($metadata[RequestExecutorInterface::META_REQUEST_COMPLETE]) { return false; } $operation = $descriptor->getOperation(); return $operation instanceof NullOperation || $operation instanceof ReadOperation && $operation->getFramePicker() instanceof EmptyFramePicker; }
/** * 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); }