# Faye
[Faye](http://faye.jcoglan.com/) é um sistema para subscrever e enviar mensagens baseado no protocolo *Bayeux* (Pub/Sub). O **Faye** providencia um **servidor** de mensagens para ser usado com *node.js* ou *Ruby* além de uma biblioteca em *Javascript* para os **clientes** que funciona em todos os browsers modernos.
## Instalação
Primeiro instale o gem executando o comando: ```gem install faye```
Adicione o **faye** e o servidor **thin** ao gemfile:
gem 'faye'
gem 'thin'
Em seguida rode o comando ```bundle install``` para instalar os *gems*.
Crie o arquivo ```faye.ru``` na raiz da pasta do projeto. Este arquivo vai inicializar o servidor **faye**.
require 'faye'
Faye::WebSocket.load_adapter('thin')
faye_server = Faye::RackAdapter.new(:mount => 'faye', :timeout => 45)
run faye_server
Depois rode o seguinte comando para inicializar o servidor:
```rackup faye.ru -s thin -E product```
Para fazer os dois servidores iniciarem juntos em *development*, modifique o arquivo ```Procfile```:
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
faye: rackup faye.ru -s thin -E production
Depois execute o comando ```foreman start``` para iniciar o servidor
O servidor **faye** vai iniciar em ```0.0.0.0:9292```. Este servidor vai estar servidondo um arquivo *javascript* que precisa ser adicionado no layout da página no rails:
<%= javascript_include_tag :defaults, "http://localhost:9292/faye.js" %>
Em seguida, vamos criar a função para subscrever e enviar mensagens para o servidor. Abra o arquivo ```app/assets/javascripts/application.js```:
$(function() {
var faye = new Faye.Client('http://localhost:9292/faye');
faye.subscribe("messages/new", function(data){
alert(data);
});
});
Para fazer um teste, é possivel enviar uma mensagem via **curl**. Primeiro entre ná página no navegador, depois no terminal execute o comando:
``` curl http://localhost:9292/faye -d 'message={"channel":"/messages/new", "data":"hello" }'```
Ao executar esse comando, o navegador ira receber uma mensagem com o conteúdo do comando.
## Exemplo: Chat
Adicione um *view* com uma lista e um formulário para novas mensagens:
<%= render @messages %>
<%= form_for Message.new, remote => true do |f| %>
<%= f.text_field :content %>
<%= f.submit "Send" %>
<% end %>
Crie mais um *view* com o nome de ```create.js.erb``` para resposta do servidor em *JSON*:
<% broadcast "/messages/new" do %>
$("#chat").append("<%= escape_javascript render(@message) %>");
<% end %>
$("#new_message")[0].reset();
Toda vez que uma mensagem for enviada pela página e criada no servidor, o bloco de codigo dentro da função ```broadcast``` vai ser enviado via o faye para todos os clientes que estão assinando este canal.
Agora precisamos criar a função ```broadcast```. No arquivo ```app/helpers/application_helper.rb``` adicione a função:
module ApplicationHelper
def broadcast(channel, &block)
message = { :channel => channel, :data => capture(&block)}
uri = URI.parse("http://localhost:9292/faye")
Net::HTTP.post_form(uri, :message => message.to_json)
end
end
Pelo fato de estarmos usando a biblioteca **Net:HTTP**, precisamos requerer ela em algum lugar do aplicativo rails. Um bom lugar para se fazer isso é no arquivo ```config/application.rb```.
require "rails/all"
require "net/http"
Por ultimo temos que fazer uma mudança no arquivo ```app/assets/javascripts/application.js```. Em vez de um alerta, o navegador precisa executar o codigo que for recebido do servidor **faye**:
$(function() {
var faye = new Faye.Client('http://localhost:9292/faye');
faye.subscribe("messages/new", function(data){
eval(data);
});
});
Agora ao enviar uma mensagem no formulário da página, todas as instancias abertas da página iram receber a modificação instantaneamente.
## Segurança
É necessário criar um protocolo de segurança, se não qualquer um pode tentar enviar uma mensagem via **curl** para o servidor e rodar *javascript* em todos os clientes que estnao subscrevendo ao sistema do **Faye**.
Para resolver isso, precisamos criar uma extenção no servidor **Faye** para lidar com a segurança. Veja mais informações [aqui](http://faye.jcolan.com/ruby.html).
Basicamente vamos criar uma nova classe que vai conter funções para mensagens entrando e saindo do servidor. Cada uma dessas funções vai fazer um cheque e se algo estiver errado a função levanta um *erro*. Depois criamos uma instancia dessa classe e adicionamos ela como uma extenção do servidor **Faye**.
Primeiro vamos criar um **token** que vai ser enviado junto com as mensagens pelo servidor. Se a mensagem não conter o **token**, o servidor vai levantar um erro.
No arquivo ```config/application.yml``` adicione uma nova **env variable**:
FAYE_SECRET_OKEN: 12345678901234567890
Depois precisamos modificar a função **broadcast** em ```app/helpers/application_helper.rb```:
module ApplicationHelper
def broadcast(channel, &block)
message = { :channel => channel, :data => capture(&block), ext => {:auth_token => env['FAYE_SECRET_OKEN']}}
uri = URI.parse("http://localhost:9292/faye")
Net::HTTP.post_form(uri, :message => message.to_json)
end
end
Por último, no arquivo ```faye.ru``` precisamos criar a classe que vai lidar com a autenticação:
require 'faye'
class ServerAuth
def incoming(message, callback)
if message['channel'] !~ %r{^/meta/}
if message['ext']['auth_token'] != env['FAYE_SECRET_OKEN']
message['error'] = 'Invalid authentication token'
end
end
callback.call(message)
end
end
faye_server = Faye::RackAdapter.new(:mount => 'faye', :timeout => 45)
faye_server.add_extension(ServerAuth.new)
run faye_server
Ao reiniciar o servidor e tentar enviar uma nova mensagem via **curl** sem o token de autenticação, vamos receber um erro:
$ curl http://localhost:9292/faye -d 'message={"channel":"/messages/new", "data":"hello" }
HTTP/1.1 400 Bad Request
Content-Type: application/json
Connection: close
Server: thin 1.2.11 codename Bat-Shit Crazy
content-Length: 11
Mas ao enviar a mensagem atravez da página em rails, a autenticação vai funcionar e a mensagem vai ser recebida por todos que estão subscrevendo a página.
## Outros recursos
-----------------
[Index](index.md)