/** * Handle process on close transport * * @param \React\Socket\Connection $conn */ public function handleClose(Connection $conn) { Logger::debug($this, "Raw socket closed " . $conn->getRemoteAddress()); $session = $this->sessions[$conn]; $this->sessions->detach($conn); $this->router->getEventDispatcher()->dispatch('connection_close', new ConnectionCloseEvent($session)); }
/** * Handles incoming connections from $this->port. Basically redirects to a slave. * * @param Connection $incoming incoming connection from react */ public function onWeb(Connection $incoming) { //preload sent data from $incoming to $buffer, otherwise it would be lost, //since getNextSlave is async. $redirectionActive = false; $connectionOpen = true; $incomingBuffer = ''; $incoming->on('data', function ($data) use(&$redirectionActive, &$incomingBuffer) { if (!$redirectionActive) { $incomingBuffer .= $data; } }); $redirectionTries = 0; $incoming->on('close', function () use(&$redirectionActive, &$redirectionTries, &$connectionOpen) { $connectionOpen = false; }); $start = microtime(true); $redirectionTries++; $redirectRequest = function ($id) use(&$redirectRequest, &$incoming, &$incomingBuffer, &$redirectionActive, $start, &$redirectionTries, &$connectionOpen) { if (!$connectionOpen) { //since the initial connection of a client and getting a free worker the client meanwhile closed the connection, //so stop anything here. return; } if (!is_resource($incoming->stream)) { //Firefox closes somehow a connection directly after opening, at this state we need to check //whether the connection is still alive, to keep going. This check prevents the server from crashing return; } $took = microtime(true) - $start; if ($this->output->isVeryVerbose() && $took > 1) { $this->output->writeln(sprintf('<info>took abnormal %f seconds for choosing next free worker</info>', $took)); } $slave =& $this->slaves[$id]; $slave['busy'] = true; $slave['connections']++; $start = microtime(true); $stream = stream_socket_client($slave['host'], $errno, $errstr, $this->timeout); if (!$stream || !is_resource($stream)) { //we failed to connect to the worker. Maybe because of timeouts or it is in a crashed state //and is currently dieing. //since we don't know whether the worker is only very busy or dieing we just //set it back to available worker list. If it is really dying it will be //removed from the available worker list by itself during connection:close event. $slave['busy'] = false; $slave['connections']--; if ($this->output->isVeryVerbose()) { $this->output->writeln(sprintf('<error>Connection to worker %d failed. Try #%d, took %fs. ' . 'Try increasing your timeout of %d. Error message: [%d] %s</error>', $id, $redirectionTries, microtime(true) - $start, $this->timeout, $errno, $errstr)); } //Try next free client. It's important to do this here due to $incomingBuffer. $redirectionTries++; $this->getNextSlave($redirectRequest); return; } $connection = new \React\Socket\Connection($stream, $this->loop); $took = microtime(true) - $start; if ($this->output->isVeryVerbose() && $took > 1) { $this->output->writeln(sprintf('<info>took abnormal %f seconds for connecting to :%d</info>', $took, $slave['port'])); } $start = microtime(true); $headersToReplace = ['X-PHP-PM-Remote-IP' => $incoming->getRemoteAddress()]; $headerRedirected = false; if ($this->isHeaderEnd($incomingBuffer)) { $incomingBuffer = $this->replaceHeader($incomingBuffer, $headersToReplace); $headerRedirected = true; $connection->write($incomingBuffer); } $redirectionActive = true; $connection->on('close', function () use($incoming, &$slave, $start) { $took = microtime(true) - $start; if ($this->output->isVeryVerbose() && $took > 1) { $this->output->writeln(sprintf('<info>took abnormal %f seconds for handling a connection</info>', $took)); } $slave['busy'] = false; $slave['connections']--; $slave['requests']++; $incoming->end(); /** @var Connection $connection */ $connection = $slave['connection']; if ($slave['requests'] >= $this->maxRequests) { $slave['ready'] = false; $this->output->writeln(sprintf('Restart worker #%d because it reached maxRequests of %d', $slave['port'], $this->maxRequests)); $connection->close(); } else { if ($slave['closeWhenFree']) { $connection->close(); } } }); $connection->on('data', function ($buffer) use($incoming) { $incoming->write($buffer); }); $incoming->on('data', function ($buffer) use($connection, &$incomingBuffer, $headersToReplace, &$headerRedirected) { if (!$headerRedirected) { $incomingBuffer .= $buffer; if ($this->isHeaderEnd($incomingBuffer)) { $incomingBuffer = $this->replaceHeader($incomingBuffer, $headersToReplace); $headerRedirected = true; $connection->write($incomingBuffer); } else { //head has not completely received yet, wait } } else { //incomingBuffer has already been redirected, so redirect now buffer per buffer $connection->write($buffer); } }); $incoming->on('close', function () use($connection) { $connection->close(); }); }; $this->getNextSlave($redirectRequest); }
/** * Handle process on close transport * * @param \React\Socket\Connection $conn */ public function handleClose(Connection $conn) { Logger::debug($this, "Raw socket closed " . $conn->getRemoteAddress()); $transport = $this->transports[$conn]; $this->transports->detach($conn); $this->peer->onClose($transport); }