Nei titoli e nei testi troverete qualche rimando cinematografico (ebbene si, sono un cinefilo). Se non vi interessano fate finta di non vederli, già che non sono fondamentali per la comprensione dei post...

Di questo blog ho mandato avanti, fino a Settembre 2018, anche una versione in Spagnolo. Potete trovarla su El arte de la programación en C. Buona lettura.

giovedì 8 dicembre 2016

L'ultimo degli Apache III - Il ritorno
come scrivere un modulo Apache in C - pt.3

Allora, facciamo il punto: negli ultimi tre post abbiamo parlato di moduli lighttpd (qui, qui e ancora qui). Nel primo dei tre avevo ricordato che l'argomento non era nuovo: anticamente avevo scritto due post (qui e qui) in cui si parlava di moduli per Apache, che è un parente stretto di lighttpd (famiglia Web Servers). Ecco, per chiudere in bellezza vi propongo un terzo episodio de "L'ultimo degli Apache" (o dei Mohicani?), con una (spero) interessante ampliazione di quello che si era scritto.
...veramente sono un Mohicano, non un Apache...
Bene, ne "L'ultimo degli Apache II - La vendetta" (che, avete appena riletto, immagino...) avevamo scritto un bel modulo elementare per Apache. Faceva poco (scriveva solo una presentazione nel browser) però era un buon inizio per entrare nel mondo dei moduli per Web Servers. Ecco, adesso riprenderemo quel modulo e gli aggiungeremo una funzione che estrae i dati POST inclusi in una eventuale petizione HTTP di tipo POST che arriva al modulo. Perché ho scelto di aggiungere proprio questa funzionalità? Beh, ovviamente perchè è abbastanza normale che un modulo tratti vari tipi di petizioni (GET, POST, ecc.), e poi, in particolare, ricordo che la prima volta che aggiunsi questa funzionalità in un modulo Apache che avevo scritto, notai che non erano disponibili molte indicazioni su questo argomento (e questo nonostante la enorme mole di documentazione Apache disponibile rispetto a quella di lighttpd).

Bene, senza stare a ripetere tutto il codice e la maniera di generarlo (c'è già tutto in "L'ultimo degli Apache II - La vendetta") riscriveremo solo la funzione myapmodHandler() e aggiungeremo una nuova funzione getPost(). Vai col codice!
// handler del modulo
static int myapmodHandler(
    request_rec *reqrec)            // request data
{
    // test handler
    if (! reqrec->handler || strcmp(reqrec->handler, "myapmod"))
        return DECLINED;

    // set del appropriato content type
    ap_set_content_type(reqrec, "text/html");

    // test metodo http
    if (reqrec->method_number == M_GET) {
        // petizione GET: scrive solo "Hello, World!"
        ap_rprintf(reqrec, "GET request: Hello, world!");
    }
    else if (reqrec->method_number == M_POST) {
        // petizione POST: legge POST data
        char *data;
        if (readPost(reqrec, &data) != -1) {
            // scrive "Hello, World!" e mostra POST data
            ap_rprintf(reqrec, "POST request: Hello, world! postdata: %s", data);
        }
        else {
            // POST data non disponibile: scrive solo "Hello, World!"
            ap_rprintf(reqrec, "POST request: Hello, world!");
        }
    }
    else
        return HTTP_METHOD_NOT_ALLOWED;

    // esce con OK
    return OK;
}

// funzione per leggere POST data
static int readPost(
    request_rec *reqrec,            // request data
    char        **data)             // buffer di destinazione per POST data
{
    // setup del client per permettera che Apache legga il request body
    if (ap_setup_client_block(reqrec, REQUEST_CHUNKED_ERROR) == OK) {
        // determina se il client ha spedito dati
        if (ap_should_client_block(reqrec)) {
            // legge i dati di POST
            char argsbuffer[HUGE_STRING_LEN];
            int rsize, len_read, rpos=0;
            long length = reqrec->remaining;
            *data = (char*)apr_pcalloc(reqrec->pool, length +1);

            // loop di inserzione dati nel buffer di destinazione
            while ((len_read = ap_get_client_block(reqrec, argsbuffer, sizeof(argsbuffer))) > 0) {
                if ((rpos + len_read) > length)
                    rsize = length - rpos;
                else
                    rsize = len_read;

                // copia un blocco di dati
                memcpy((char *)*data + rpos, argsbuffer, rsize);
                rpos += rsize;
            }

            // POST data letto: return OK
            return 0;
        }
    }

    // esce con NOK
    return -1;
}
Ok, come vedete è ampiamente commentato e quindi è auto-esplicativo, per cui non mi dilungherò sulle singole istruzioni e/o gruppi di istruzioni (leggete i commenti! sono li per quello!), ma aggiungerò, solo, qualche dettaglio strutturale.

L'originale funzione myapmodHandler() è diventata una sorta di funzione helloworld (beh, in pratica lo era anche prima), che distingue i tipi di petizione e, nel caso POST, chiama la funzione readPost() e scrive come risposta "Hello, world!" più i dati. Nel caso di petizioni GET o di petizioni POST senza dati si limita a scrivere "Hello, world!", e per altri tipi di petizione esce con errore.

La funzione readPost() è semplice ma non immediata: a suo tempo la derivai dall'unico esempio interessante e ben fatto che trovai, e che stava nel (ottimo) libro "Writing Apache Modules with Perl and C", che vi raccomando. Direi che nell'ultima versione di Apache, la 2.4, sono stati introdotti (e descritti qui) altri metodi di estrazione dei dati POST (vi auguro una buona lettura!), comunque io me l'ero cavata così, e il modulo funzionava (e funziona tutt'ora) benissimo.

