Archive

Posts Tagged ‘REST’

something more about HTTP requests and erlang

September 7, 2009 1 comment

Un paio di posts fa vi ho introdotto le API di Twitter e vi ho mostrato come ottenere la timeline di un utente. In tale post ho  utilizzato il modulo gen_tcp fornito da erlang, e utilizzato tale modulo per spedire le mie requests al web server di twitter.

In questo post cercherò di utilizzare il modulo http, che mette a disposizione il metodo request, questo modulo ci dovrebbe risparmiare un sacco di codice!

La prima cosa da fare quando si lavora con il modulo http  consiste nell’avviare l’applicazione inets, tale applicazione mette a disposizione tra le varie cose delle funzioni utili nella scrittura di client e server web.

Iniziamo quindi richiamando la nostra applicazione inets ed eseguiamo una richiesta tramite la funzione request/1 del modulo http. Tale richiesta ci permetterà ti recuperare gli ultimi posts provenienti dalla timeline pubblica. Essendo appunto tale timeline pubblica, non abbiamo bisogno di alcuna autenticazione.

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

Eshell V5.6.5  (abort with ^G)
1> application:start(inets).
ok
2> http:request("http://twitter.com/statuses/public_timeline.xml").

Come specificato nel post precedente, nell’indirizzo utilizzato per la richiesta, possiamo specificare una diversa estensione tra quelle consentite, a seconda dei nostri gusti o bisogni.

Quale funzione dobbiamo utilizzare invece se vogliamo richiedere una timeline associata ad uno specifico utente? Beh in questo caso la procedura da seguire è la seguente:

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

Eshell V5.6.5  (abort with ^G)
1> application:start(inets).
ok
2> Aut = lists:flatten(["Basic ", base64:encode("user:password")]).
[66,97,115,105,99,32,<<"dXNlcjpwYXNzd29yZA==">>]
3> http:request(get,
                {"http://twitter.com/statuses/user_timeline.xml",
                    [{"Authorization", Aut}]
                }, [], []).

Come vedete la funzione request/4 all’interno del modulo http ci permette di risparmiare un sacco di codice!

Tale funzione prende 4 parametri: il primo indica metodo che si vuole effettuare (nel nostro caso get, ma avrebbe potuto essere anche uno qualsiasi tra head, get, put, post, trace, options, delete come vedremo tra poco). il secondo parametro è una tupla rappresentante la richiesta vera e propria al web server. Tale richiesta è composta da una stringa che identifica la risorsa che vogliamo ottenere/modificare e da una lista di headers. Vi consiglio di andare alla pagina http://erlang.org/doc/man/http.html per avere maggiori informazioni.  :-)

Per autenticarci al server inseriamo un nuovo header di tipo Authentication e gli assegniamo il valore ottenuto dall’operazione di unione tra la stringa “Basic ” (ricordatevi di lasciare una spazio prima delle virgolette finali) e il risultato ottenuto dall’encoding delle nostre credentials.

Se avete fatto tutto come si deve, dovreste ottenere la timeline del utente che avete utilizzato per l’autenticazione, altrimenti riceverete un messaggio di errore dal server (come è capitato a me che avevo lasciato user:password :-) )

Gli ultimi due valori dati in ingresso alla funzione request/4 sono due liste vuote; la prima identifica le HTT Options (ad esempio un timeout per la richiesta o la versione dell’HTTP) mentre la seconda lista identifica altre options che vi invito a controllare nel link sopra riportato.

Ora vediamo qualcosa di diverso: proviamo ad aggiornare lo status di un utente!

La documentazione sulle API di Twitter dice che per aggiornare lo status di un utente, bisogna utilizzare nella richista il metodo POST e la URL http://twitter.com/statuses/update.format dove format può essere scelto fra xml e json.  A tale URL possono essere aggiunti altri parametri, il cui unico obbligatorio è status, che rappresenta l’effettivo status che vogliamo postare.

Se ad esempio volessimo modificare il nostro status in “erlang rules” dovremmo utilizzare il seguente codice:

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

Eshell V5.6.5  (abort with ^G)
1> application:start(inets).
ok
2> Aut = lists:flatten(["Basic ", base64:encode("user:password")]).
[66,97,115,105,99,32,<<"dXNlcjpwYXNzd29yZA==">>]
3> http:request(post,{"http://twitter.com/statuses/update.xml",
       [{"Authorization", Aut}],
        "application/x-www-form-urlencoded",
         "status=erlang rules"}, [], []).

Come vedete alcune cose sono rimaste uguali e altre sono cambiate: rimane l’autorizzazione, e le liste vuote alla fine. Cambia il tipo di richiesta (si passa ad un post). E’ stata aggiunta una nuova stringa che specifica che si tratta di codice che va codificato come URL. Cosa più importante è stato aggiunto un body: in questo caso il parametro status che viene fatto coincidere con il la stringa erlang rules.

Molto semplice direi…la cosa che mi piace di questo modulo è che la nostra stringa viene codificata in URL senza grandi sforzi da parte nostra…infatti se volessimo fare lo stesso tipo di operazione tramite un gen_tcp send, dovremmo codificare da soli la nostra stringa in URL, ad esempio sostituendo ogni spazio con un %20.

Ultima cosa importante per chi vuole sperimentare: ricordatevi che la lunghezza del messaggio può essere al massimo 140 caratteri!

Categories: Erlang Tags: , ,

HTTP requests, Erlang and Twitter

September 1, 2009 Leave a comment

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! :D

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: , , , ,
Follow

Get every new post delivered to your Inbox.

Join 26 other followers