public function __construct($source, $destination, bool $closeSource = true, int $length = null, int $chunkSize = 4096, callable $transform = null) { $this->transform = $transform; if ($source instanceof ReadableStream) { $channel = $source->channel($chunkSize, $length); } elseif (!\is_resource($source)) { throw new \InvalidArgumentException(\sprintf('Expecting streamable source, given %s', \is_object($source) ? \get_class($source) : \gettype($source))); } else { $channel = $this->createSocketChannel($source, $chunkSize, $length); } if ($destination instanceof WritableStream) { $writer = $this->streamWriter($destination, $channel, $transform); } elseif (!\is_resource($destination)) { $channel->close(); throw new \InvalidArgumentException(\sprintf('Expecting streamable destination, given %s', \is_object($destination) ? \get_class($destination) : \gettype($destination))); } else { $writer = $this->socketWriter($destination, $channel, $transform); } $this->copy = new Coroutine($writer); if ($closeSource) { $this->copy->when(function () use($source) { if (\is_resource($source)) { Socket::shutdown($source); } else { $source->close(); } }); } $this->resolve($this->copy); }
protected function connectToAddress(string $address, float $timeout, bool $encrypt) { $url = \sprintf('%s://%s', $this->protocol, $address); $errno = null; $errstr = null; $context = \stream_context_create(\array_replace_recursive($this->options, ['socket' => ['connect' => $address]])); $socket = @\stream_socket_client($url, $errno, $errstr, $timeout ?: null, \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT, $context); if (!\is_resource($socket)) { throw new SocketException(\sprintf('Could not connect to "%s" (%s): [%s] %s', $url, $this->peer, $errno, \trim($errstr))); } try { \stream_set_blocking($socket, false); \stream_set_read_buffer($socket, 0); \stream_set_write_buffer($socket, 0); if ($this->tcpNoDelay) { Socket::setTcpNoDelay($socket, true); } if ($encrypt) { yield from $this->encryptSocketClient($socket, $timeout); } else { (yield new AwaitWrite($socket, $timeout)); } return $socket; } catch (\Throwable $e) { Socket::shutdown($socket); throw $e; } }
/** * {@inheritdoc} */ public function close() { if ($this->socket !== null) { try { $this->reader->dispose(); Socket::shutdown($this->socket); } finally { $this->socket = null; } } }
protected function runServer($socket, callable $server) : \Generator { try { $result = $server($socket); if ($result instanceof \Generator) { $result = (yield from $result); } return $result; } finally { Socket::shutdown($socket); } }
protected function socketReader($socket) : \Generator { $reader = new SocketReader($socket); $remaining = $this->length ?? \PHP_INT_MAX; $contents = ''; try { while ($remaining && null !== ($chunk = (yield $reader->read($remaining)))) { $contents .= $chunk; $remaining -= \strlen($chunk); } } finally { $reader->dispose(); if ($this->closeSource) { Socket::shutdown($socket); } } return $contents; }
/** * {@inheritdoc} */ public function close() : Awaitable { if ($this->socket !== null) { $this->disposeWriter(); $this->disposeReader(); try { Socket::shutdown($this->socket); } finally { $this->socket = null; } } return new Success($this->readBuffer); }