-+  Associazione
-+  Documenti
-+  Eventi
-+  Community
-+  Blog
-+  Link

Ottobre 2013

Dom Lun Mar Mer Gio Ven Sab
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

Cerca






 


« Maggio 2004 | Home | Luglio 2004 »

Migliorare la navigazione sul web con Perl

Ai tempi in cui utilizzavo ancora Windows :-), trovavo molto utile Proxomitron, un programma che permette di filtrare le richieste del browser bloccando e/o modificando i popup, le immagini, il codice javascript e le pagine html in base a impostazioni personalizzabili in modo semplice e potente.

Proxomitron purtroppo è disponibile soltanto per Windows. Niente paura, però! Perl (e CPAN) vengono in nostro aiuto, permettendoci di realizzare in modo semplice e veloce un mini proxy, che con un po' di intraprendenza può diventare un proxomitron in miniatura. Vediamo come...

HTTP::Proxy è una distribuzione presente su CPAN che permette di realizzare un server proxy minimale in poche righe di perl.

#!/usr/bin/perl
use HTTP::Proxy;
fork and exit;
my $proxy = HTTP::Proxy->new(port => 3129);
$proxy->start();

Questo proxy non è di per sé molto utile.

Nella distribuzione di HTTP::Proxy sono presenti due classi base che permettono di estenderne il funzionamento, filtrando le richieste HTTP. Ogni filtro può lavorare a livello di headers HTTP (head), oppure a livello di contenuto effettivo (body). Rispettivamente, il filtro erediterà dalle classi base HTTP::Proxy::HeaderFilter oppure HTTP::Proxy::BodyFilter.

Una classe filtro utilizza il metodo init() per l'inizializzazione globale e può agire sulla richiesta del browser tramite il metodo filter().

Ipotizziamo ora un semplice filtro d'esempio che intercetti le richieste ad immagini di banner pubblicitari e le sostituisca automaticamente con una predefinita. Insomma, qualcosa che somigli vagamente a questo:

Come avrete già capito, questo filtro c'è già! Si tratta del modulo HTTP::Proxy::BodyFilter::Adnix. Si tratta di un proof-of-concept che mostra cosa è possibile fare con le classi filtro del framework di HTTP::Proxy. Per chi si stesse chiedendo che cosa significa Adnix, consiglio la lettura del fantastico libro Contact di Carl Sagan. In caso di curiosità estrema, una ricerca sul web può alleviare la sofferenza.

Vediamo in dettaglio la parte centrale del modulo Adnix, il metodo filter().

 1: sub filter
 2: {
 3:   my($self, $headers, $message) = @_;
 4:   my $uri = $message->uri();
 5:  
 6:   # "DECLINE" non-image urls
 7:   return 0 unless $uri =~ /\.(gif|jpe?g|png)/i;
 8:  
 9:   # Load placeholder image if not yet done
10:   if( ! $IMAGE ) {
11:     $IMAGE = $self->_loadImage();
12:   }
13:
14:   foreach( @{ $self->{_denylist} } ) {
15:     my $re = $_;
16:     if( $uri =~ $re ) {
17:       $self>proxy()>log( '', '', 'blocked ad image('.$uri.')' );
18:       my $response = HTTP::Response->new(
19:         200,
20:         'OK',
21:         HTTP::Headers->new(
22:           Content_Type => ( $self->{_image} =~ /\.(gif|png|jpg)/i
23:                                             ? qq{image/$1}
24:                                             : 'image/png' ),
25:           Content_Length => -s $IMAGE,
26:         ),
27:         $IMAGE
28:       );
29: 
30:       $self>proxy()>response($response);
31:       last;
32:     }
33:   }
34: 
35:   return 1;
36: }

