Capitolo 6.   Moduli e programmi CGI

I moduli si definiscono in un documento HTML con il tag <form> ma la loro gestione, o meglio, la gestione dei dati in essi contenuti, non avviene con gli strumenti dell'HTML ma grazie a dei programmi esterni chiamati programmi o script CGI.

Occorre quindi esaminare due aspetti distinti:

Più in dettaglio, la corretta gestione di un modulo passa attraverso le seguenti fasi:

La figura 6.1 schematizza le fasi appena illustrate.

Figura 6.1.

figure/pagine-web-schema-cgi

I CGI si differenziano in due categorie in base al linguaggio di programmazione utilizzato per realizzarli:

Nel proseguo usiamo indifferentemente il termine «programma CGI», o anche solo CGI, per riferirci ad entrambe le tipologie di strumenti.

Nei prossimi paragrafi esaminiamo le due fasi della gestione di un modulo: la sua definizione in HTML e l'elaborazione dei dati con i programmi CGI.

6.1   Creazione di moduli HTML

I moduli iniziano con il tag <form> e terminano con </form>; si tratta di un elemento di blocco che può contenere qualsiasi altro elemento ma non altri moduli; i suoi attributi sono:

Esistono poi i cosiddetti attributi di evento come onmouseover, onclick, onsubmit, onreset che però vengono solitamente gestiti con gli script attivi.

Gli attributi più importanti sono action e method.

Il valore di action corrisponde all'indirizzo a cui inviare i dati; può essere o un indirizzo di posta elettronica o il nome del programma CGI (completo di percorso) che dovrà ricevere ed elaborare in qualche modo i dati del form.

Il valore di method può essere "get" oppure "post"; nel primo caso i dati vengono accodati all'URI indicato in action, nel secondo caso i dati vengono passati attraverso lo «standard input» al servente.

Questi aspetti vengono ripresi e approfonditi nel paragrafo 6.8 relativo al passaggio dei dati di un modulo a un programma CGI.

Riguardo all'attributo enctype è opportuno soffermarsi brevemente sui tipi MIME (Multipurpose Internet Mail Extension) creati originariamente per descrivere gli allegati di posta elettronica e ora usati, più generalmente, per riconoscere la natura dei file presenti sul Web.

Sono composti da due parti: «tipo-principale/tipo-specifico», come ad esempio: image/gif (immagini di tipo gif), text/plain (testo puro) e molti altri.

Elenchi completi di tipi MIME si trovano facilmente in Internet, ad esempio all'indirizzo <http://www.asciitable.it/mimetypres.asp>.

Il tipo di MIME da usare per trasferire i dati dal modulo al servente Web è: application/x-www-form-urlencoded e deve essere specificato solo nel caso si usi il metodo "post"; questo valore è comunque quello attribuito per difetto all'attributo enctype.

In pratica serve a comunicare al servente che i dati saranno inviati attraverso lo standard input ma codificati alla stessa maniera di quando sono inviati accodati all'URI.

6.2   Controlli dei moduli

Gli elementi specifici usati all'interno dei moduli sono tag di testo chiamati «controlli»; i più importanti sono:

I primi tre prevedono l'attributo name che permette di indicare il nome della variabile o del campo che contraddistingue l'elemento in questione.

Talvolta si utilizza anche l'attributo value per assegnare un valore alla variabile o al campo; più spesso però il valore assunto corrisponde a ciò che l'utente ha digitato o scelto in corrispondenza di quel campo.

Il nome ed il valore dei campi sono naturalmente di fondamentale importanza per il programma CGI che deve elaborare i dati inviati dal modulo; in pratica siamo in presenza della distinzione tra nome e contenuto di una variabile, ben nota a chi conosca i rudimenti della programmazione.

6.2.1   Elemento input

Vediamo prima di tutto un esempio molto semplice di modulo con un campo di input:

<html>
<head>
<title>Esempio di modulo</title>
</head>
<body>
<h4>ESEMPIO DI INPUT CON RICHIAMO DI PROGRAMMA CGI</h4>
<form action="/cgi-bin/cgi-prova.sh" method="get">
<p/><br/><b>Inserire dato da passare al programma&nbsp;&nbsp;</b>
<input type="text" name="datoin" size="5" maxlentgh="3"/>
<input type="submit" value="Invio"/>
</form>
</body>
</html>

L'aspetto del modulo è quello mostrato in figura 6.3.

Figura 6.3.

figure/pagine-web-ese0016

In questo esempio si chiede l'inserimento di un dato che viene passato con metodo "get" al programma "cgi-prova.sh" (presumibilmente scritto con i comandi della shell di GNU/Linux o Unix, data l'estensione .sh) residente nella directory /cgi-bin.

Il dato viene identificato come "datoin" dal programma, viene immesso in una casella lunga cinque caratteri ma può essere lungo al massimo tre caratteri.

L'invio dei dati avviene premendo sul pulsante definito dal controllo "submit"; in questo esempio tale controllo non ha l'attributo name che può servire nel caso si debbano distinguere più pulsanti di invio presenti in uno stesso modulo.

Tra gli attributi di <input> il più importante è type, il cui valore permette di definire vari tipi di controlli:

Nei prossimi due esempi sono presenti tutti i controlli della lista ancora non esaminati (ad eccezione di "button" che viene usato in seguito) nonché altri possibili attributi del tag <input>:

<html>
<head><title>Esempio con controlli vari</title></head>
<body>
<div align="center"><b>Esempio con controlli vari</b></div>
<p/>
<form action="/cgi-bin/cgi-prova.sh" method="get">
<input type="hidden" name="nascosto" value="aaa"/>
<p>Fascia di età:</p>
<p><input type="radio" name="fascia" value="0-15"/>0-15</p>
<p><input type="radio" name="fascia" value="16-30" checked/>16-30</p>
<p><input type="radio" name="fascia" value="30-50"/>30-50</p>
<p><input type="radio" name="fascia" value="50-99"/>50-99</p>
<p/>
Seleziona i tuoi campi di interesse:
<p/>
<input type="checkbox" name="int" value="computer" checked/>&nbsp;Computer
<input type="checkbox" name="int" value="musica"/>&nbsp;Musica
<input type="checkbox" name="int" value="sport"/>&nbsp;Sport
<p/>
Inserisci la password:&nbsp;
<input type="password" name="psw" size="8" maxlength="8"/>
<p/>
<input type="image" src="tux.jpg" width="50" height="55"
title="Premi qui per inviare i dati" alt="Invio" align="right"/></p>
<p><input type="reset" value="Pulisci campi"/></p>
</form>
</body>
</html>