Ah, il test, dimenticavo! Testare con petizioni POST non è semplice come farlo con le GET, dove è sufficiente (come detto nel post precedente) scrivere la URI del modulo nella barra degli indirizzi di Firefox o Chrome e aspettare la risposta. Per testare il nostro nuovo modulo è meglio usare un bel plugin del browser (tipo Poster) per semplificare il lavoro.

Va bene, per il momento credo che possiamo fermare per un tempo l'argomento moduli Apache/lighttpd. Giuro che nel prossimo post parlerò d'altro, non vorrei che pensaste che il C si usa solo per i Web Servers...

Ciao e al prossimo post!

sabato 5 novembre 2016

Il grande lighttpd
come scrivere un modulo lighttpd in C - pt.3

    Drugo: E se poi quello se la prende?   
    Bunny: A lui non importa niente di niente, è un nichilista.
    Drugo: Ah, dev’essere faticoso da morire.

Ebbene si, siamo faticosamente arrivati al capitolo finale, oggi si scrive il codice del nostro mini-modulo lighttpd. Uff, che fatica per il Drugo...
...che fatica...
Ma prima vediamo un attimo come è strutturato un modulo. Eccovi un piccolo schema esemplificativo (estratto dal nostro mod_helloworld.c creato nel post precedente: rileggere subito il post precedente!):
// init the plugin data
INIT_FUNC(mod_helloworld_init) {
    ...
}

// destroy the plugin data
FREE_FUNC(mod_helloworld_free) {
    ...
}

// handle plugin config and check values
SETDEFAULTS_FUNC(mod_helloworld_set_defaults) {
    ...
}

// handle uri
URIHANDLER_FUNC(mod_helloworld_uri_handler) {
    ...
}