Esaminiamo le parti essenziali.

 1: sub filter
 2: {
 3:   my($self, $headers, $message) = @_;

I due parametri passati al metodo filter() da HTTP::Proxy sono:

  • $headers, un reference ad un oggetto HTTP::Headers, parte della onnipresente libreria LWP, che contiene la lista degli headers della richiesta HTTP (guarda caso)
  • $message, un reference ad un oggetto HTTP::Request.
 4:   my $uri = $message->uri();
 5:  
 6:   # "DECLINE" non-image urls
 7:   return 0 unless $uri =~ /\.(gif|jpe?g|png)/i;

Alla linea 4, ad $uri viene assegnato l'URL completo della richiesta HTTP relativa all'immagine corrente.
Le linee 6 e 7 provvedono a declinare le richieste relative a URL che non siano immagini gif, jpeg o png. In realtà questo non è corretto; come sappiamo non è obbligatorio identificare le immagini con un'estensione. Per semplicità, diamo per scontato che sia così.

Con declinare (italiano impreciso per to decline), si intende ripassare la richiesta al modulo HTTP::Proxy in modo che venga servita in modo "regolare", senza l'intervento del filtro.

 9:  # Load placeholder image if not yet done
10:   if( ! $IMAGE ) {
11:     $IMAGE = $self->_loadImage();
12:   }

Le linee da 9 a 12 caricano in memoria l'immagine da utilizzare come segnaposto sostitutivo dei banner pubblicitari. A questo scopo usiamo lo scalare globale $IMAGE.

14:   foreach( @{ $self->{_denylist} } ) {
15:     my $re = $_;
16:     if( $uri =~ $re ) {

Siamo nella parte principale del filtro. Viene iniziato un ciclo su tutte le espressioni regolari definite nella denylist, che viene definita nella parte di inizializzazione come lista di espressioni regolari precompilate:

 $self->{_denylist} ||= [ map { qr($_) }
    'ad[vs\.]',
    'adv?server',
    '468x60',
    'doubleclick\.net',
    'promot[ie]',
];

Se l'URL dell'immagine corrente corrisponde ad almeno una regexp (linea 16), essa viene considerata pubblicità. Se nessuna regexp corrisponde, il filtro non viene attivato, e l'immagine viene servita normalmente.

17:       $self>proxy()>log( '', '', 'blocked ad image('.$uri.')' );
18:       my $response = HTTP::Response->new(
19:         200,
20:         'OK',
21:         HTTP::Headers->new(
22:           Content_Type => ( $self->{_image} =~ /\.(gif|png|jpg)/i
23:                                             ? qq{image/$1}
24:                                             : 'image/png' ),
25:           Content_Length => -s $IMAGE,
26:         ),
27:         $IMAGE
28:       );
29: 
30:       $self>proxy()>response($response);
31:       last;

Appurato che l'immagine corrente è da rimpiazzare, non rimane che "cortocircuitare" la richiesta HTTP in modo che non segua il ciclo normale (richiesta HTTP al server, risposta, invio al browser), ma la risposta venga generata direttamente dal nostro filtro, inserendo come dati lo stream binario dell'immagine già caricata in $IMAGE.

Di ciò si occupano le linee da 18 a 28, costruendo un nuovo oggetto HTTP::Response, che contiene anche gli header HTTP opportuni (oggetto HTTP::Headers) come Content-Type e Content-Length, in base all'immagine da servire. Questo è importante, altrimenti il browser potrebbe non interpretare in maniera corretta i dati dell'immagine servita (PNG, piuttosto che Jpeg, ...).

Ultima operazione (linea 30), segnalare al modulo HTTP::Proxy, che è già disponibile una risposta valida (oggetto HTTP::Response) che può essere inviata al browser.

Ed ecco finalmente il risultato di cotanto sforzo...

Ovviamente, il tutto è decisamente migliorabile. Si potrebbe pensare di intercettare le dimensioni delle immagini da rimpiazzare, in modo da utilizzare immagini diverse a seconda delle grandezze (tipica caratteristica dei banner pubblicitari, che si presentano per lo più in 468×60, 728×90, ...).

Facciamo ancora un passo avanti. Potremmo generare dinamicamente delle immagini segnaposto tagliate su misura in base a quelle originali. Sarebbe necessario eseguire la richiesta HTTP per leggere il contenuto delle immagini da rimpiazzare. Successivamente ricavarne le dimensioni con un modulo come Image::Info, e creare un'immagine vuota esattamente di quella grandezza, con librerie come GD oppure ImageMagick, a seconda delle esigenze.

Insomma, la strada è aperta...

Happy proxying!

Inviato da Cosimo alle 01:55 | Commenti (0)

Migrazione

Il sito www.perl.it sta migrando su un nuovo server. Gran parte del lavoro è ultimato, e l'operazione dovrebbe essere preludio all'implementazione di nuovi servizi per migliorare il sito, anche seguendo le indicazioni che stiamo raccogliendo con il poll. Un grande grazie a valdez, che ha deciso di ospitarci.

(Grazie ad Andrea Rasetti, che ci ha permesso di usare una delle sue foto)

Inviato da larsen alle 15:20 | Commenti (0)

Perl in edicola a giugno

Il mese di giugno si è rivelato ricco di articoli in cui si scrive di Perl. Iniziamo da Dev di Infomedia, dove troviamo l'articolo di Marco Marongiu sul Perl e gli strumenti che offre per la portabilità e la riusabilità del codice e, firmato da Michele Beltrame, la prima parte del minicorso in due puntate sul sistema di templating Petal. Su Computer Programming, sempre di Infomedia, troviamo un articolo sull'autenticazione SSL su Apache firmato da Massimo Nardone, dove Perl viene utilizzato per l'inserimento di un certificato. Infine su Linux&C troviamo la prima parte di una serie dedicata a DBI firmata da Giuseppe Maxia ed inoltre uno script Perl anche sull'articolo di Gilberto Ficara dedicato alla creazione di una distribuzione live Linux personalizzata, basata su Knoppix.

Inviato da dree alle 15:41 | Commenti (4)

IPW 2004: programma online!

Amici,

i lavori per l'organizzazione dell'Italian Perl Workshop procedono a ritmo serrato. Abbiamo finalizzato il programma, con abstract degli interventi e informazioni sui relatori: http://www.perl.it/workshop/schedule.html

Abbiamo inoltre aperto le registrazioni: http://www.perl.it/workshop/registration/

Ora è il vostro turno! Abbiamo bisogno di conoscere al più presto il numero di partecipanti e il numero dei posti è limitato a 150, quindi se siete intenzionati a venire registratevi subito.

Arrivederci a Pisa! http://www.perl.it/workshop/

Inviato da larsen alle 16:28 | Commenti (0)

De comparatione

Recentemente sulla mailing list dedicata allo sviluppo del Perl6 è comparsa una interessante domanda: si chiedeva se il Perl6 avrebbe continuato ad avere, come il Perl5, gli operatori eq e == (e quindi tutta la serie degli operatori di comparazione "doppi", gt e >, lt e <, etc.).

La risposta è ovviamente si, e l'interesse sta soprattutto in una serie di riflessioni che l'argomento mi ha portato a fare, e che mi permetto di usare come "pretesto" per infliggervi un breve trattato su come il Perl, al suo interno, organizza e manipola i nostri dati.

Anatomia di una variabile

Sappiamo che il Perl è un linguaggio typeless, nel senso che le variabili non hanno un tipo dichiarato (numero, stringa, etc.) e possono essere considerate, in maniera del tutto trasparente, l'una o l'altra cosa.

Ciò è senz'altro vero, ma esistono tuttavia nel Perl (parliamo del codice C con il quale Perl è implementato) dei tipi di dato, e precisamente 3:

  • IV (Integer Value), un numero intero
  • NV (Numeric Value), un numero decimale
  • PV (Pointer Value), una stringa

Questi tipi (semplifichiamo di molto, ma per ora accontentatevi) corrispondono ai tipi del C int, double e char *.

Quindi, quando nel mio script uso la variabile $x, cos'è in realtà $x? Una di queste tre cose? Non direttamente: le variabili Perl sono SV (Scalar Value), quello che appunto siamo abituati a chiamare "scalare".

Ma di cosa è fatto un SV? Generalmente, di un IV, un NV o un PV, più qualcos'altro a contorno.

A questo punto conviene sospendere un attimo la teoria per vedere come si realizza in pratica quello che abbiamo detto.

Enter Devel::Peek

Per aiutarci nell'indagine dobbiamo fornirci di uno strumento immensamente utile per cominciare a sbirciare sotto le gonne di mamma Perl.

Devel::Peek, che è incluso nella libreria standard del Perl a partire dalla versione 5.6.0, fornisce fondamentalmente una funzione, Dump, che ci permette di passare un SV sotto i raggi X, vedendone così lo scheletro, la composizione interna.

Cominciamo subito con un esempio semplice semplice:

    use Devel::Peek;
    $x = 42;
    print Dump($x);

    SV = IV(0x1827db8) at 0x1823ffc
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 42

Ecco il nostro SV, ed eccolo il nostro IV, che vale 42. FLAGS e REFCNT sono il "contorno" di cui abbiamo accennato prima (quei numeri 0x... sono indirizzi di memoria, potete fare come se non ci fossero per ora).

Facciamo lo stesso con una stringa:

    use Devel::Peek;
    $x = "hello world";
    print Dump($x);
    
    SV = PV(0x2253bc) at 0x182401c
      REFCNT = 1
      FLAGS = (POK,pPOK)
      PV = 0x1828304 "hello world"\0
      CUR = 11
      LEN = 12

Un PV, come vediamo, è in realtà un po' più complesso di un semplice char *. CUR e LEN (più altre cosette qui non riportate) servono al Perl per gestire efficientemente l'allocazione di memoria per le stringhe (questa, però, è un'altra storia).

Ed ora, vediamo di complicare un po' le cose.

Tutti per uno, uno per tutti

Considerate il seguente codice:

    use Devel::Peek;
    $y = 42;
    $y = "$y";
    print Dump($y);

    SV = PVIV(0x2259bc) at 0x183e28c
      REFCNT = 1
      FLAGS = (POK,pPOK)
      IV = 42
      PV = 0x22b3f4 "42"\0
      CUR = 2
      LEN = 3

E questo PVIV cos'è? A occhio e croce sembra una cosa che è sia un PV che un IV. E strano a dirsi, è esattamente quel che sembra.

Quando il Perl si trova a dover effettuare delle conversioni fra tipi, sceglie (e questa è una sua caratteristica molto peculiare) di conservare in ogni caso il valore del tipo dal quale converte.

Un SV può avere quindi una doppia identità, numero e stringa. Il Perl tiene traccia nei FLAGS dell'SV di quale (o quali) fra queste identità è valida. Sono le "bandierine" che finiscono per OK: IOK indica che l'IV è valido, POK che il PV è valido e NOK che l'NV è valido (le flag con la p minuscola davanti sono private, quindi fate finta di non averle viste).

Nel nostro caso, dopo l'operazione solo il PV è valido (c'è POK ma non IOK). Ma ha senso una cosa del genere? Sì, se considerate il seguente codice:

    use Devel::Peek;
    $y = 42;
    $y = "$y";
    $y++;
    print Dump($y);

    SV = PVIV(0x2259bc) at 0x183e284
      REFCNT = 1
      FLAGS = (POK,pPOK)
      IV = 42
      PV = 0x22b3f4 "43"\0
      CUR = 2
      LEN = 3

Abbiamo "43" nel PV (l'operatore di auto-incremento è sul valore stringa) e 42 nell'IV, che non è, ovviamente, più valido.

Potrebbe a questo punto venirvi il dubbio: perché mantenere sia un IV che un PV per una sola variabile? Non è un inutile spreco di memoria? Tutt'altro che inutile: innanzitutto liberare memoria è un processo relativamente costoso, in termini di tempo macchina. In secondo luogo, Perl usa ovunque può le conversioni già fatte per "portarsi avanti col lavoro", in un certo senso.

Se al nostro codice aggiungiamo una riga:

    use Devel::Peek;
    $y = 42;
    $y = "$y";
    $y++;
    $y = 0+$y;
    print Dump($y);

    SV = PVIV(0x2259bc) at 0x183e28c
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 43
      PV = 0x22b3f4 "43"\0
      CUR = 2
      LEN = 3

Ecco che abbiamo riutilizzato lo stesso IV che avevamo già allocato.

Tornando a noi...

E' ora di tornare al nostro punto di partenza, eq e ==. Qualche sospetto a questo punto vi sarà forse già venuto, ma lasciate che enunci la cosa come si conviene.

Da un punto di vista semantico, proprio perché le variabili Perl sono typeless (né numero né stringa) acquistano un senso unicamente nel contesto in cui vengono utilizzate, ed è precisamente quello che fanno eq (contesto stringa) e == (contesto numerico).

Da un punto di vista "ontologico", gli operatori di comparazione non sono, come in altri linguaggi, semplici confronti sul valore di una cosa, ma implicano operazioni di conversione, e quindi alterano l'essenza del comparato.

Sorpresi? Beh, sarebbe carino da parte vostra esserlo, perché questo è il punto cruciale del mio articolo :-)

Vediamo dunque che succede quando utilizziamo questi operatori nel codice:

    use Devel::Peek;
    $z = 42;
    print Dump($z);
    1 if $z eq "42";
    print Dump($z);

    SV = IV(0x1828e58) at 0x184f848
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 42
    SV = PVIV(0x2259cc) at 0x184f848
      REFCNT = 1
      FLAGS = (IOK,POK,pIOK,pPOK)
      IV = 42
      PV = 0x22cb04 "42"\0
      CUR = 2
      LEN = 3

Notate la differenza fra prima e dopo l'if? L'operazione di confrontare $z con la stringa "42" l'ha trasformata in un PVIV (e in questo caso, è proprio sia un PV valido che un IV valido).

E tutto ciò è molto sensato. Se ad esempio la quarta riga del nostro ultimo esempio fosse:

    if($z eq "42") { print "Z=$z\n"; }

In questo caso, l'istruzione print si troverebbe $z già convertito in una stringa! Ovviamente, anche print, come eq, trasforma in PV le variabili che interpola nell'output.

Conclusioni

Spero che questo giro di montagne russe fra gli SV vi sia risultato in qualche modo utile. Se non altro, avete conosciuto un nuovo modulo (Devel::Peek) e avete qualche argomento in più da dare a chi vi chiede il perché di eq e ==.

Sappiate, inoltre, che abbiamo appena appena scalfito la superficie di un oceano, quello dei Perl Internals, in cui ci sarebbe da nuotare per anni e anni. Quando avrò trovato il prossimo pretesto, magari, comparirà una seconda puntata di questo articolo :-)

Intanto, se siete interessati, vi lascio qualche riferimento per continuare da soli le vostre indagini:

  • http://www.netthink.co.uk/downloads/internals/ un corso di Simon Cozens sui Perl Internals, uno dei migliori punti di partenza che possiate trovare.
  • http://gisle.aas.no/perl/illguts/ un corposo documento (con illustrazioni!) di Gisle Aas che spiega probabilmente più di quanto vogliate sapere.
  • e naturalmente la documentazione del vostro Perl! In particolare, le pagine perlguts, perlxstut, perlxs, perlcall e perlapi (non vi demoralizzate subito se vi sentite persi, è roba decisamente pesante :-).

cheers,
Aldo

Inviato da dada alle 15:45 | Commenti (3)

Permutazioni e Perl

L'altro giorno dree sul canale ci parlava della sua intenzione di assemblare un modulo scritto in C per fare le permutazioni. Subito incuriosito, non avendo fatto studi matematici particolari, (sono un ragioniere :) ho chiesto alla mia fidanzata (laurea scienze statistiche) cos'erano le permutazioni, e come al solito sono entrato in loop!.

Nel caso specifico dree parlava di permutazioni con ripetizione cioe' vale a dire, dati 'n' elementi trovare tutte le loro combinazioni con una lunghezza pari a 'x' posizioni:

Es.
elementi: a b c
posizioni: 2
risultato: ba ca ab cb ac bc

Dopo innumerevoli paturnie mentali, la soluzione che ho trovato e che vi volevo presentare sfrutta il sistema con il quale si conta normalmente, in qualsiasi base numerica (binaria, ottale , decimale etc).

Infatti se noi immaginiamo che a,b,c corrispondono alle unita' numeriche e vogliamo "contare" sostituendo queste lettere ai numeri avremmo:

a
b
c
aa
ab
ac
ba
bb
bc
ca
cb
cc
etc

ovviamente essendo gli elementi "tre" avremo un sistema a base 3 quindi le soluzioni ottenibili nel caso di 2 posizioni sono 3^2 + 3^1 cioe' 12.

Su questo principio ho scritto poche righe di codice che "contassero", sostituendo i numeri ai caratteri della stringa che viene passata come parametro.

Lanciato il programma la prima volta pero' mi accorsi subito che c'era qualcosa che non andava. Infatti come per i numeri alcune "unita'" venivano ovviamente ripetute.

Osservando infatti l'esempio sopra riportato, vi sono "aa" "bb" etc casi non richiesti dal problema. Non e' stato difficile pero' individuare queste eccezioni e saltare il ciclo senza considerarle.

I matematici mi scuseranno se ho utilizzato nomenclature matematiche in modo inappropriato. Immagino che esista un metodo scientifico apposito per calcolare le permutazioni, ma volevo comunque riportavi la mia soluzione.

Lanciare lo script passando come primo parametro la stringa e come secondo le posizioni.

#! /usr/bin/perl
$p[$i++]=$_ for split //,$ARGV[0];
until ($n[$ARGV[1]])
 {
  $n[0]++; $r="";
  for (0..$ARGV[1]-1)
   {
    if ($n[$_]==$i) { $n[$_]=0; $n[$_+1]++;}
    $r.= ($n[$_]?$n[$_]:"0").":";
   }
  if ($r!~ /(\d+)\:.*\1/)
   {
    print $p["$_"] for split /\:/,$r;
    print "\t";
   }
 }

Inviato da LucaS alle 19:09

That's Amore

sull'ultimo numero del Perl Journal (maggio 2004) nella sezione Perl News c'è un simpatico box che rende onore al nostro lavoro. mi permetto di citare integralmente il testo della notizia:

That's Amore

The Italian Perl Mongers have finished a labor of love: They've translated the entire Perl functions documentation (perlfunc.pod) into Italian. It's all available now at http://www.perl.it/documenti/perlfunc/index.html, along with an Italian translation of the Perl FAQ documents (perlfaq*.pod). The group plans to post a POD2::IT module to CPAN that will contain the translated documentation. Their work is part of the Italian Perldoc Translation Project, hosted on SourceForge at http://pod2it.sourceforge.net/.

non per fare dell'autocelebrazione, ma fa sicuramente piacere :-)

grazie di cuore a tutti, e continuiamo il nostro labor of love!

cheers,
Aldo

Inviato da dada alle 14:20 | Commenti (0)

D:
Progetti e documenti in rilievo
Corso di Perl Progetto pod2it
D:
La ML di Perl.it
mongers@perl.it è la lista ufficiale di Perl Mongers Italia per porre quesiti di tipo tecnico, per rimanere aggiornato su meeting, incontri, manifestazioni e novità su Perl.it.
Iscriviti!
D:
Annunci Google