obsolete

OpenShift et DIY, PHP7, WebSockets, Ratchet

Héberger une application WebSockets unique sur un noeud OpenShift peut sembler relativement simple, malgré les nombreuses stratégies de sécurité de la plateforme. Si nous souhaitons héberger plusieurs applications, nous nous heurtons à la contrainte de n’avoir qu’un port en écoute localement, le port 8080. Si nous voulons héberger plusieurs applications, nous n’avons d’autre choix que les héberger derrière le port 8080, moyennant un adressage par routes.

Précédemment, nous avons évoqué la réalisation d’une application de chat en temps-réel sur OpenShift. Nous avions ébauché la chose en nous efforçant de valider, dans un premier temps, que cela pouvait fonctionner, simplement. Nous allons maintenant faire en sorte de mettre à disposition une application WebSockets, et un serveur HTTP, qui délivrera la page HTML et les différents fichiers qui vont avec : feuilles de style, JavaScripts, etc…

La librairie Ratchet référence certains composants Symfony, dont elle emprunte le système de routes. Ainsi, pour déclarer des routes, nous allons instancier une objet RouteCollection :

$routes = new RouteCollection();
$routes->add('chat', new Route('/chat', array('_controller' => new WsServer($chat), 'allowedOrigins' => '*')));
$routes->add('webpage', new Route('/webpage', array('_controller' => $webpage, 'allowedOrigins' => '*')));
$routes->add('js/main.js', new Route('/js/main.js', array('_controller' => $jsHandler, 'allowedOrigins' => '*')));
$routes->add('css/style.css', new Route('/css/style.css', array('_controller' => $cssHandler, 'allowedOrigins' => '*')));

Chacune des routes insérées dans la collection est indexée par un nom. Une route se compose d’une URL simplifiée, et d’une instance du contrôleur chargé de répondre à la demande. Pour délivrer un document HTML sur le port 80 externe, nous avons par exemple besoin de définir un contrôleur chargé de pousser dans le flux de réponse le contenu du fichier demandé. Ici, par exemple :

$routes->add('webpage', new Route('/webpage', array('_controller' => $webpage, 'allowedOrigins' => '*')));

Avec :

$webpage = new WebPage($folder);

Nous devons aussi délivrer les fichiers JavaScripts, les feuilles de style CSS, et pour chacun de ces fichiers nous définissons des contrôleurs. Nous aurions pu tout à fait choisir de développer un contrôleur générique, mais ce n’est pas l’objet de cet article. Le coeur de l’application, à savoir le serveur WebSockets, est supporté par la route /chat, et le contrôleur new WsServer($chat).

Une grosse partie de ce travail n’est possible que par la présence des proxies OpenShift. Ce sont ces proxies qui permettent de faire fi des contraintes de port unique du côté de l’infrastructure, car ce sont eux qui vont adresser les demandes HTTP ou WS sur l’application, en rendant transparent pour les clients le fait qu’il s’agit en fait derrière d’une même application.

Ratchet vient avec une documentation très dense et requiert de nombreuses dépendances. Pour faire simple, sachons simplement que chacun des contrôleurs doit implémenter l’interface HttpServerInterface. Dans le cas de la livraison d’un contenu par le protocole HTTP, nous poussons les données dans le flux de réponse au moment ou la demande arrive au contrôleur, dans la méthode :

function onOpen(ConnectionInterface $conn, RequestInterface $request = null)

Comme suit, pour le fichier CSS par exemple :

public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {

    $this->response = new Response(200, [
        'Content-Type' => 'text/css; charset=utf-8',
    ]);

    $content = file_get_contents($this->folder . 'diy/css/style.css');
    $this->response->setBody($content);
    $this->close($conn);
}

En n’oubliant pas de le préciser dans l’entête de la réponse, afin que le navigateur puisse l’interpréter correctement.

La classe chat. qui contient la logique applicative derrière la route ne change pas, par rapport à ce nous avions implémenté. Elle implémente l’interface MessageComponentInterface et elle est encapsulée dans un objet WsServer, responsable de négocier la poignée de main entre le contrôleur applicatif et le serveur HTTP.

$router = new Router(new UrlMatcher($routes, new RequestContext));
$server = IoServer::factory(new HttpServer($router), 8080, $host);
$server->run();

Voyons maintenant l’instanciation de l’ensemble. Pour continuer à faire simple, la classe IoServer ouvre simplement un socket en écoute, sans implémenter aucun protocole. Cette classe ne gère que l’aspect “transport” de l’application. La classe HttpServer permet la prise en compte d’un protocole de dialogue (HTTP ici…), une fois la connexion établie. Enfin, le routeur permet de distribuer chacune des routes vers un contrôleur donné.

IoServer : il devrait être à la base de votre application. C’est le cœur des événements générés par les actions du client. Il permet de recevoir de nouvelles connexions, de lire / écrire sur ces connexions, de fermer les connexions et de gérer toutes les erreurs de votre application. Cela devrait être à la base de votre application car elle gère la communication directe et le transport avec les clients.

HttpServer : ce composant est responsable de l’analyse des requêtes HTTP entrantes. Le but est d’intercepter les données jusqu’à ce qu’elles reçoivent une requête d’en-tête HTTP complète et la transmettre. Vous pouvez utiliser ceci comme un serveur HTTP brut (pas conseillé) mais il est destiné à la mise à niveau des requêtes WebSocket.

WsServer : ce composant (lorsqu’il est utilisé avec IoServer) permet à votre serveur de communiquer avec les navigateurs Web qui utilisent l’API WebSocket W3C. Il est compatible avec chacune des différentes versions de protocole WebSocket utilisées par les navigateurs: RFC6455, HyBi et Hixie76. Ratchet transmet tous les tests de protocole WebSocket (non binaires) à partir de la Autobahn Testsuite. Ce composant est créé pour vous lorsque vous ajoutez un itinéraire à une instance de l’application.

require __DIR__ . '/vendor/autoload.php';

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use Ratchet\Http\Router;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Matcher\UrlMatcher;

require 'chat.php';
require 'webPage.php';
require 'jsHandler.php';
require 'cssHandler.php';

$host   = '127.0.0.1';
$folder = '.';

if (isset($argv) == true && isset($argv[1])) {
    $host = $argv[1];
}

if (isset($argv) == true && isset($argv[2])) {
    $folder = $argv[2];
}

if (isset($host) == false) {
    $host = "localhost";
}

echo 'Listening on : ' . $host . "\r\n";
echo '1.0.9' . "\r\n";

$chat = new Chat();
$webpage = new WebPage($folder);
$jsHandler = new JsHandler($folder);
$cssHandler = new CssHandler($folder);

$routes = new RouteCollection();

$routes->add('chat', new Route('/chat', array('_controller' => new WsServer($chat), 'allowedOrigins' => '*')));
$routes->add('webpage', new Route('/webpage', array('_controller' => $webpage, 'allowedOrigins' => '*')));
$routes->add('js/main.js', new Route('/js/main.js', array('_controller' => $jsHandler, 'allowedOrigins' => '*')));
$routes->add('css/style.css', new Route('/css/style.css', array('_controller' => $cssHandler, 'allowedOrigins' => '*')));

$router = new Router(new UrlMatcher($routes, new RequestContext));
$server = IoServer::factory(new HttpServer($router), 8080, $host);

$server->run();

Le code source se trouve ici.

Add a Comment

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

84 − = 78