Follow me
RSS feed
My sources
My Viadeo

Notification, Comet, ... Capcode

Greg | 21 Nov 2009

devDepuis que nous développons des sites Web, nous avons pris l'habitude du mode connecté imposé par le protocole HTTP. Rien ne nous surprend plus quand nos serveurs ne répondent qu'à des demandes explicites. Et nous vivons avec l'habitude de ce dialogue de sourds ou le serveur répond à une requête en oubliant totalement ce qu'il a dit à la demande précédente. Nous pallions généralement à ce problème en utilisant des principes de session et autre cookie, mais n'oublions pas que ces artefacts sont en fait gérés par le client à qui le serveur demande de stocker des souvenirs. Depuis l'apparition des frameworks JavaScript comme Dojo, prototype, jQuery et consorts, nous mettons de plus en plus de mécanismes dans les pages Web qui évitent de reloader toute une page. Là encore, cela ne fait plus rêver personne et nous restons toujours dans un mode requête/réponse. Là où nous écarquillons un peu plus les yeux, c'est quand une page Web affiche, sans que nous n'ayons rien demandé, de nouvelles informations. C'est tout le principe des notifications et c'est ce que nous allons mettre en place ici.

Notifications ?

Une notification intervient quand un serveur envoie des données au client quand il en a envie, et donc sans aucune demande du client. En dehors du Web, tous les systèmes de messagerie instantanés, par exemple, fonctionnent sur ce principe. Mais de par son côté stateful, le protocole HTTP ne permet pas ce genre de chose. Faux ! me direz-vous. La preuve par Google, ou plutôt Google Mail qui non seulement ne nous oblige pas à reloader sa page pour découvrir de nouveaux messages, mais qui en plus propose un service de messagerie instantanée aussi efficace que n'importe quel Skype ou MSN ! Et bien malheureusement, c'est vrai. Et si nous arrivons à faire de la notification via le Web ce n'est que par le truchement de bidouillages plus ou moins subtils qui vont permettre de le simuler. Depuis plusieurs années, nous désignons les mécanismes permettant de faire du push HTTP sous le terme Comet, inventé par Alex Russell.

Comet

Comet est une désignation peu claire. En effet, personne ne sait très bien ce que recouvre ce terme. Globalement on lui attribut tous les mécanismes permettant de faire de la notification.

Oui mais comment faire cela justement ?

Dans son fonctionnement classique, un serveur HTTP attend une requête du client et renvoie une réponse :

ClassicHTTP

Avec AJAX, nous avons rajouté, côté client, une couche entre l'utilisateur et le serveur. C'est elle qui reçoit les demandes du client, qui les transmet au serveur, qui reçoit la réponse, et elle se charge de mettre à jour la page. Ceci fait que les chargements sur la page ne sont pas forcement synchronisés avec les échanges qui ont lieu entre le moteur AJAX et le serveur :

AJAXHTTP

Cela permet ainsi de mettre à jour des informations dans la page sans tout recharger. Avec AJAX, nous pouvons déjà simuler un comportement de notifications. En effet, il suffit de demander au moteur AJAX d'envoyer une requête, à intervalle régulier au serveur, et si ce dernier à une nouvelle information, de l'afficher. Mais ce n'est pas du Comet. En effet, dans ce cas, ce n'est pas le serveur qui est à l'initiative de l'envoi de données. De plus, l'envoi de données par le serveur est rythmé par les demandes du client. Et même si elles peuvent être très rapprochées, elles ne donneront pas toujours le même sentiment de fluidité que nous retrouverons avec un Comet bien fait.

Avec Comet, nous allons remplacer le serveur AJAX pas un client Comet qui se placera entre le client et le serveur. Côté serveur, nous allons placer un bus Comet chargé de communiquer avec le service :

CometHTTP

Côté client, la connexion Comet est initialisée. A partir de là, le service enverra des évènements au bus Comet qui se chargera d'envoyer les informations nécessaires au client Comet qui à son tour mettra à jour la page Web. Bien entendu le client peut lui même envoyer de l'information au service par l'intermédiaire du client puis du bus Comet. Vous l'aurez compris, nous utilisons aussi de l'AJAX. Quant au service, cela peut être n'importe quoi : un service de messagerie instantané, un serveur de données, ... bref n'importe quel service capable lui-même de pousser des données.