// this function is called at dlopen() time and inits the callbacks
int mod_helloworld_plugin_init(plugin *p) {
    p->version          = LIGHTTPD_VERSION_ID;
    p->name             = buffer_init_string("helloworld");
    p->init             = mod_helloworld_init;
    p->handle_uri_clean = mod_helloworld_uri_handler;
    p->set_defaults     = mod_helloworld_set_defaults;
    p->cleanup          = mod_helloworld_free;
    p->data             = NULL;
    return 0;
}
Come si nota si usano delle macro (gentilmente fornite dall'ambiente di sviluppo lighttpd) per creare delle callback con cui inizializzare una struttura plugin che poi lighttpd userà per gestire il nostro modulo ogni qualvolta arrivi una petizione HTTP che ne richieda l'uso. Per chi vuole approfondire l'argomento callback è già stato ampiamente descritto qui e qui (immagino abbiate già letto quei post... o no?).

Allora, che parti del nostro modulo dobbiamo modificare per fare in modo che, usandolo, ci scriva "Hello, world!" nel browser? Visto che il problema è abbastanza semplice anche il codice da scrivere non sarà molto voluminoso e complesso: ci basterà modificare solo la callback mod_helloworld_uri_handler nella seguente maniera:
URIHANDLER_FUNC(mod_helloworld_uri_handler)
{
    plugin_data *p = p_d;
    UNUSED(srv);

    // test modo (return se errore)
    if (con->mode != DIRECT)
        return HANDLER_GO_ON;

    // test uri path (return se errore)
    if (con->uri.path->used == 0)
        return HANDLER_GO_ON;

    mod_helloworld_patch_connection(srv, con, p);

    // test handler (return se errore)
    if (con->uri.path->ptr && strstr(con->uri.path->ptr, "helloworld")) {
        // scrive buffer
        buffer *b = chunkqueue_get_append_buffer(con->write_queue);
        BUFFER_APPEND_STRING_CONST(b, "<big>Hello, world!</big>");

        // send header
        response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
        con->http_status = 200;
        con->file_finished = 1;

        // handling finito
        return HANDLER_FINISHED;
    }
    else
        return HANDLER_GO_ON;
}
Ok, come vedete è ampiamente commentato e quindi è auto-esplicativo, per cui non mi dilungherò sulle singole istruzioni e/o gruppi di istruzioni (leggete i commenti! sono li per quello!), ma aggiungerò, solo, qualche dettaglio strutturale.

Alcune parti sono rimaste identiche alla callback originale (ma ho aggiunto dei commenti), e sono tipiche della struttura del codice di lighttpd: per non complicare troppo l'argomento non mi dilungherò su queste parti. Comunque fidatevi perché lighttpd è scritto e funziona come si deve.

La parte veramente nuova che ho scritto è quella che inizia col commento "test handler (return se errore)", dove si testa se la URI passata al browser contiene il nome del modulo e, in quel caso, esegue quello che vogliamo: copia in un buffer la scritta "Hello, world!" e la invia come risposta alla petizione. Quindi se noi scriviamo nella barra degli indirizzi di Firefox, Chrome, Opera (o qualsiasi altro browser meno IE, per favore...)
http://127.0.0.1/helloworld
avremo come risposta nel browser:
Hello, world!
Ho scritto 127.0.0.1 (e si poteva scrivere anche localhost) perché, ovviamente, il primo test lo faremo in locale. Se poi avete voglia di aprire la vostra macchina al mondo esterno e usarla come un vero Web Server (in una rete locale o nel WWW) potrete ripetere il test usando il vostro indirizzo IP pubblico e otterrete lo stesso risultato. Il modulo funziona!

Il codice appena mostrato l'ho scritto e provato con lighttpd 1.4.33. Se usate una versione più recente (dalla 1.4.36 in avanti) scoprirete che qualcosa è cambiato a livello di gestione dei buffer, quindi ad esempio le linee:   
if (con->uri.path->used == 0)
...
buffer *buf = chunkqueue_get_append_buffer(con->write_queue);
devono diventare:   
if (buffer_is_empty(con->uri.path))
...
buffer *buf = buffer_init();
ma questi sono dettagli. In futuro, come promesso, vi proporrò qualche modulo più sofisticato, con modifiche anche alle altre callback del modulo, ma questo per il momento può bastare, non gettiamo troppa carne al fuoco, se no ci stanchiamo come il Drugo...

Ciao e al prossimo post!

domenica 9 ottobre 2016

Il grande lighttpd
come scrivere un modulo lighttpd in C - pt.2

Drugo: Sai, questo... questo è un caso molto, molto complicato, Maude. Un sacco di input e di output. Sai, fortunatamente io rispetto un regime di droghe piuttosto rigido per mantenere la mente, diciamo, flessibile.
Allora: la buona notizia è che per scrivere un modulo lighttpd non è necessario seguire lo stesso regime del Drugo... quella cattiva è che ci vuole un certo impegno, ma sarà molto più semplice dopo aver letto questo post (e il precedente, ovviamente... non l'avete ancora letto? Ma questa è la seconda parte!).
faccia da "Ora so come si scrive un modulo lighttpd!"
Allora, dove eravamo rimasti? Abbiamo installato lighttpd sul nostro sistema e abbiamo creato un ambiente di sviluppo scaricando e decomprimendo il tar di installazione trovato sul sito ufficiale di lighttpd (ricordarsi di creare l'ambiente usando la stessa versione di quella già installata sul sistema). La compilazione e installazione della versione scaricata ha dato (spero) buon esito, quindi siamo pronti per scrivere il nostro modulo.

Per prima cosa scegliamo un nome e un azione... sarà un classico: lo chiameremo mod_helloworld e il modulo scriverà nel browser la frase "Hello, world!" (molto, ma molto, originale!).

Apriamo un terminale Linux, entriamo nella root-directory dell'ambiente di sviluppo ed eseguiamo i seguenti comandi (che ho numerato per descriverli uno a uno):
 1. sudo gedit /etc/lighttpd/lighttpd.conf
 2. gedit src/Makefile.am     
 3. cp src/mod_skeleton.c src/mod_helloworld.c 
 4. gedit src/mod_helloworld.c    
 5. ./autogen.sh
 6. ./configure
 7. make
 8. sudo make install
 9. sudo cp /usr/local/lib/mod_helloworld.so /usr/lib/lighttpd
10. sudo /etc/init.d/lighttpd restart
Allora: con 1. editiamo (con gedit, pluma, vim, geany... o quello che preferite) il file di configurazione di lighttpd per aggiungere alla lista dei moduli installati il nostro nuovo modulo: cercate la lista "server.modules" e inserite, dopo l'ultimo modulo listato, il nuovo. Fatto ciò l'aspetto della lista sarà del tipo:
server.modules = (
    "mod_access",
    "mod_alias",
    "mod_compress",
    "mod_redirect",
    "mod_helloworld",
)
Con 2. editiamo il file Makefile.am del package. Rispetto al package originale stiamo aggiungendo un modulo, quindi bisogna cercare l'ultimo modulo standard presente (normalmente è mod_accesslog). Troveremo quattro linee così:
lib_LTLIBRARIES += mod_accesslog.la
mod_accesslog_la_SOURCES = mod_accesslog.c
mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_accesslog_la_LIBADD = $(common_libadd) 
Copiamo queste linee e le replichiamo immediatamente sotto, sostituendo nelle NUOVE quattro linee mod_accesslog con mod_helloworld. Salviamo e usciamo: ora il nostro Makefile.am è pronto per gestire anche il nuovo modulo. Le linee aggiunte saranno queste:
lib_LTLIBRARIES += mod_helloworld.la
mod_helloworld_la_SOURCES = mod_helloworld.c
mod_helloworld_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_helloworld_la_LIBADD = $(common_libadd)
Con 3. copiamo il file mod_skeleton.c in un nuovo file che chiameremo mod_helloworld.c: mod_skeleton.c è un template file contenente lo scheletro base di ogni modulo: questo file ce lo mettono gentilmente a disposizione gli ottimi sviluppatori di lighttpd per facilitarci la scrittura di nuovi moduli.

Con 4. editiamo mod_helloworld.c e, con un comando di sostituzione globale, cambiamo tutte le ricorrenze della parola skeleton in helloworld. Per il momento salviamo così il file e usciamo dall'editor.

Con la sequenza 5.6.7.8. (vista già nel post precedente, ricordate?) compiliamo e installiamo il nuovo modulo. È possibile che nella fase 6. (quella di configurazione) il processo vi dica che non trova alcune librerie: normalmente l'errore si risolve usando:
sudo apt-get install libpcre3-dev libbz2-dev
e ripetendo, poi, il punto 6.

Con 9. copiamo il nuovo modulo generato (che ha la forma di una libreria dinamica, una .so) nella directory di sistema che contiene i moduli lighttpd.

Con 10., finalmente, riavviamo lighttpd e, se non ci appare nessun errore il nostro Web Server è già pronto per usare il nuovo modulo (che però, per il momento, è semivuoto: come detto sopra è solo lo scheletro di un modulo).

Cosa ci manca a questo punto? Beh, ovviamente, dobbiamo aggiungere un po' di codice nel nostro modulo per fargli fare qualcosa (nel nostro caso mostrarci un bel "Hello, world!"). Ma questo lo vedremo nella prossima puntata, e, come sempre, non trattenete il respiro nell'attesa...

Ciao e al prossimo post!