erlang: mochiweb + BOSH

August 11th, 2010 — 08:49 pm

I’ve been playing with mochiweb for a while and I found it a nice way to improve my Erlang knowledge. For making things more interesting I want to use mochiweb to implement a BOSH manager. If you are not tempted to read the spec documentation about BOSH I can give you this excerpt:

BOSH, the technology defined in this specification, essentially provides a “drop-in” alternative to a long-lived, bidirectional TCP connection …

For applications that require both “push” and “pull” semantics, BOSH is significantly more bandwidth-efficient and responsive than most other bidirectional HTTP-based transport protocols and the techniques now commonly known as “Ajax”…

That is, BOSH is quite similar to WebSockets

So, at this point is where mochiweb comes in. It will provide the framework for handling the HTTP requests where the BOSH requests themselves are wrapped. But before going any further lets explain how mochiweb works, with a picture:

What do we have here?

  1. An HTTP request comes in, to the address and port mochiweb is configured.
  2. If it’s a valid HTTP request, a mochiweb_request “object” is created and the handler function at the callback module (which is provided when mochiweb starts) is invoked with this mochiweb_request as a parameter. Give a look to this and this to get a grasp at parameterized modules if you wish to know more about “objects” on Erlang.

Easy, huh?

And what is all that about a BOSH manager? The BOSH manager handles the connection between the resource-client (for example a browser) and the resource-owner (for example a server). It has to keep HTTP connections opened to fake this bidirectional comunication. That is, the client ask for something about the resource and so it sends a request to the BOSH manager, the BOSH manager sends this same request to the server and waits for a reply. If a reply arrives, then it’s forwarded to the client. But aside from this, which is no more than plain-old AJAX, BOSH offers PUSH technologies. If any event happens at the server and the client has to be notified, the BOSH manager forwards the event to the client. That’s why it has to keep HTTP connections opened, it must have a valid path.

In Erlang, creating and destroying processes is extremely cheap, so what I wanted was  to spawn a new process for each single BOSH session (between one client and one or multiple servers). Lets give another picture:

On the picture above, there are two separate BOSH managers (each of them is a Erlang process) BM1 and BM2 which handler connections from one client to one server (BM1 ) and from another different client to two servers (BM2)

To complete the setup I used, I have to talk about StropheJS which is a library for writing xmpp clients (BOSH is an xmpp extension).

Well, once I have explained more or less what I’m doing with mochiweb and BOSH, lets got to the details.

First, lets talk about my callback module for mochiweb. It’s not a ver big one nor a complicated one, not because I don’t know how to move on but was the error I’ll talk later which didn’t allow me to progress. So ladys and gentleman here is my litle callback module:


-module(mochibosh).

-export([start/0, start/1, stop/0]).
-export([handler/1]).

-export([init/0]).

-record(mochibosh_state,{procs, xml_models}).

handler(Req) ->
 io:format("CALLBACK. Petition on port ~p  ~n", [inet:port(Req:get(socket))]),
 mochibosh ! Req,
 ok.

start() ->
 erlang:register(mochibosh, spawn_link(?MODULE, init, [])),
 start([{ip,"192.168.1.110"}, {port, 8888}]).

start(Options) ->
 Options2 = [{loop, {?MODULE, handler}} | Options],
 mochiweb_http:start(Options2).

stop() ->
 mochibosh ! exit,
 mochiweb_http:stop().

% Internal API