L'aspetto di questo modulo è quello della figura 6.5.

Figura 6.5.

figure/pagine-web-ese0017

Alcuni degli attributi utilizzati in questo esempio sono di immediata comprensione, anche perché già visti in precedenza (come src, alt, align).

L'attributo checked viene usato in caso di controlli che prevedono più alternative per preimpostarne una o alcune.

I controlli "hidden" possono essere utili nel caso si debbano inviare dei dati fissi al programma CGI senza che l'utente possa vederli e tantomeno variarli.

L'invio dei dati del modulo avviene cliccando sull'immagine che appare in fondo a destra.

<html>
<head><title>Esempio con trasferimento di file</title></head>
<body>
<div align="center"><b>Trasferimento di file</b>
</div>
<p/>
<form enctype="multipart/form-data" action="/cgi-bin/cgi-prova.sh">
<p/>
File da inviare:&nbsp;<input type="file" name="file"/>
<p/>
<input type="submit" size="5" value="Invio"/>
</form>
</body>
</html>

Questo modulo ha l'aspetto mostrato in figura 6.7.

Figura 6.7.

figure/pagine-web-ese0018

Il controllo "file" permette di inviare un file al programma CGI; presenta un bottone "Browse" o "Sfoglia", inserito automaticamente, per permettere la ricerca del file nell'ambito della macchina locale.

Si noti che in questo contesto si deve valorizzare l'attributo enctype con "multipart/form-data".

6.2.2   L'elemento select

Con il tag <select> si crea un menu a cascata le cui opzioni sono indicate ognuna con un tag <option> e la cui definizione si conclude con </select>.

Il tag <select> prevede obbligatoriamente l'attributo name; altri attributi utilizzabili in questo contesto sono:

6.2.3   L'elemento textarea

Con il tag <textarea> si può creare una casella di testo contenente più righe.

Si deve chiudere con </textarea> ed è obbligatorio l'attributo name.

Ci sono anche gli attributi rows e cols con i quali si indica l'ampiezza della casella di testo senza però limitare la quantità di caratteri inseribili; quasi tutti i browser infatti inseriscono le barre di scorrimento a sinistra ed in basso in caso la quantità di caratteri immessi superi la dimensione della casella.

Si può inoltre definire un testo pre-inserito semplicemente scrivendolo tra i tag <textarea> e </textarea>.

Nel seguente esempio viene mostrato l'uso di un menu a cascata e di una casella di testo.

<html>
<head><title>Esempio con menu a cascata e casella di testo</title>
</head>
<body>
<div align="center"><b>Esempio con menu a cascata e casella di testo</b>
</div>
<p/>
<form action="/cgi-bin/cgi-prova.sh" method="post">
Componenti da acquistare:<p/>
<select name="comp" multiple size="6">
<option selected>Main board</option>
<option selected>Cpu</option>
<option selected>Ram</option>
<option>Floppy disk 120 Mb</option>
<option>Floppy disk 1,44 Mb</option>
<option>Hard disk IDE</option>
<option>Hard disk SCSI</option>
<option>Scheda video</option>
<option>Scheda audio</option>
</select>
<p/>
Inserire un commento<p/>
<textarea name="commento" rows="5" cols="50">
Testo inserito preventivamente
</textarea>
<p/>
<input type="submit" value="Invio"/>
</form>
</body>
</html>

Il modulo che si ottiene è quello della figura 6.9.

Figura 6.9.

figure/pagine-web-ese0019

6.2.4   Raggruppamento e ordinamento dei controlli di un modulo

Con l'elemento <fieldset> si possono raggruppare, incorniciandoli automaticamente, più controlli di un modulo, tutti quelli presenti fino al tag di chiusura </fieldset>; all'interno si può usare il tag <legend> per aggiungere una legenda posizionandola con l'attributo align.

Resta infine da segnalare l'attributo tabindex, utilizzabile per tutti i controlli di un modulo, con il quale si può cambiare l'ordine di selezione degli stessi assegnandogli un valore intero.

Valori alti hanno la precedenza rispetto a quelli bassi o negativi; in caso di assenza di questo attributo, l'ordine di selezione dei controlli corrisponde a quello di inserimento all'interno del modulo: dall'alto in basso e da sinistra verso destra.

6.3   L'attributo accesskey

L'attributo accesskey può essere usato con i controlli <input>, <textarea>, <button> e anche con i tag per i collegamenti <a> e <area>; il suo scopo è quello di definire una «scorciatoia» da tastiera per attivare il controllo o il collegamento.

Il suo uso è molto banale, come mostrato nel seguente esempio riguardante un controllo per l'invio dei dati di un modulo:

<input type="submit" value="[I]nvio" accesskey="I"/>

In questo modo diventa possibile attivare l'invio dei dati, oltre che come di consueto attraverso il mouse, anche con la tastiera; la modalità di selezione della scorciatoia dipende dal browser utilizzato.

Nel nostro esempio, in cui la scorciatoia è il carattere «I», se usiamo Firefox si devono premere i tasti [Shift+Alt+I], se usiamo Internet Explorer i tasti [Alt+I].

6.4   Prerequisiti per l'uso di CGI (cenni alla configurazione di Apache)

