Scrivere applicazioni CGI in (x)Harbour – Parte I

Le CGI

CGI significa Common Gateway  Interface e designa una classe di programmi (applicazioni) fatti per funzionare in tandem con un web server. Queste applicazioni prevedono di essere invocate da un web server e restituiscono al medesimo una pagina HTML.

Il web server sarà configurato in modo tale per cui l’applicazione CGI diventa a tutti gli effetti parte dell’URL del sito. Per esempio un URL (in ambiente Windows) del tipo: http://www.miosito.com/cgi-bin/MyCgi.exe invoca direttamente MyCgi.exe (nella directory convenzionale cgi-bin).

Il web server a sua volta restiturà pari pari tale pagina HTML al browser che ha invocato la CGI (cioè ha tentato di “navigare” l’ URL che invoca la CGI), per esempio una stringa come la seguente (EOL=LF, chr(10) o CRLF, (chr(13)+chr(10)):

“Content-Type:text/html[EOL][EOL]Grazie per avermi invocato![EOL]“

La stringa viene ritrasmessa al browser chiamante, che la tratterà come una pagina statica. Dal punto di vista del browser che ha emesso la richiesta, ricevere una pagina HTML statica, ovvero il contenuto di un file situato sul server, oppure una pagina creata al momento non fa alcuna differenza: si tratta sempre di un flusso di bytes (uno stream) e verrà processato sempre allo stesso modo.

Tutto questo a poco servirebbe se non ci fosse modo di passare alla CGI anche una serie di valori che ne determinino il comportamento.

Come passare parametri alla CGI

Il web server fornisce un ambiente standard di esecuzione per i programmi CGI, completo di un set di variabili d’ambiente. In (x)Harbour possiamo leggere le variabili d’ambiente, conoscendo il loro nome, con l’istruzione GetEnv(“nomevar”). Per prima cosa quindi possiamo ricavare PATH_INFO, QUERY_STRING E CONTENT_LENGTH, variabili d’ambiente che dovrebbero contenere l’URL richiesto dal client, una parte specifica di esso e la lunghezza in bytes dello stream inviato dal client. Il modo più semplice quindi di trasmettere parametri è quello di includerli nell’URL, per esempio:
www.miosito.com/cgi-bin/MyApp.cgi?cheoresono
Si noterà che viene usato il carattere convenzionale “?” per delimitare l’inizio della parte dell’URL che contiene i parametri (la query string). Tutto quello che segue il “?” finisce nella variabile d’ambiente QUERY_STRING.

La CGI prende forma: essa deve quindi esaminare QUERY_STRING e agire di conseguenza. La risposta dovrà essere scritta su STDOUT. (x)Harbour ha la funzione OutStd() (e il comando qout, suo alias) che fa appunto questo. Per esempio:

Procedure main()
cCommand:=getenv("QUERY_STRING")
if .not. empty(cCommand)
do case
case cCommand="cheoresono"
outStd("Content-Type:text/html"+chr(10)+chr(10))
outStd(""Sono le "+time()+"")
...
endcase
endif
return nil

Un altro metodo utilizza specifici elementi HTML, ovvero i FORM. Un form è costituito da un “contenitore” e da un certo numero di controlli. Un form dispone di un METHOD e di una ACTION. I METHOD che ci interessano sono POST e GET, mentre la ACTION conterrà il comando da passare alla CGI. Per esempio: (Nota: questo editor è fatto con Javascript e HTML, quindi se tento di scrivere del codice HTML esso verrà interpretato anziché presentato in forma testuale. Per questo i simboli TAG inizio e fine sono sostituiti da parentesi quadre).

[form action="cgi-bin/MyApp.exe" method="GET" name="myform">Importo:[input type="TEXT" name="IMPORTO" value="100"><br><br> Rate:[input type="TEXT" name="RATE" value="12"><br> [input type="SUBMIT" value=""><br></form>
 Il FORM quando SUBMITted trasmetterà al server una richiesta, corrispondente a:

www.miosito.com/cgi-bin/MyApp.exe?IMPORTO=100&RATE=12, il che porrà in QUERY_STRING “IMPORTO=100&RATE=12″. In pratica il metodo GET è equivalente a un URL. Notare come dal FORM vengano generate coppie di valori del tipo[VARIABILE]=[VALORE] separate questa volta dal carettere “&”.

Se lo stesso FORM usasse il METHOD POST, il risultato sarebbe diverso.

[form action="cgi-bin/MyApp.exe" method="POST" name="myform">Importo:[input type="TEXT" name="IMPORTO" value="100"><br><br> Rate:[input type="TEXT" name="RATE" value="12"><br> [input type="SUBMIT" value=""><br></form>
Il metodo POST trasmette i dati del FORM su STDOUT (del client, arrivano al server su STDIN del server, il quale fa lo stesso e copia i dati sul suo STDOUT e questi arrivano su STDIN della CGI) e setta la variabile d’ambiente CONTENT-LENGTH. Come si fa a leggere STDIN via (x)Harbour? Anche se la docuentazione ufficiale non affronta specificamente il tema, ricordiamoci le funzioni FREAD() e FREADSTR(). Sono funzioni di basso livello che leggono uno stream e necessitano come argomento di un handle valido, solitamente ottenuto con una FOPEN() per leggere un file in modo “grezzo”. Orbene, se il numero di handle è 0 (zero), queste funzioni leggono STDIN.

La nostra CGI potrebbe a questo punto arricchirsi con un ulteriore confronto:


IF VAL(GETENV(“CONTENT_LENGTH”))!=0
cStream:=FREADSTR(0)

In questo modo possiamo riconosceer una richiesta POST e leggere lo stream via STDIN, viceversa leggeremo QUERY_STRING.

C’è un altro modo, simile al metodo GET, ovvero utilizzare Javascript per manipolare direttamente il contenuto dell’URL prima di effettuare il submit(). Il risultato sarà simile ad un GET.

I ferri del mestiere

A questo punto ci rendiamo conto di necessitare di qualche funzione specializzata: per esempio se l’URL inserito nel client comprende il carattere(ASCII 32), la Query String conterrà %20, ovvero il carattere “%” seguito dal codice ASCII in esadecimale. Questo vale per tutti i caratteri speciali, per cui ci occorrono un paio di funzioni: hexify() e dehexify() per convertire queste stringhe. Al posto di Qout() definiamo mediante il possente preprocessore di (x)Harbour:

#command cgiout [<xList>] => ([OutStd(),] OutStd(chr(10)))

Questo ci permetterà di stampare su STDOUT aggiungendo alla fine [EOL]. OutStd() infatti si limita a porre in STDOUT i caratteri passati, senza formattazione né EOL (che viene aggiunto invece dai comandi “?” e da “??”).

Dato che, come abbiamo visto, avremo a che fare con streams contenenti coppie separate da “&” a loro volta composte da due elementi separati da “=”, ci occorre una funzione “splitter” che separi una stringa in base a un separatore arbitrario. La funzione estrai(stringa,separatore) restituisce un array con tanti elementi quanti i separatori nella stringa.
Visto poi che tutte le pagine che la CGI dovrà produrre iniziano con “content-type…” ecc. e finiscono con il tag di chiusura, scriviamo le funzioni MakeHTMLHeader() e MakeHTMLBottom() che automatizzano in parte queste operazioni.

Anche l’errorsystem andrà modificato: se vogliamo che i messaggi di errore siano visibili, devono anche loro essere stampati su STDOUT e non visualizzati su schermo.

Con questo armamentario ridotto siamo già in grado di scrivere delle CGI, ma non bisogna farsi illusioni. Ci sono alcune complessità intrinseche nella programmazione di applicazioni CGI. Se leggere uno stream in entrata e scriverne uno in uscita non sono operazioni difficili, tutto quello che sta in mezzo invece lo è. Una difficoltà è ovviamente rappresentata dal fatto che bisogna generare in output codice HTML valido, e quindi una certa conoscenza di HTML è indispensabile. Un’altra difficoltà analoga alla prima riguarda Javascript: è evidente che, a parte applicazioni piuttosto scarne, bisognerà fare uso massiccio di Javascript, il che significa impararsi un nuovo linguaggio, niente meno, e per di più un linguaggio che, anche se apparentemente semplice, è invece un brutto cliente se si vuole cercare il pelo nell’uovo. Però bisogna dire che la situazione attualmente è meno tragica di quanto non fosse qualche anno fa: è vero che Javascript è implementato in modo diverso nei diversi browser, ma la corsa alla standardizzazione ha per fortuna ridotto le distanze. Fino a qualche anno fa era laborioso e complesso, oltre che fonte di innumerevoli dispute nei forum dei programmatori Javascript, produrre script  davvero robusti in grado di funzionare su qualsiasi browser.

Un’altra difficoltà consiste nel dover rivedere il paradigma di programmazione. Chi programma da tempo ha già dovuto affrontare il passaggio da programmazione procedurale a programmazione a oggetti. In entrambi i casi il programma dopo essersi avviato aspetta che l’utilizzatore emetta un comando e rimane in esecuzione svolgendo elaborazioni e/o attendendo altri input. Una CGI peraltro invece viene invocata, fa tutto quello che deve fare e termina. Questo significa che alla successiva chiamata la CGI sarà completamente immemore della chiamata precedente! Ogni invocazione della CGI è una esecuzione-e-termine del programma (ci sono tecniche per mantenere delle sessioni ma non verranno trattate qui).

La conseguenza più notevole è che l’output di una CGI deve essere costituito in realtà da una specie di programma che contiene tutte le istruzioni e i riferimenti per compiere l’operazione successiva. Chi ha visto il film Memento può avere un’idea del meccanismo: il protagonista per un disturbo della memoria vive in una specie di eterno presente. Unica guida sono le scritte che imprime sul suo stesso corpo, cosicché al risveglio saprà che fare. Analogamente il programma dovrà scrivere, nella pagina HTML che genererà, i form (o altro) contenenti i comandi da passare a se stesso corredati dai parametri necessari.

Il sorgente che segue è un omaggio a un bravo programmatore che molti anni fa pubblicò una rivista dal titolo “Clipper Magazine”. In uno dei numeri veniva descritta appunto la tecnica per creare delle CGI con Clipper. Si era nel 1996 o giù di lì e possiamo immaginare quanto fosse rivoluzionaria l’idea. Il fatto che dopo oltre 20 anni i concetti siano ancora validi è notevole. Il programmatore si chiamava Mario Lener e ho voluto conservare nel sorgente i suoi messaggi originali di copyright. In realtà non trovo più i sorgenti originali, per cui riporto uno dei miei esperimenti dell’epoca di diretta derivazione dal sorgente originale di Lener. Ci sono degli elementi “spurii” nel sorgente, come la funzione normalizza() ecc. introdotti da me. Per adesso ignoriamoli.

Il programma viene richiamato da un form, quindi per allestire questo test bisognerà avere il server web funzionante, la pagina HTML con il form al suo posto nella documentroot del web server, un database “ar_cli.dbf” con almeno i campi DITTA,CAP e CITTA in una directory accessibile dal nostro programma (C.\cgidata per esempio) e ovviamente Harbour o xHarbour installati.

Link per il sorgente: Link

Questa voce è stata pubblicata in harbour, programmazione, xBase, xharbour. Contrassegna il permalink.

Lascia un Commento

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

*

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>