init() ->
 Models = init_xml_models(),
 loop(#mochibosh_state{procs = dict:new(), xml_models = Models}).

loop(State = #mochibosh_state{procs = Procs, xml_models = Models}) ->
 receive
 Request ->
 io:format("SPAWNED PROCESS. Request received on port ~p ~n", [inet:port(Request:get(socket))]),
 loop(State)
 end.

init_xml_models()->
 [{bosh_init, init_xml_model("bosh_init.xsd")}].

init_xml_model(Xsdfile) ->
 {ok, Model} = erlsom:compile_xsd_file(Xsdfile),
 Model.

It very easy. If want to use it, you cant fire a Erlang terminal and type mochibosh:start() and it will be up and running. The loop
option in the tuple that is passed to mochiweb_http:start is used to provide the callback module. In this case is the mochibosh module itself (?MODULE)

So what would happen if we try it….

In one terminal we fire the Erlang shell (erl) and start mochibosh:


20:38:25 mochibosh $ erl
Erlang R14A (erts-5.8) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.8  (abort with ^G)
1> mochibosh:start().
{ok,<0.34.0>}

In another terminal we issue a simple request with curl


20:40:19 mochibosh $ curl -v http://192.168.1.110:8888
* About to connect() to 192.168.1.110 port 8888 (#0)
*   Trying 192.168.1.110... connected
* Connected to 192.168.1.110 (192.168.1.110) port 8888 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.21.0 (i386-apple-darwin10.2.0) libcurl/7.21.0 OpenSSL/1.0.0a zlib/1.2.5 libidn/1.19
> Host: 192.168.1.110:8888
> Accept: */*
>
^C

And meanwhile in the Erlang shell..


{ok,<0.34.0>}
CALLBACK. Petition on port {ok,8888}
SPAWNED PROCESS. Request received on port {ok,8888}

2>

GREAT!! Everything seems to work. So now, I decided to move on and begin to develop my BOSH manager.

As I sayed, I’ll be using StropheJS for the browser/client side. For testing purpouses I began using one of the examples shipped with the library: echobot.js .This example will issue a request for starting a BOSH session (among other things) and that is perfect for my testing pourposes. So lets, try.

(Imagin that the request is sent from echobot to the BOSH manager)

And I expecto to get the same reply from my callback module as when I tryed it with curl. Lets see:


CALLBACK. Petition on port {ok,8888}
SPAWNED PROCESS. Request received on port {error,einval}

WTF!!!!! What’s that of {error, einval}. Well, I can see that’s an error, but why? As this post is getting really big, I’ll leave the explanation for another one.

3 comments » | erlang, programacion

Install mochiweb with macports

July 19th, 2010 — 06:32 pm

Just two things:

  1. I installed this on a MacBook with a CoreDuo processor. By default macports will try to use all avaliable processors for the building stuff (2 on my case) with the -j option of Make. On my case this ended up with weird compilation errors (something like a race condition between the to make jobs). I fixed it setting the buildmakejobs to 1 (at /opt/etc/macports/macports.conf) so it used just one make job
  2. The Makefile rule for the documentation at <package>/support/include.mk is wrong. The function that make should call at the edoc module is files/3 not file as it’s deprecated.

Enjoy!!

1 comment » | programacion

Ruby: gmaps-geocode 0.1.0 is out !!

July 4th, 2009 — 09:12 pm

Disponible en github

Comment » | ruby

Ruby: ¡Cállate IRB!

June 22nd, 2009 — 10:45 am

¿Harto de que en alguna ocasión IRB sea demasiado verboso? Yo si.

Por defecto, IRB siempre “escupirá” el valor resultante de evaluar la expresión introducida por el usuario. Aunque la mayoría de las veces eso está bien, hay otras en las que sacarias el corazón con una cucharilla de Té  a quien tuvieras delante por la crispación que produce esta “verbosidad”.

Me refiero a situaciones del tipo “quiero recorrer un array y listar el nombre de sus elementos con puts”, ejemplo:

ArrayEnorme.each do |element| puts "#{element.name}\n" end

Con el código anterior vamos a mostrar el nombre de cada elemento del Array. Hasta aquí todo normal. El problema de evaluar esto en IRB es que, si ArrayEnorme tiene bastantes elementos y cada elemento tiene bastantes atributos, las lineas que imprimos con el nombre de cada elemento quedaran “sepultadas” por un montón de datos inutiles. Es decir, si el buffer de nuestra consola no se ha llenado con estos datos inutiles (perdiendo así las lineas con el nombre de cada elemento) tendremos que hacer scroll ad-infinitum, que para el caso es casí como si se hubierán perdido.

Para evitar esto, os presento mi solución de andar por casa:


def toggle_output
context.return_format == "Output canceled\n" ? IRB.conf[:PROMPT][context.prompt_mode][:RETURN] : context.return_format = "Output canceled\n"
end

alias tgo toggle_output

Después de copiar las lineas anteriores en nuestro .irbrc podemos hacer pruebas:

madtrick::madtrick::$irb
irb(main):001:0> 2 + 1
=> 3
irb(main):002:0> toggle_output
Output canceled
irb(main):003:0> 2 + 1
Output canceled
irb(main):004:0> toggle_output
=> "=> %s\n"
irb(main):005:0> 2 + 1
=> 3

Y voila, ya podemos callar y dar voz a IRB cuando nos plazca.

2 comments » | programacion, ruby

Ruby: Sketches en IRB

June 19th, 2009 — 05:47 pm

Hoy descubrí una gema m-a-r-a-v-i-l-l-o-s-a: Sketches.

El concepto que implementa sketches es sencillo: las modificicaciones que hagamos en nuestro editor de texto, se reflejan al instante en la sesión de irb. Es decir, si en el editor de texto creamos una clase y guardamos los cambios, la clase estará disponible automaticamente en la sesión de irb.

Su utilización es sencilla: arrancamos el IRB y lanzamos nuestro editor favorito con el comando sketch. A partir de ahí, el funcionamiento es como expliqué antes.

Mola : )

Comment » | programacion, ruby

Nach oben

« Previous Entries     Next Entries »