Gli esempi di pagine Web mostrati finora possono essere visualizzati, con un programma di navigazione, anche semplicemente aprendoli come file locali (usando quindi il «protocollo» file://); da questo momento le pagine devono invece essere richieste ad un servente Web (con http://) in modo che possano essere correttamente eseguiti i CGI richiamati dai moduli presenti in esse.

Inoltre è necessario che sul servente sia possibile eseguire i programmi nel linguaggio di programmazione scelto per realizzare i CGI (ad esempio se si scrivono in linguaggio Perl, deve essere installato l'interprete di questo linguaggio).

In questo paragrafo e nei successivi si fa riferimento al servente Web Apache (usato in circa i due terzi dei siti Web esistenti), su piattaforma GNU/Linux.

6.4.1   Configurazione di Apache

Viene presa in considerazione la versione più recente del programma, cioè la 2 e si suppone di utilizzare una distribuzione GNU/Linux derivata da Debian (nel nostro caso Ubuntu-7.04).

Nel proseguo, anche in paragrafi successivi, si fa spesso riferimento alla documentazione di alcuni pacchetti software di GNU/Linux o al manuale in linea di certi comandi; può allora essere utile chiarire quanto segue:

  • il manuale in linea relativo a un comando si ottiene eseguendo:

    man nome_comando

    si scorre il testo una riga alla volta con i tasti freccia e il tasto [Invio]; e una pagina alla volta con la barra spaziatrice e i tasti [pagina su] e [pagina giu]; si possono effettuare ricerche di testo premendo [shift /] e inserendo la stringa da cercare seguita da [Invio] e dalla pressione di [n] per trovare successive occorrenze; si esce dal manuale premendo [q];

  • i file di documentazione dei pacchetti installati in un sistema GNU/Linux sono solitamente in /usr/share/doc/nome_pacchetto;

  • gli «HOWTO» e le FAQ (Frequently Asked Questions) sono rispettivamente in /usr/share/doc/HOWTO e /usr/share/doc/FAQ.

Si tenga presente che sulla configurazione e l'uso di Apache sono stati scritti interi volumi; qui ci accontentiamo di fornire le informazioni necessarie al suo funzionamento su un singolo nodo di rete (localhost o numero IP 127.0.0.1), contesto sufficiente per i nostri scopi, e alla configurazione richiesta per l'esecuzione di programmi CGI.

Quello che segue presuppone il possesso dei diritti dell'utente amministratore di un sistema GNU/Linux cioè dell'utente root.

In Ubuntu, dopo l'installazione del sistema, esso non è abilitato, ma l'utente definito in sede di installazione appartiene al gruppo dei cosiddetti utenti sudoers cioè abilitati, previo reinserimento della loro password quando richiesto, ad eseguire comandi con i privilegi dell'utente amministratore.

Si può allora operare come segue per attivare un terminale nell'ambiente grafico in cui eseguire i comandi come utente root:

  1. premere [Alt+F2] per ottenere l'interfaccia di esecuzione comandi;

  2. scrivere nella riga di comando:

    gksudo gnome-terminal

    e cliccare su «esegui»;

  3. rispondere alla richiesta di password dell'utente corrente.

Nella figura 6.11 vediamo il modulo per l'esecuzione dei comandi con inserito il comando appena citato.

Figura 6.11.

figure/pagine-web-sudo-root1

Nella figura 6.12 vediamo il successivo modulo per l'inserimento della password.

Figura 6.12.

figure/pagine-web-sudo-root2

Infine nella figura 6.13 vediamo il terminale aperto in cui si possono eseguire i comandi con i privilegi di root (lo si capisce dal fatto che il prompt dei comandi termina con il carattere # anziché con $).

Figura 6.13.

figure/pagine-web-sudo-root3

La prima operazione da svolgere è naturalmente l'installazione del pacchetto Apache2; si consiglia di utilizzare le versioni già pronte per la distribuzione utilizzata e, nel caso di Ubuntu, di procedere con uno dei programmi dedicati all'aggiornamento dei pacchetti software, come Synaptic (in ambiente grafico) o apt-get (in ambiente testuale nel terminale in cui si hanno i diritti dell'amministratore).

Per maggiori dettagli si consultino manuali e altra documentazione, anche in linea, relativa a questi programmi o in generale all'aggiornamento dei pacchetti software in GNU/Linux e in Ubuntu in particolare.

Alla fine dell'installazione il programma ha già una configurazione sufficiente per poter essere utilizzato e viene anche inserito tra i cosiddetti «demoni» (programmi costantemente in esecuzione, in background, cioè «dietro le quinte», e pronti a rispondere a richieste di servizi provenienti dalla stessa macchina o da altre collegate in rete) che vengono attivati automaticamente ad ogni avvio della macchina.

Se comunque fosse necessario avviare manualmente il demone di Apache2 si può provvedere con il seguente comando:

apache2ctl start

Invece per fermarlo:

apache2ctl stop

Per riavviarlo:

apache2ctl restart

Per verificarne lo stato:

apache2ctl status

I file di configurazione del servente Apache2 risiedono nella directory /etc/apache2/; il file più importante è apache2.conf.

Ci sono poi le sottodirectory mods-available, site-available, mods-enabled, site-enabled; nelle prime due sono presenti i file di configurazione rispettivamente dei moduli aggiuntivi e dei siti gestibili, nelle altre due i file di configurazione (che non sono altro che dei collegamenti simbolici a file delle altre due sottodirectory) rispettivamente dei moduli e dei siti abilitati.

Per gli scopi che ci prefiggiamo, nel file /etc/apache2/apache2.conf non è necessario alcun intervento; di seguito riportiamo un piccolo estratto di tale file.

      1 #
      2 # ServerRoot: The top of the directory tree under which the server's
      3 # configuration, error, and log files are kept.
      4 #
      5 # NOTE!  If you intend to place this on an NFS (or otherwise network)
      6 # mounted filesystem then please read the LockFile documentation (available
      7 # at <URL:http://httpd.apache.org/docs-2.1/mod/mpm_common.html#lockfile>);
      8 # you will save yourself a lot of trouble.
      9 #
     10 # Do NOT add a slash at the end of the directory path.
     11 #
     12 ServerRoot "/etc/apache2"
     13 
     14 User www-data
     15 Group www-data
     16 
     17 # Include module configuration:
     18 Include /etc/apache2/mods-enabled/*.load
     19 Include /etc/apache2/mods-enabled/*.conf
     20 
     21 # Include the virtual host configurations:
     22 Include /etc/apache2/sites-enabled/

La numerazione delle righe non è presente nel file ed è stata aggiunta per spiegarne più agevolmente il contenuto:

Nella directory /etc/apache2/sites-enabled troviamo il file 000-default che è un collegamento simbolico al file /etc/apache2/sites-available/default e che contiene le configurazioni necessarie per la gestione del sito predefinito.

I collegamenti simbolici sono ulteriori riferimenti a file esistenti nel file system; in GNU/Linux se ne fa un uso molto frequente per vari scopi che qui non approfondiamo.

Per visualizzare o modificare uno di questi file si può agire indifferentemente sul file effettivo o su uno dei suoi collegamenti simbolici.

Nel caso della configurazione di Apache l'idea è quella di avere nelle sottodirectory «-available» tutti i file utilizzabili e rendere attivi solo quelli che servono creando un collegamento ad essi nelle rispettive sottodirectory «-enabled».

Anche nel file /etc/apache2/sites-enabled/000-default non sono normalmente necessari molti interventi; di seguito viene riportato il suo contenuto.

      1 NameVirtualHost *
      2 <VirtualHost *>
      3  ServerAdmin webmaster@localhost
      4  
      5  DocumentRoot /var/www/
      6  <Directory />
      7   Options FollowSymLinks
      8   AllowOverride None
      9  </Directory>
     10  <Directory /var/www/>
     11   Options Indexes FollowSymLinks MultiViews
     12   AllowOverride None
     13   Order allow,deny
     14   allow from all
     15   # This directive allows us to have apache2's default start page
     16   # in /apache2-default/, but still have / go to the right place
     17   RedirectMatch ^/$ /apache2-default/
     18  </Directory>
     19 
     20  ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
     21  <Directory "/usr/lib/cgi-bin">
     22   AllowOverride None
     23   Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
     24   Order allow,deny
     25   Allow from all
     26  </Directory>
     27 
     28  ErrorLog /var/log/apache2/error.log
     29 
     30  # Possible values include: debug, info, notice, warn, error, crit,
     31  # alert, emerg.
     32  LogLevel warn
     33 
     34  CustomLog /var/log/apache2/access.log combined
     35  ServerSignature On
     36 
     37     Alias /doc/ "/usr/share/doc/"
     38     <Directory "/usr/share/doc/">
     39         Options Indexes MultiViews FollowSymLinks
     40         AllowOverride None
     41         Order deny,allow
     42         Deny from all
     43         Allow from 127.0.0.0/255.0.0.0 ::1/128
     44     </Directory>
     45 
     46 </VirtualHost>

Illustriamo brevemente almeno le opzioni di configurazione più importanti:

Riguardo ai log di Apache sottolineiamo l'importanza della consultazione dei relativi file: /var/log/apache2/access.log e /var/log/apache2/error.log (gli altri file con nomi simili presenti nella directory /var/log/apache2 vengono creati automaticamente dal meccanismo di «rotazione» dei file di log e contengono messaggi meno recenti).

Nel primo si trovano i messaggi di log riguardanti gli accessi fatti al servente.

L'altro file contiene i messaggi ed è importantissimo se ci sono problemi nell'esecuzione di un CGI; in tal caso infatti la risposta che il servente invia al browser è un enigmatico e poco utile messaggio del tipo: «500 Internal Server Error» senza alcuna indicazione sui motivi che hanno causato il malfunzionamento; solo consultando il file /var/log/apache2/error.log si può sperare di avere qualche informazione utile per cercare di individuare e correggere l'errore nel CGI «incriminato».

6.4.2   Gestione dei «siti-utente»

Una modifica interessante che può essere fatta alla configurazione predefinita di Apache2 è quella riguardante l'attivazione dei cosiddetti «siti-utente» che sono degli spazi Web, uno per ogni utente del sistema GNU/Linux, gestiti dal servente Apache2 e raggiungibili all'URI: http://localhost/~nome_utente/.

Per attivare questa funzionalità occorre (sempre con i diritti dell'utente root) attivare e configurare il modulo userdir creando i necessari collegamenti simbolici in /etc/apache2/mods-enabled con i seguenti comandi:

cd /etc/apache2/mods-enabled

ln -s ../mods-available/userdir.load .

ln -s ../mods-available/userdir.conf .

Si ricordi di riavviare il servente dopo ogni modifica alla sua configurazione.

Il file /etc/apache2/mods-enabled/userdir.conf ha il seguente contenuto:

      1 <IfModule mod_userdir.c>
      2         UserDir public_html
      3         UserDir disabled root
      4 
      5         <Directory /home/*/public_html>
      6          AllowOverride FileInfo AuthConfig Limit
      7          Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
      8         </Directory>
      9 </IfModule>

Anche in questo caso commentiamo alcune direttive importanti.

Per fare in modo che sia possibile eseguire dei CGI residenti nella directory del sito personale anziché solo nella directory /usr/lib/cgi-bin/ occorre modificare la riga 7 inserendo, tra le opzioni, anche ExecCGI e aggiungere in coda una riga ulteriore con questo aspetto:

AddHandler cgi-script .cgi .pl .sh

In tale riga .cgi, .pl, .sh sono alcune estensioni usate spesso nei nomi dei CGI (se ne possono aggiungere altre).

Con queste modifiche l'utente può eseguire dei CGI memorizzati nella directory del proprio sito personale che, come detto, è solitamente /home/nome_utente/public_html; è comunque consigliabile creare in essa delle sottodirectory in cui posizionare le varie risorse del sito suddivise per tipologia, ad esempio: doc per i file HTML, imm per le immagini, cgi per i CGI e così via.

Con uno spazio personale organizzato in questo modo, per aprire un modulo HTML contenuto nel file doc/modulo.html, dovremmo collegarci al seguente URI: http://localhost/~nome_utente/doc/modulo.html; al suo interno, per inviare i dati al CGI cgi/prova-cgi.cgi, occorrerebbe valorizzare l'attributo action del modulo con il valore "../cgi/prova-cgi.cgi".

Il vantaggio di poter usare i siti personali risiede principalmente nel fatto che ogni utente può manipolare a suo piacimento i documenti del proprio sito senza essere costretto dopo ogni variazione a trasferire i file nelle posizioni predefinite da Apache (/var/www, /usr/lib/cgi-bin), senza contare poi che gli amministratori di sistema spesso impediscono queste operazioni.

L'uso dei siti-utente è poi senza dubbio utile, forse indispensabile, nel caso si debbano collaudare siti o applicazioni Web e nelle attività didattiche riguardanti questi argomenti.

6.4.3   Permessi su file e directory di un sito Web

In precedenza si è accennato al fatto che certe directory devono essere raggiungibili e certi file leggibili o eseguibili da parte dell'utente www-data; ricordiamo infatti che, solitamente, i processi (programmi in esecuzione) del servente Apache2 vengono eseguiti con i diritti di tale utente e del gruppo di uguale nome.

Questo significa appunto che le directory che devono essere attraversate e i file che devono essere letti o eseguiti devono permettere queste operazioni all'utente o al gruppo succitati.

Occorre quindi fornire alcune informazioni su come assegnare i permessi adeguati affinché il nostro sito funzioni regolarmente.

Nel caso del sito predefinito le operazioni di assegnazione o variazione dei permessi devono essere fatte dall'amministratore; nel caso di sito personale, invece, può farle anche l'utente «proprietario» di quel sito, visto che le risorse risiedono nel proprio spazio personale dove egli ha «pieni poteri».

Riassumiamo quali devono essere i permessi delle varie risorse fornendo anche possibili comandi per loro assegnazione (ricordando comunque che essa può essere fatta anche in ambiente grafico con strumenti più amichevoli):

Analoghe considerazioni valgono per le risorse utilizzate in un sito-utente: deve essere possibile per tutti raggiungere la directory /home/nome_utente/public_html e eventuali sottodirectory e i file in esse contenuti devono essere leggibili e, nel caso di CGI, anche eseguibili per tutti gli utenti.

A titolo di esempio ecco il comando per rendere leggibili e attraversabili tutte le sottodirectory di public_html e leggibili e eseguibili tutti i file in esse contenuti, per tutti gli utenti, in modo ricorsivo:

chmod -R o+rx ~/public_html/*

6.5   Approfondimenti sul protocollo HTTP

Come accennato in precedenza, i CGI ricevono dati in input dal browser tramite il servente Web e possono manipolarli liberamente per produrre una risposta da inviare «indietro» al programma di navigazione.

Prima di proseguire è dunque importante fornire maggiori dettagli (senza pretese di completezza) sui principi di funzionamento del protocollo HTTP che sovrintende al dialogo tra programma di navigazione e servente Web (e quindi programma CGI).

Nel paragrafo 1.4.2 si è già visto come il protocollo HTTP regoli l'interazione tra un programma di navigazione e un servente Web tramite una sequenza di quattro fasi:

Vediamo in dettaglio le fasi più importanti che sono quelle della richiesta e della risposta.

6.5.1   La richiesta

La richiesta deve essere così strutturata:

Il corpo della richiesta deve essere preceduto da «CRLF» (ritorno di carrello e riga nuova, codici ASCII 13 e 10) in modo da rendere la richiesta stessa compatibile con i sistemi operativi della famiglia Unix (che concludono una riga con il solo «LF») e con quelli tipo MS-Dos, MS-Windows (che concludono una riga con «CRLF»).

Non viene fatta distinzione tra maiuscole e minuscole eccetto che nell'indicazione del file richiesto.

Le versioni del protocollo accettate sono: HTTP/1.0 e HTTP/1.1; con questa ultima è possibile anche effettuare più richieste per ogni connessione anziché una sola come previsto dalla versione precedente.

Il metodo permette di specificare il tipo di richiesta da effettuare sulla risorsa specificata; i metodi previsti sono:

Le intestazioni servono ad aggiungere informazioni alla richiesta; ad esempio Accept-Language: che indica quali lingue vuole il programma di navigazione, oppure Host: che indica il nome del servente.

Vale la pena soffermarsi su quest'ultima intestazione che è obbligatoria a partire dalla versione 1.1 del protocollo.

A prima vista parrebbe che essa fornisca un'informazione ridondante in quanto, nel momento in cui il cliente effettua la richiesta, il nome del servente cui si rivolge è già stato specificato nella precedente fase di connessione.

Invece questa informazione è importantissima perché permette di configurare i serventi Web in modo che possano gestire più siti Web associati allo stesso indirizzo IP e identificati da nomi diversi.

Vediamo come questo è possibile: supponiamo di avere i siti www.sito1.it e www.sito2.it entrambi associati all'indirizzo IP 82.88.114.44 e gestiti da un servente Web Apache; quando i clienti effettuano richieste ai due siti esse vengono indirizzate, grazie alla risoluzione dei nomi fatta dal DNS, allo stesso indirizzo 82.88.114.44.

Il servente non sarebbe quindi in grado di distinguere le richieste originariamente dirette a sito1 da quelle dirette a sito2; ecco perché è indispensabile l'informazione circa il nome del sito Web interessato dalla richiesta, fornita con l'intestazione Host:.

Per questo motivo ormai tutti i programmi di navigazione utilizzano il protocollo HTTP/1.1 e specificano il nome del servente, mediante l'intestazione Host:, nelle loro richieste di documenti HTML.

L'aspetto di una tipica richiesta può essere il seguente:

GET /index.html HTTP/1.1
Host: www.maxplanck.it
Accept-Language: it en
<CRLF> (riga vuota)

6.5.2   La risposta

Anche la risposta ha una struttura ben definita:

Ad ogni codice di stato è associata una stringa esplicativa e i codici sono divisi in categorie in base alla prima cifra:

Le intestazioni servono ad aggiungere informazioni alla risposta; ad esempio Date: che indica data e ora della trasmissione, oppure Server: che descrive il servente.

Esistono anche altri tipi di intestazioni che permettono di fornire informazioni aggiuntive circa la trasmissione del corpo della risposta; ad esempio: Content-Type: per indicare il tipo MIME del contenuto della risposta, oppure Content-Length: per specificarne la lunghezza in byte.

Ecco l'aspetto di una possibile risposta:

HTTP/1.1 200 OK
Date: Sun, 12 Aug 2007 20:00:00 GMT
Content-Type: text/html
Content-Length: 75
<CRLF> (riga vuota)
<html><head><title>Risposta</title></head><body>Prova risposta</body></html>

6.5.3   Simulazione di una connessione HTTP da linea di comando

Il protocollo HTTP regola un «semplice» scambio di file di testo tra due nodi di rete; si può allora verificarne il funzionamento utilizzando l'applicazione TELNET per effettuare la connessione e la successiva richiesta di una risorsa ad un servente Web.

Il servizio TELNET risponde di solito alla porta 23 ma si può utilizzare il comando telnet per connettersi ad altri tipi di servizi semplicemente indicando il relativo numero di porta (se non si indica la porta viene usata la 23); quindi per connettersi al servente Web installato sulla macchina locale si esegue il comando:

telnet localhost 80

Se la connessione viene stabilita correttamente si può poi effettuare la richiesta rispettando le regole illustrate in precedenza; la risorsa desiderata viene ricevuta (a meno di errori) sul terminale usato per la connessione e quest'ultima termina (dopo alcuni secondi nel caso del protocollo HTTP/1.1).

Nella figura 6.20 viene mostrato il procedimento fino all'ottenimento della risposta; gli unici input sono le righe sottolineate che corrispondono all'attivazione della di connessione e all'effettuazione della richiesta HTTP (si noti la riga vuota dopo l'intestazione Host:).

Come si vede la risposta appare come testo puro, quindi senza che i tag HTML vengano interpretati, cosa del tutto normale in questo contesto, data la mancanza di un programma di navigazione in grado di interpretarli.

Figura 6.20.

figure/pagine-web-telnet-http

6.6   Creazione della pagina di risposta da parte del CGI

I programmi CGI, nel momento in cui devono creare pagine Web di riposta da inviare al cliente, devono rispettare le regole appena illustrate; quindi devono creare un file di testo con una intestazione Content-Type: con l'indicazione del tipo MIME, seguita da una riga vuota e dal contenuto effettivo della risposta.

Il tipo MIME più utilizzato in questo contesto è ovviamente text/html.

Ovviamente il corpo della risposta deve essere coerente con quanto dichiarato nell'intestazione e quindi deve essere un sorgente HTML.

Un altro tipo di risposta che può essere fornita da un programma CGI consiste nell'invio di una pagina Web già pronta, residente sul servente o presso qualsiasi altro indirizzo in rete; in tal caso la risposta deve contenere solo una intestazione di tipo Location in questa forma:

Location: URI della pagina da inviare

Anche in questo caso è necessario aggiungere una riga vuota dopo l'intestazione.

6.7   Variabili di ambiente per i CGI

Un programma CGI ha a disposizione un gruppo di «variabili di ambiente» che vengono valorizzate automaticamente dal servente HTTP e che il programma può usare per i suoi scopi.

Segue l'elenco delle più importanti di tali variabili con una breve descrizione del loro significato:

Nome variabile Significato
CONTENT_LENGTH Numero di byte nello standard input, se il metodo di invio è "post".
CONTENT_TYPE Tipo MIME dei dati inviati.
GATEWAY_INTERFACE Versione del CGI in esecuzione (di solito CGI/1.1).
HTTP_ACCEPT Elenco di tipi MIME che il browser è in grado di accettare.
HTTP_REFERER Nome della pagina HTML che ha inviato i dati.
HTTP_USER_AGENT Nome e versione del browser che ha inviato i dati.
PATH_INFO Informazioni aggiuntive passate aggiungendo un percorso in coda all'URI specificato come valore di action.
PATH_TRANSLATED Percorso completo ottenuto da quello contenuto nella variabile precedente aggiungendo in testa il «document root» del servente Web cioè il percorso di base dei documenti HTML (di solito /var/www/).
QUERY_STRING Stringa di input se il metodo di invio è "get".
REMOTE_ADDR Indirizzo IP della macchina che ha inviato i dati.
REMOTE_HOST Nome della macchina che ha inviato i dati.
REMOTE_USER Nome dell'utente che ha inviato i dati (valorizzato solo se il servente richiede l'autenticazione dell'utente).
REQUEST_METHOD Metodo usato per l'invio dei valori: "post" o "get".
SCRIPT_NAME Nome del programma CGI completo di percorso.
SERVER_ADMIN Indirizzo di posta elettronica dell'amministratore del servente.
SERVER_NAME Nome della macchina su cui viene eseguito il programma CGI
SERVER_PORT Porta per il collegamento al servente Web (quasi sempre 80).
SERVER_PROTOCOL Protocollo HTTP usato dal servente (di solito HTTP/1.1).
SERVER_SOFTWARE servente Web utilizzato, ad esempio Apache 2.2.3

Il valore di queste variabili si ottiene nell'ambiente della shell di GNU/Linux anteponendo al loro nome il simbolo «$».

In altri linguaggi l'operazione è meno immediata; ad esempio in Perl occorre usare il vettore associativo $ENV (per maggiori dettagli si consiglia di consultare manuali riguardanti tale linguaggio).

Di seguito vediamo un esempio in cui si usa un modulo di input che richiama un programma CGI, denominato cgi-var.sh, scritto con i comandi della shell Bash di GNU/Linux; tale programma crea un documento di risposta in cui sono visualizzati i valori delle variabili appena elencate.

Sorgente HTML del modulo:

<html>
<head>
<title>Esempio di modulo per visualizzare variabili</title>
</head>
<body>
<h2>Esempio di input e visualizzazione di variabili d'ambiente</h2>
<form action="/cgi-bin/cgi-var.sh" method="get">
<p><br/><b>Inserire dato da passare al programma&nbsp;&nbsp;</b>
<input type="text" name="dato" size="5" maxlength="3"/>
</p>
<input type="submit" value="Invio"/> 
</form>
</body>
</html>

L'aspetto del modulo è quello mostrato in figura 6.24.

Figura 6.24.

figure/pagine-web-ese0020

Di seguito viene riportato il programma CGI cgi-var.sh:

#!/bin/sh
#
# Programma cgi scritto in shell per la visualizzazione delle variabili di ambiente
#
echo "Content-type: text/html"
echo
echo "<html>"
echo "<head>"
echo "<title>Test variabili di ambiente CGI</title>"
echo "</head>"
echo "<body>"
echo "<h4>Test variabili di ambiente CGI</h4>"
echo "<pre>"
echo
echo "CONTENT_LENGTH = $CONTENT_LENGTH"
echo "CONTENT_TYPE = $CONTENT_TYPE"
echo "GATEWAY_INTERFACE = $GATEWAY_INTERFACE"
echo "HTTP_ACCEPT = $HTTP_ACCEPT"
echo "HTTP_REFERER = $HTTP_REFERER"
echo "HTTP_USER_AGENT = $HTTP_USER_AGENT"
echo "PATH_INFO = $PATH_INFO"
echo "PATH_TRANSLATED = $PATH_TRANSLATED"
echo "QUERY_STRING = $QUERY_STRING"
echo "REMOTE_ADDR = $REMOTE_ADDR"
echo "REMOTE_HOST = $REMOTE_HOST"
echo "REMOTE_USER = $REMOTE_USER"
echo "REQUEST_METHOD = $REQUEST_METHOD"
echo "SCRIPT_NAME = $SCRIPT_NAME"
echo "SERVER_ADMIN = $SERVER_ADMIN"
echo "SERVER_NAME = $SERVER_NAME"
echo "SERVER_PORT = $SERVER_PORT"
echo "SERVER_PROTOCOL = $SERVER_PROTOCOL"
echo "SERVER_SOFTWARE = $SERVER_SOFTWARE"
echo
echo "Standard input:"
cat
echo "</pre>"
echo "</body>"
echo "</html>"

L'aspetto del documento di risposta creato da cgi-var.sh è mostrato nella figura 6.26.

Figura 6.26.

figure/pagine-web-ese0020r

Se nel modulo cambiamo il metodo di invio dei dati da "get" a "post" otteniamo la risposta mostrata nella figura 6.27.

Figura 6.27.

figure/pagine-web-ese0020r2

6.8   Passaggio dei dati del modulo al programma CGI

Al momento della definizione di un modulo, come abbiamo visto, occorre specificare il nome e la locazione del CGI che deve trattare i dati e anche il modo in cui essi vengono trasferiti al CGI stesso.

Ricordando che allo scopo si usano gli attributi action e method del tag <form>, vediamo il seguente esempio di definizione di un modulo (tralasciando i dettagli dei controlli) in cui si fanno queste ipotesi:

<form action="/cgi-bin/cgi-prova.sh" method="get">
...
(controlli vari del modulo)
....
</form>

In caso di invio con metodo "get" i dati, nella forma «nome=valore», vengono accodati all'URI indicato come valore di action separati da esso con il simbolo «?»; inoltre ogni coppia nome-valore è separata dalla successiva dal carattere «&».

Quindi l'URI effettivo diventa:

/cgi-bin/cgi-prova.sh?cognome=Paperino&nome=Paolino

Il programma cgi-prova.sh riceve i dati nella variabile di ambiente QUERY_STRING che assume il valore "cognome=Paperino&nome=Paolino".

L'alternativa è usare il metodo "post" nel qual caso i dati vengono passati attraverso il canale di input standard mentre la variabile QUERY_STRING rimane vuota; il programma CGI deve quindi preoccuparsi di leggere lo standard input dove trova la stringa: "cognome=Paperino&nome=Paolino".

Il metodo "post" è quello da usare obbligatoriamente nel caso i dati debbano essere trasferiti ad un indirizzo di posta elettronica; negli altri casi si possono usare indifferentemente i due metodi.

Occorre però osservare che il metodo "get" è il meno indicato per due motivi: rende visibili i dati accodati agli URI, anche se questi sono di tipo password, ed è soggetto ai limiti di lunghezza degli stessi (circa 2 Kbyte) e quindi dà problemi in presenza di una grossa mole di dati da trasferire.

Inoltre il metodo "get" è, secondo le specifiche del protocollo HTTP, «sicuro» (non genera variazioni nello stato interno del servente) ma è anche «idempotente» (l’effetto sul servente di più richieste identiche è lo stesso di quello di una sola richiesta) e questo fa si che i programmi di navigazione possano memorizzare nella propria memoria cache le relative pagine di risposta.

Con il metodo "post" invece il servente viene ricontattato ogni volta che la pagina viene ricaricata e appare anche una opportuna domanda all'utente circa la sua reale volontà di inviare di nuovo i dati; per questo è consigliabile usare il metodo "post" in tutte le situazioni in cui l'elaborazione dei dati del modulo provoca un qualche aggiornamento di dati residenti sul servente.

Se il modulo di provenienza dei dati prevede la possibilità di più valori per uno stesso campo (si pensi ad un controllo <select> con attributo multiple), nella stringa dei dati troviamo più coppie nome-valore riguardanti quel campo, ad esempio: "cognome=Paperino&nome=Paolino&hobby=musica&hobby=pittura".

Nel caso infine si usi un modulo per trasferire un file, il programma CGI deve essere di tipo particolare: non deve aspettarsi stringhe in input né in QUERY_STRING né nello standard input, ma deve essere predisposto alla lettura del file inviato attraverso quest'ultimo.

6.9   Decodifica dei dati di input del CGI

Come abbiamo visto, indipendentemente dal metodo usato per l'invio, l'input arriva al programma CGI come una stringa composta da coppie nome=valore come la seguente:

nome1=valore1&nome2=valore2&nome3=valore3.....

Questo formato di invio prende il nome di «codifica URL» e si basa sulle seguenti regole:

Vediamo in dettaglio le codifiche dei caratteri riservati:

Carattere Codifica
% %25
& %26
+ %2B
/ %2F
= %3D
@ %40
~ %7E

Il programma CGI deve per prima cosa fare il parsing dell'input, cioè scandirlo e decodificarlo, tenendo conto di tutte le regole appena illustrate, e deve ottenere dalla stringa di input i nomi delle variabili e i rispettivi valori in modo che siano utilizzabili a fini elaborativi; solo successivamente può svolgere le elaborazioni sulle variabili così ottenute.

6.9.1   Esempio di decodifica dati in Perl

Come esempio viene proposto un programma scritto in Perl che svolge i compiti appena illustrati:

#!/usr/bin/perl
#
# cgi per decodifica variabili da modulo html
# versione autonoma con output variabili
# 
print "Content-type: text/html\n";
print "\n";
print "<html>\n";
print "<head>\n";
print "<title>Decodifica</title>\n";
print "</head>\n";
print "<body>\n";

$metodo=$ENV{REQUEST_METHOD};
if ($metodo eq "POST" || $metodo eq "post") {
   read (STDIN,$dati,$ENV{CONTENT_LENGTH});
}
else {
   $dati =$ENV{QUERY_STRING};
}
print "<p>Dati = $dati\n";
# decodifica di +, & %xx ecc.)
$i=0;
@vars=();
@vars = split(/&/,$dati);
foreach $_ (@vars)
{
   s/\+/ /g;
# sostituisce %xx con il relativo carattere;
# g = sost. globale;  e=2.a parte è una espressione
   s/%([0-9a-fA-F][0-9a-fA-F])/sprintf("%c",hex($1))/eg;
   /=/;
   @val[$i]=$';
   @nom[$i]=$`;
   $i++;
}
for ($j=0;$j<$i;$j++) {
   print "<p>nomevar = $nom[$j]<br>valore = $val[$j]<br><br>\n";
}
print "</body>\n";
print "</html>\n";

Tralasciamo la spiegazione dei dettagli di funzionamento del programma per la quale è necessaria la conoscenza almeno delle basi del linguaggio Perl.

Osserviamo solo (per chi conosce tale linguaggio o per chi, invogliato da questo esempio, si accinge a conoscerlo) che il programma proposto effettua la decodifica dell'input e presenta una risposta banale contenente nomi e valori delle variabili; tale risposta, nell'ipotesi che la stringa di input sia: "cognome=Paperino&nome=Paolino", viene mostrata nella figura 6.33.

Figura 6.33.

figure/pagine-web-ese0021

Con opportune modifiche si può far diventare il programma un modulo utilizzabile da altri programmi Perl (con l'istruzione use) allo scopo di ottenere la sola decodifica dell'input per una successiva elaborazione: sicuramente occorre eliminare tutte le istruzioni di output (le print); per le altre modifiche da effettuare si rimanda alla consultazione di manuali sul linguaggio Perl in particolare sulla creazione e uso di sottoprogrammi.

Facendo ancora riferimento alla risposta mostrata nella figura 6.33 possiamo fare un paio di osservazioni interessanti:

6.9.2   Altri strumenti per la decodifica

Essendo la fase di decodifica necessaria in qualsiasi elaborazione CGI, esistono moltissimi strumenti, reperibili anche in Internet, che svolgono questa operazione; un esempio e' il programma uncgi scritto in linguaggio c, scaricabile all'indirizzo <http://www.midwinter.com/~koreth/uncgi.html>.

Il programma è freeware ed è utilizzabile anche in applicazioni commerciali previa richiesta all'autore.

Una volta scaricato il programma si deve compilarlo, seguendo le istruzioni allegate, e installare l'eseguibile ottenuto nella directory dei CGI.

Per illustrarne il funzionamento facciamo ancora riferimento alla stringa di input: "cognome=Paperino&nome=Paolino".

Per fare in modo che tale input venga decodificato dal programma uncgi l'attributo action deve assumere il valore "/cgi-bin/uncgi/cgi-prova.sh".

In questo caso il suo effetto non è quello di far eseguire il programma cgi-prova.sh residente nella sottodirectory uncgi di /cgi.bin in quanto uncgi non è una sottodirectory; il valore dell'attributo viene invece interpretato come richiesta di eseguire in sequenza i programmi uncgi e cgi-var.sh entrambi memorizzati in /cgi-bin.

Il programma uncgi riceve la stringa contenente i dati di input e la decodifica indifferentemente rispetto al metodo di invio utilizzato ("get" o "post"); al termine della sua esecuzione rende disponibili, come variabili di ambiente, le variabili della stringa di input decodificate con nome preceduto dal prefisso "WWW_".

L'altro programma quindi può essere scritto senza preoccuparsi della fase di decodifica e tenendo conto che le variabili provenienti dal modulo sono disponibili come variabili di ambiente con nomi: "WWW_cognome" e "WWW_nome".

6.10   Esempio completo di esecuzione di un CGI

A questo punto possiamo illustrare un esempio completo in cui vengono utilizzati:

Sorgente HTML del modulo:

<html>
<head>
<title>Modulo di prova</title>
</head>
<body>
<h2 style="text-align:center;">Immissione dei dati</h2>
<form action="/cgi-bin/uncgi/cgi-modulo.pl" method="post">
 <table>
  <tr>
  <td>Inserire il cognome&nbsp;&nbsp;</td>
  <td><input type="text" name="cognome" size="20" maxlength="20"/></td>
  </tr><tr>
  <td>Inserire il nome&nbsp;&nbsp;</td>
  <td><input type="text" name="nome" size="20" maxlength="20"/></td>
  </tr><tr>
  <td>Inserire la professione&nbsp;&nbsp;</td>
  <td><input type="text" name="professione" size="20" maxlength="20"/></td>
  </tr><tr>
  <td>Vuoi informazioni via e-mail?&nbsp;&nbsp;</td>
  <td>Si<input type="checkbox" name="risp" value="si"/>
      No<input type="checkbox" name="risp" value="no"/></td>     
  </tr><tr>
  <td>Indirizzo e-mail&nbsp;&nbsp;</td>
  <td><input type="text" name="email" size="40" maxlength="40"/>
  </tr><tr>
  <td><input type="submit" value="Invio"/>
  <td><input type="reset"/>
  </tr>
 </table>
</form>
</body>
</html>

La pagina contenente il modulo ha l'aspetto mostrato in figura 6.35.

Figura 6.35.

figure/pagine-web-ese0022

Il programma CGI si chiama cgi-modulo.pl e questo è il suo listato:

#!/usr/bin/perl
#
# cgi-modulo.pl
#
#==============================================================================
# Funzione messaggio
#==============================================================================
sub messaggio {
    print ("<p>Clicca <a href=$ENV{'HTTP_REFERER'}> qui</a> per correggere\n");
    return 0
    };
#=============================================================================
# Main
#=============================================================================        
$cognome     = $ENV{'WWW_cognome'};
$nome        = $ENV{'WWW_nome'};
$professione = $ENV{'WWW_professione'};
$risp        = $ENV{'WWW_risp'};
$email       = $ENV{'WWW_email'};
print ("Content-type: text/html\n");
print ("\n");
print ("<html>\n");
print ("<head>\n");
print ("<title>Test CGI</title>\n");
print ("</head>\n");
print ("<body>\n");
print ("<h1>Resoconto immissione dati</h1>\n");
if ($cognome eq "") {
    print ("<p>Cognome obbligatorio\n");
    &messaggio;
    } 
else {
    if ($nome eq "") {
 print ("<p>Nome obbligatorio\n");
 &messaggio;
 } 
    else {
 if (($risp eq "si") && ($email eq "")) {
     print ("<p>Se vuoi e-mail inserisci il tuo indirizzo\n");
     &messaggio;
     }
 else {
     print ("<p>Cognome:&#160;&#160;&#160;&#160;");
     print ("&#160;&#160;&#160; $cognome </p>\n");
     print ("<p>Nome:&#160;&#160;&#160;&#160;&#160;&#160;");
     print ("&#160;&#160;&#160;&#160;&#160;&#160;&#160; $nome </p>\n");
     print ("<p>Professione:&#160;&#160;&#160;&#160;");
     print ("&#160;$professione </p>\n");
     print ("<p>Richiesta mail:&#160; $risp </p>\n");
     print ("<p>Indirizzo:&#160;&#160;&#160;&#160;&#160;");
     print ("&#160;&#160;&#160;&#160;&#160;&#160;$email </p>\n");
     } } };
print ("</body>\n");
print ("</html>\n");

Infine nella figura 6.37 vediamo l'aspetto del documento di risposta creato dal programma.

Figura 6.37.

figure/pagine-web-ese0022r

6.11   Considerazioni finali sui CGI

Come più volte fatto notare, i programmi CGI vengono sempre eseguiti sul servente Web al quale ci si collega; ciò comporta alcuni limiti, in particolare:

Ci sono però anche importanti aspetti positivi relativi alla natura dei CGI:

Di quest'ultimo aspetto, cioè l'interazione tra pagine Web e basi di dati, che è molto importante, si occupa in modo più esteso il prossimo paragrafo.