Home > Erlang > HTTP requests, Erlang and Twitter

HTTP requests, Erlang and Twitter


Una delle cosette a cui mi sto interessando ultimamente, sono le API di Twitter di cui potete trovare una completa descrizione a questo indirizzo: http://apiwiki.twitter.com/

Un’operazione che uno sviluppatore potrebbe voler eseguire, consiste nell’accedere alla timeline di un preciso utente, per fare questo dobbiamo in qualche modo avere un metodo di autenticazione verso Twitter.

Twitter.com

Originally from: Twitter.com

I due metodi messi a disposizione sono OAuth e Basic Auth: lasciamo stare OAuth al momento e vediamo come funziona un’autenticazione di tipo Basic Auth.

Internet è ricco di buone guide per capire quali sono i passi da eseguire in questo caso (e.g. http://en.wikipedia.org/wiki/Basic_access_authentication). Come potete vedere nel link precedente, l’autorizzazione di tipo Basic Auth non fa niente di più che aggiungere alla http request rivolta al server per una specifica risorsa, le credentials dell’utente, ovvero la stringa “utente:password” codificata in base64.

La richiesta che dobbiamo spedire alla porta 80 del server Twitter.com per ottenere gli ultimi 20 elementi postati da un utente è la seguente:

GET /statuses/user_timeline.rss HTTP/1.0
Host: localhost
Authorization: Basic dXNlcjpwYXNzd29yZA==

E’ interessante notare che possiamo ottenere questi 20 elementi incapsulati in diversi formati; nella richiesta precedente vogliamo ottenere un risultato in formato rss ma è possibile sostituire .rss con .json, .atom o .xml

Nella parte authorization, indichiamo che si tratta di un’autorizzazione di tipo basic è di seguito alleghiamo la stringa ottenuta codificando in base64 le credentials dell’utente (in questo caso user:password).

Un primo modo per testare questo processo è utilizzare telnet (se lo avete sul vostro sistema!).

Aprite il terminale ed eseguite:

telnet twitter.com 80
Trying 168.143.162.116...
Connected to twitter.com.
Escape character is '^]'.
GET /statuses/public_timeline.rss HTTP/1.0
Host: localhost
Authorization: Basic (qui mettete le vostre credentials criptate)

Dopo aver inserito le vostre credentials dovete premere invio 2 volte e magicamente otterrete sulla console gli ultimi 20 post dell’utente.

Ora il punto è: come fare tutto ciò in erlang?

Beh, accediamo alla erlang shell tramite il comando erl.

Erlang (BEAM) emulator version 5.6.5  [async-threads:0]
[kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> base64:encode("user:password").
<<"dXNlcjpwYXNzd29yZA==">>

Il modulo base64 mette a dispozione la funzione encode che data una stringa da come risultato un binary ottenuto codificando la stringa in base64; il contrario dell funzione encode è la funzione decode.

A questo punto non ci resta che aprire una connessione verso Twitter!

2> {ok, S1} = gen_tcp:connect("www.twitter.com", 80, [binary, {active, false}]).
{ok,#Port<0.426>}
3> gen_tcp:send(S1, <<"GET /statuses/user_timeline.rss HTTP/1.0\r\n">>).
ok
4> gen_tcp:send(S1, <<"Host: localhost\r\n">>).
ok
5> gen_tcp:send(S1, <<"Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n\r\n">>).
ok
6> gen_tcp:recv(S1, 0).

Nella riga numero 2 utilizziamo il modulo gen_tcp per aprire una connessione verso twitter.com alla porta 80; alcune flags sono state settate in questa funzione: binary, che ci dice che i pacchetti sono ricevuti come binaries e la tupla {active, false} che indica che i pacchetti saranno recuperati per mezzo del comando gen_tcp:recv/2 o gen_tcp:recv/3.

Come potete notare eseguiamo un pattern matching tra il risultato di questa funzione e la tupla {ok, S1} in questo modo leghiamo il socket ottenuto alla variabile S1 (N.B. se l’operazione non va a buon fine la funzione non ritornerà la tupla precedente e qundi ci sarà un errore nel pattern matching!).

Il comando gen_tcp:sent(Socket, binary) manda verso il socket (nel nostro esempio S1) un binary a nostra scelta e restituisce sempre l’atom ok.

Dopo aver spedito i nostri 3 binaries (notare che l’ultimo contiene un”\r\n” per indicare l’ulteriore a capo) usiamo la funzione gen_tcp:recv(Socket, 0) che ritorna il risultato della nostra operazione di get nei confronti del web server. Il numero 0 indica che vogliamo ottenere tutto ciò che il server ha risposto in un unico bulk (A volte capita che il bulk di dati non funzioni (mi e’ capitato) quindi potete eseguire piu volte l’operazione gen_tcp:recv/n.); gen_tcp:recv/3 differisce dalla precedente funzione in quanto si può specificare un timeout in millisecondi come terzo argomento, se nulla è ricevuto dopo Timeout, la funzione ritorna una tupla del tipo {error, timeout} (vi consiglio http://erlang.org/doc/man/gen_tcp.html per maggiori informazioni!).

Come possiamo racchiudere tutto cio’ in un modulo?

Eccovi una breve soluzione!

-module(twitter).
-export([start/0]).

start() ->
        {ok, S} = gen_tcp:connect("www.twitter.com", 80, [binary, {active,true}]),
        gen_tcp:send(S, <<"GET /statuses/user_timeline.rss HTTP/1.0\r\n">>),
        gen_tcp:send(S, <<"Host: localhost\r\n">>),
        gen_tcp:send(S, <<"Authorization: Basic (vostre credentials)\r\n\r\n">>),
        Result = recv(S),
        io:format("Result is: ~p~n", [Result]).

recv(S) ->
        recv(S, []).
recv(S, Bulk) ->
        receive
                {tcp, S, Data} ->
                        recv(S, [Bulk|Data]);
                {tcp_closed, S} ->
                        list_to_binary(Bulk)
        end.

La prima cosa da notare e’ che abbiamo cambiato nella funzione di connessione la flag active da false a true. Come prima mandiamo i nostri binaries al server e dopo di cio’ eseguiamo la funzione recv(S) che puo’ ricevere due diversi tipi di dato: pacchetti tcp dal socket o un messaggio di chiusura del socket.

Nel primo caso non facciamo altro che iterare nuovamente la funzione di ricezione aggiungendo alla nostra lista dei dati ricevuti (che in partenza e’ vuota) un nuovo binary. Quando riceviamo il messaggio di chiusura del socket prendiamo la lista di binaries che abbiamo accumulato in ricezione e la convertiamo in un unico binary.

Ecco, piu o meno questo e’ quando ho da dire sull’argomento…probabilmente mi sono dimenticato qualcosa..o forse ho scritto delle castronerie!😀

In futuro vorrei scrivere sul modulo http di erlang e cercare tramite tale modulo di fare cio’ che ho fatto in questo post tramite il modulo gen_tcp…ma non garantisco nulla!

Categories: Erlang Tags: , , , ,
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: