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);
}