Capcode

Avec Capcode nous pouvons faire du Comet en utilisant un middleware Rack. Personnellement j'en ai trouvé 2 : Pusher et faye. Bien qu'un peu moins aboutit que le premier, je vous propose d'utiliser faye.

faye arrive avec un exemple sous forme de messagerie instantanée à la Twitter. Pour valider le fonctionnement de faye avec Capcode, je vous propose d'adapter cet exemple.

soapbox est écrit comme une application Sinatra. Le code de l'application est minimaliste :

require 'rubygems'
require 'sinatra'

get '/' do
  @server = env['faye.server']
  erb :index
end

La seule chose remarquable ici est le fait que lors de l'accès à la racine de l'application, le serveur et modifié pour devenir le serveur faye et que nous renvoyons la page index.erb. Transformer cela en code Capcode est ultra simple :

 1 require 'rubygems'
 2 require 'capcode'
 3 require 'capcode/render/erb'
 4 
 5 module Capcode
 6   set :erb, "views"
 7 
 8   class Index < Route '/'
 9     def get
10       @server = env['faye.server']
11       render :erb => :index
12     end
13   end
14 end

Pour respecter ce qui est fait, nous créons un répertoire views dans lequel nous placerons les templates Erb, tout cela sans oublié de le déclarer (ligne 6).

Si nous regardons maintenant le fichier de configuration pour Rack, nous avons cela :

dir = File.dirname(__FILE__)

require dir + '/../../lib/faye'
require dir + '/app'

use Faye::RackAdapter, :mount => '/comet'
run Sinatra::Application
Ajouter cela dans Capcode est là encore trivial :
require 'rubygems'
require 'capcode'
require 'capcode/render/erb'
require 'faye'

module Capcode
  set :erb, "views"
  use Faye::RackAdapter, :mount => '/comet'
  
  class Index < Route '/'
    def get
      @server = env['faye.server']
      render :erb => :index
    end
  end  
end

Si maintenant nous regardons le template Erb nous voyons qu'il utilise une CSS et trois fichiers JavaScript :

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 
<html>
<head>
  <meta http-equiv="Content-type" content="text/html
 charset=utf-8" />
  <title>Faye demo: chat client</title>
  <link rel="stylesheet" href="/style.css" type="text/css" media="screen">
  <script src="/jquery.js" type="text/javascript"></script>
  <script src="/comet.js" type="text/javascript"></script>
  <script src="/soapbox.js" type="text/javascript"></script>
</head>
<body>
<div class="container">
...

Les fichiers style.css, jquery.js et soapbox.js se trouvent dans le répertoire public. Nous devons donc créer ce même répertoire et déclarer son existence dans Capcode :

require 'rubygems'
require 'capcode'
require 'capcode/render/erb'
require 'faye'

module Capcode
  set :erb, "views"
  set :static, "public"
  use Faye::RackAdapter, :mount => '/comet'
  
  class Index < Route '/'
    def get
      @server = env['faye.server']
      render :erb => :index
    end
  end  
end

Puis nous modifions le template Erb :

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
<head>
  <meta http-equiv="Content-type" content="text/html
 charset=utf-8" />
  <title>Faye demo: chat client</title>
  <link rel="stylesheet" href="/public/style.css" type="text/css" media="screen">
  <script src="/public/jquery.js" type="text/javascript"></script>
  <script src="/comet.js" type="text/javascript"></script>
  <script src="/public/soapbox.js" type="text/javascript"></script>
</head>
<body>
<div class="container">
...

Oui, mais le fichier comet.js ??? Et bien si vous regardez la documentation de faye, vous verrez que le simple fait de déclarer l'utilisation du middleware faye met automatiquement en place un bus Comet accessible via la route /comet et met à disposition la librairie JavaScript cliente via la route /comet.js. Donc, nous n'avons rien de plus à faire pour ce fichier.

Vous pouvez maintenant démarrer l'application et tester :

comet-example

Cet exemple a été ajouté dans les sources de Capcode.

Toute l'intelligence de l'exemple se trouve non seulement fans faye mais également dans le fichier soapbox.js que je vous engage vivement à regarder, tout comme la documentation de faye.

Copyright © 2009 - 2011 Grégoire Lejeune.
All documents licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License, except ones with specified licence.
Powered by Jekyll.