Maude: E di cosa ti occupi nel tempo libero?Sottinteso che per scrivere del buon Software non c'è bisogno di imitare le abitudini del mitico Drugo del Grande Lebowski, ritorniamo sull'argomento lighttpd, perchè nel terzo post della serie (qui, qui e ancora qui) avevo promesso di ampliare il discorso. Ebbene è venuto il momento, le promesse si mantengono!
Drugo: Mah, le solite cose: Bowling, un giro in macchina, un trip d'acido quando capita...
...Mah, le solite cose: Bowling, un giro in macchina, un modulo lighttpd quando capita... |
Bene, senza stare a ripetere tutto il codice e la maniera di generarlo, riscriveremo solo la funzione mod_helloworld_uri_handler() e aggiungeremo una nuova funzione getPost(). Premetto che, per integrare il codice che ho scritto, è necessario seguire la guida di installazione indicata in "Il grande lighttpd - pt.1" e ripetere tutti i 10 passi indicati in "Il grande lighttpd - pt.2", usando questa volta la versione 1.4.45 di lighttpd (occhio: se usate una versione più vecchia il codice che sto per mostrarvi non funziona). Fatto? Allora vai col codice!
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) size_t s_len = buffer_string_length(con->uri.path); if (s_len == 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")) { // prepara il buffer per la risposta buffer *buf = buffer_init(); // test metodo http if (strstr(con->request.request->ptr, "GET")) { // metodo GET: scrive risposta buffer_append_string(buf, "<big>Hello, world!</big>"); } else if (strstr(con->request.request->ptr, "POST")) { // metodo POST: controlla la presenza di post-data size_t len = con->request.content_length; if (len) { // post-data presenti; li legge per scrivere la risposta char *data = malloc(len + 1); if (readPost(con, data, len)) { // scrive la risposta buffer_append_string(buf, data); } // libera la memoria free(data); } else { // errore: messaggio POST senza post-data buffer_append_string(buf, "mod_helloworld: errore: POST senza post-data"); } } else { // errore: messaggio con metodo non trattato buffer_append_string(buf, "mod_helloworld errore: metodo non trattato"); } // scrive il buffer e lo libera chunkqueue_append_buffer(con->write_queue, buf); buffer_free(buf); // spedisce l'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; } // handler non trovato return HANDLER_GO_ON; } static bool readPost( connection *con, // dati connessione char *data, // buffer destinazione per post-data size_t len) // lunghezza data (senza terminatore) { // set valore di return di default int retval = false; // legge post-data (nello stream di chunks) size_t rpos = 0; chunkqueue *cq = con->read_queue; for (chunk *mychunk = cq->first; mychunk; mychunk = cq->first) { // calcola il size del buffer corrispondente al chunk di post-data corrente size_t n_tocopy = buffer_string_length(mychunk->mem) - mychunk->offset; // test se ci sono dati da copiare if (n_tocopy <= (len - rpos)) { // copia un chunk e set della posizione di copia del prossimo chunk memcpy(data + rpos, mychunk->mem->ptr + mychunk->offset, n_tocopy); rpos += n_tocopy; // é stato letto (almeno) un chunk di post-data: set retval=true retval = true; } else { // buffer overflow: forzo uscita dal loop break; } // segnalo come letto il chunk di post-data corrente chunkqueue_mark_written(cq, chunkqueue_length(cq)); } // aggiungo il terminatore di stringa (il buffer é lungo len+1) data[len] = 0; // esco con retval return retval; }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 mod_helloworld_uri_handler() è ancora una sorta di funzione "Helloworld", ma ora distingue i tipi di petizione e, nel caso POST, chiama la funzione readPost() e scrive come risposta "Hello, world!" più i dati POST. Nel caso di petizioni GET si limita a scrivere "Hello, world!" e, con altri tipi di petizione o con petizioni POST senza dati, scrive un messaggio di un errore.
La funzione readPost() è semplice ma non immediata: vista la scarsezza quasi totale di esempi disponibili in rete ho dovuto, praticamente, fare reverse engineering sugli (ottimi) moduli integrati nella distribuzione (avere il codice sorgente originale a disposizione è sempre una cosa ottima!) in maniera di dedurre come fare quello che mi ero prefisso. Il risultato finale è (modestia a parte) buono, sia come aspetto (stile) sia come prestazioni (ho fatto dei test e funziona bene).
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 (o qualsiasi altro browser meno IE, per favore...) 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 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!
Nessun commento:
Posta un commento