Come posso risolvere il mio script Perl CGI?

Ho uno script Perl che non funziona e non so come iniziare a restringere il problema. Cosa posso fare?


Nota: sto aggiungendo la domanda perché voglio davvero aggiungere la mia risposta molto lunga a Stackoverflow. Continuo esternamente a collegarmi ad esso in altre risposte e merita di essere qui. Non essere timido nel modificare la mia risposta se hai qualcosa da aggiungere.

Questa risposta è intesa come una struttura generale per lavorare con i problemi con gli script CGI di Perl e originariamente apparso su Perlmonks come Risoluzione dei problemi degli script CGI Perl . Non è una guida completa a tutti i problemi che potresti incontrare, né un tutorial su bug-squashing. È solo il culmine della mia esperienza di debug degli script CGI per venti (più!) Anni. Sembra che questa pagina abbia avuto molte case diverse e mi sembra che dimentichi che esiste, quindi lo aggiungo a StackOverflow. Puoi inviare commenti o suggerimenti a [email protected]. È anche wiki della comunità, ma non impazzire. 🙂


Stai usando le funzionalità integrate di Perl per aiutarti a trovare i problemi?

Attiva gli avvisi per consentire a Perl di avvertirti in merito a parti discutibili del tuo codice. Puoi farlo dalla riga di comando con l’ -w modo da non dover modificare alcun codice o aggiungere un pragma a ogni file:

  % perl -w program.pl 

Tuttavia, dovresti costringerti a chiarire sempre il codice discutibile aggiungendo il pragma delle warnings a tutti i tuoi file:

  use warnings; 

Se hai bisogno di più informazioni rispetto al breve messaggio di avvertimento, usa il pragma di diagnostics per ottenere maggiori informazioni, o guarda nella documentazione di perldiag :

  use diagnostics; 

Hai prodotto per prima cosa un’intestazione CGI valida?

Il server si aspetta che il primo output da uno script CGI sia l’intestazione CGI. In genere potrebbe essere semplice come print "Content-type: text/plain\n\n"; oppure con CGI.pm e le sue derivate, print header() . Alcuni server sono sensibili all’output degli errori (su STDERR ) visualizzati prima dell’output standard (su STDOUT ).

Prova a inviare errori al browser

Aggiungi questa linea

  use CGI::Carp 'fatalsToBrowser'; 

alla tua sceneggiatura. Questo invia anche errori di compilazione alla finestra del browser. Assicurati di rimuoverlo prima di trasferirti in un ambiente di produzione, in quanto le informazioni aggiuntive possono rappresentare un rischio per la sicurezza.

Che cosa ha detto il registro degli errori?

I server tengono i registri degli errori (o dovrebbero, almeno). L’output dell’errore dal server e dal tuo script dovrebbe apparire lì. Trova il log degli errori e guarda cosa dice. Non esiste una posizione standard per i file di registro. Cerca nella configurazione del server la loro posizione, o chiedi all’amministratore del server. Puoi anche utilizzare strumenti come CGI :: Carp per conservare i tuoi file di registro.

Quali sono le autorizzazioni dello script?

Se vedi errori come “Autorizzazione negata” o “Metodo non implementato”, probabilmente significa che lo script non è leggibile ed eseguibile dall’utente del server web. Sui sapori di Unix, si consiglia di cambiare la modalità in 755: chmod 755 filename . Non impostare mai una modalità su 777!

Stai usando un use strict ?

Ricorda che Perl crea automaticamente delle variabili quando le usi per la prima volta. Questa è una funzionalità, ma a volte può causare bug se si digita in modo errato un nome di variabile. L’ use strict pragma ti aiuterà a trovare questo tipo di errori. È fastidioso fino a quando non ti ci abitui, ma la tua programmazione migliorerà in modo significativo dopo un po ‘e sarai libero di fare errori diversi.

Lo script è compilato?

È ansible verificare gli errori di compilazione utilizzando l’ -c . Concentrati sui primi errori riportati. Risciacquare, ripetere. Se si verificano errori davvero strani, verificare che lo script abbia i finali di linea giusti. Se si FTP in modalità binaria, checkout da CVS, o qualcos’altro che non gestisce la traduzione di fine linea, il web server potrebbe vedere il tuo script come una grande linea. Trasferisci gli script Perl in modalità ASCII.

Lo script si lamenta di dipendenze insicure?

Se il tuo script si lamenta di dipendenze insicure, probabilmente stai usando l’ -T per triggersre la modalità taint, che è una buona cosa poiché ti mantiene passare i dati non controllati alla shell. Se si lamenta, sta facendo il suo lavoro per aiutarci a scrivere script più sicuri. Tutti i dati provenienti da fuori dal programma (cioè l’ambiente) sono considerati contaminati. Le variabili d’ambiente come PATH e LD_LIBRARY_PATH sono particolarmente fastidiose. Devi impostarli su un valore sicuro o distriggersrli completamente, come raccomando. Dovresti comunque usare percorsi assoluti. Se il controllo della contaminazione si lamenta di qualcos’altro, assicurati di aver scoperto i dati. Vedi la pagina man perlsec per i dettagli.

Cosa succede quando lo si esegue dalla riga di comando?

Lo script restituisce ciò che ti aspetti quando viene eseguito dalla riga di comando? L’output dell’intestazione è il primo, seguito da una riga vuota? Ricordare che STDERR può essere unito a STDOUT se si è su un terminale (ad esempio una sessione intertriggers) e a causa del buffering potrebbe apparire in un ordine confuso. Attiva la funzione autoflush di Perl impostando $| ad un vero valore. In genere potresti vedere $|++; nei programmi CGI. Una volta impostato, ogni stampa e scrittura andranno immediatamente all’output anziché essere bufferizzati. Devi impostare questo per ogni filehandle. Usa select per cambiare il filehandle predefinito, in questo modo:

 $|++; #sets $| for STDOUT $old_handle = select( STDERR ); #change to STDERR $|++; #sets $| for STDERR select( $old_handle ); #change back to STDOUT 

In entrambi i casi, l’output di prima cosa dovrebbe essere l’intestazione CGI seguita da una riga vuota.

Cosa succede quando lo si esegue dalla riga di comando con un ambiente simile alla CGI?

L’ambiente del server Web è in genere molto più limitato rispetto all’ambiente della riga di comando e contiene informazioni aggiuntive sulla richiesta. Se il tuo script funziona correttamente dalla riga di comando, potresti provare a simulare un ambiente di server web. Se il problema appare, hai un problema con l’ambiente.

Annulla o rimuovi queste variabili

  • PATH
  • LD_LIBRARY_PATH
  • tutte le variabili ORACLE_*

Imposta queste variabili

  • REQUEST_METHOD (impostato su GET , HEAD o POST come appropriato)
  • SERVER_PORT (impostato su 80, in genere)
  • REMOTE_USER (se stai facendo accessi protetti)

Le versioni recenti di CGI.pm (> 2.75) richiedono il flag -debug per ottenere il vecchio (utile) comportamento, quindi potrebbe essere necessario aggiungerlo alle importazioni CGI.pm

 use CGI qw(-debug) 

Stai usando die() o warn ?

Queste funzioni vengono stampate su STDERR meno che non siano state ridefinite. Non producono nemmeno un’intestazione CGI. Puoi ottenere la stessa funzionalità con pacchetti come CGI :: Carp

Cosa succede dopo aver svuotato la cache del browser?

Se pensi che il tuo script stia facendo la cosa giusta, e quando esegui manualmente la richiesta ottieni il risultato giusto, il colpevole potrebbe essere il browser. Svuota la cache e imposta la dimensione della cache su zero durante il test. Ricorda che alcuni browser sono davvero stupidi e in realtà non ricaricheranno nuovi contenuti anche se gli dici di farlo. Ciò è particolarmente diffuso nei casi in cui il percorso dell’URL è lo stesso, ma il contenuto cambia (ad esempio, le immagini dinamiche).

La sceneggiatura è quella in cui pensi che sia?

Il percorso del file system per uno script non è necessariamente direttamente correlato al percorso dell’URL per lo script. Assicurati di avere la directory giusta, anche se devi scrivere un breve script di test per testarlo. Inoltre, sei sicuro di voler modificare il file corretto? Se non vedi alcun effetto con le tue modifiche, potresti modificare un file diverso o caricare un file nel posto sbagliato. (Questo è, a proposito, la causa più frequente di tali problemi;)

Stai usando CGI.pm o un suo derivato?

Se il tuo problema è legato all’analisi dell’ingresso CGI e non stai utilizzando un modulo ampiamente testato come CGI.pm , CGI::Request , CGI::Simple o CGI::Lite , usa il modulo e vai avanti con la vita. CGI.pm ha una modalità di compatibilità cgi-lib.pl che può aiutarti a risolvere i problemi di input dovuti a implementazioni più vecchie del parser CGI.

Hai usato percorsi assoluti?

Se stai eseguendo comandi esterni con system , back tick o altri servizi IPC, dovresti utilizzare un percorso assoluto per il programma esterno. Non solo sai esattamente cosa stai correndo, ma eviti anche alcuni problemi di sicurezza. Se stai aprendo i file per leggere o scrivere, usa un percorso assoluto. Lo script CGI potrebbe avere un’idea diversa della directory corrente rispetto a quella dell’utente. In alternativa, puoi fare un esplicito chdir() per metterti nel posto giusto.

Hai controllato i tuoi valori di ritorno?

La maggior parte delle funzioni di Perl ti dirà se hanno funzionato o meno e imposteranno $! in caso di fallimento. Hai controllato il valore restituito ed esaminato $! per i messaggi di errore? Hai controllato [email protected] se stavi usando eval ?

Quale versione di Perl stai usando?

L’ultima versione stabile di Perl è 5.28 (o meno, a seconda di quando è stata modificata l’ultima volta). Stai usando una versione precedente? Versioni differenti di Perl possono avere idee diverse di avvertimenti.

Quale server web stai usando?

Server diversi possono agire diversamente nella stessa situazione. Lo stesso prodotto server può agire in modo diverso con diverse configurazioni. Includere quante più informazioni possibili in qualsiasi richiesta di aiuto.

Hai controllato la documentazione del server?

I programmatori CGI seri dovrebbero sapere il più ansible sul server, includendo non solo le caratteristiche e il comportamento del server, ma anche la configurazione locale. La documentazione per il tuo server potrebbe non essere disponibile se stai utilizzando un prodotto commerciale. Altrimenti, la documentazione dovrebbe essere sul tuo server. Se non lo è, cercalo sul web.

Hai cercato negli archivi di comp.infosystems.www.authoring.cgi ?

Questo uso è utile ma tutti i buoni poster sono morti o sono scomparsi.

È probabile che qualcuno abbia già avuto il tuo problema e che qualcuno (forse me) abbia risposto in questo newsgroup. Sebbene questo newsgroup abbia passato il suo periodo di massimo splendore, a volte la saggezza raccolta dal passato può essere utile.

Puoi riprodurre il problema con un breve script di test?

Nei sistemi di grandi dimensioni, potrebbe essere difficile rintracciare un bug dal momento che stanno accadendo molte cose. Prova a riprodurre il comportamento problema con lo script più breve ansible. Conoscere il problema è la maggior parte della correzione. Questo potrebbe richiedere molto tempo, ma non hai ancora trovato il problema e le opzioni sono a corto. 🙂

Hai deciso di andare a vedere un film?

Sul serio. A volte possiamo essere così coinvolti nel problema che sviluppiamo il “restringimento percettivo” (visione a tunnel). Fare una pausa, prendere una tazza di caffè o far saltare in aria alcuni cattivi in ​​[Duke Nukem, Quake, Doom, Halo, COD] potrebbe darti la nuova prospettiva di cui hai bisogno per ri-affrontare il problema.

Hai vocalizzato il problema?

Seriamente di nuovo. A volte spiegando ad alta voce il problema ci porta alle nostre risposte. Parla con il pinguino (peluche) perché i tuoi colleghi non ascoltano. Se sei interessato a questo come un serio strumento di debug (e lo raccomando se non hai trovato il problema), potresti anche leggere The Psychology of Computer Programming .

Penso che anche CGI :: Debug sia degno di nota.

Mi chiedo come mai nessuno abbia menzionato l’opzione PERLDB_OPTS chiamata RemotePort ; anche se, ammettiamolo, non ci sono molti esempi funzionanti sul web ( RemotePort non è nemmeno menzionato in perldebug ) – ed è stato un po ‘problematico per me venire con questo, ma qui va (essendo un esempio di Linux).

Per fare un esempio corretto, prima avevo bisogno di qualcosa che potesse fare una simulazione molto semplice di un web server CGI, preferibilmente attraverso una singola riga di comando. Dopo aver trovato un semplice web server a linea di comando per l’esecuzione di cgis. (perlmonks.org) , ho trovato che l’ IO :: All – A Tiny Web Server fosse applicabile per questo test.

Qui, lavorerò nella /tmp ; lo script CGI sarà /tmp/test.pl (incluso sotto). Nota che il server IO::All servirà solo i file eseguibili nella stessa directory di CGI, quindi qui è richiesto chmod +x test.pl Quindi, per eseguire la normale esecuzione del test CGI, cambio directory in /tmp nel terminale ed eseguo il server web one-liner qui:

 $ cd /tmp $ perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })' 

Il comando webserver bloccherà il terminale e in caso contrario avvierà il server Web localmente (su 127.0.0.1 o localhost ) - successivamente, posso andare su un browser web e richiedere questo indirizzo:

 http://127.0.0.1:8080/test.pl 

... e dovrei osservare le print fatte da test.pl caricate - e mostrate - nel browser web.


Ora, per eseguire il debug di questo script con RemotePort , è necessario innanzitutto un listener sulla rete, attraverso il quale interagiremo con il debugger Perl; possiamo usare lo strumento da riga di comando netcat ( nc , visto che qui: Perl 如何 remote debug? ). Quindi, per prima cosa eseguire il listener netcat in un unico terminale, dove bloccherà e attenderà le connessioni sulla porta 7234 (che sarà la nostra porta di debug):

 $ nc -l 7234 

Quindi, vorremmo che perl iniziasse in modalità debug con RemotePort , quando è stato chiamato il test.pl (anche in modalità CGI, attraverso il server). Questo, in Linux, può essere fatto usando il seguente script "shebang wrapper" - che qui deve anche essere in /tmp , e deve essere reso eseguibile:

 cd /tmp cat > perldbgcall.sh <<'EOF' #!/bin/bash PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '[email protected]'" EOF chmod +x perldbgcall.sh 

Questa è una specie di cosa complicata - vedi script di shell - Come posso usare le variabili di ambiente nel mio shebang? - Scambio di stack Unix e Linux . Ma il trucco qui sembra non essere quello di biforcarsi all'interprete perl che gestisce test.pl - quindi una volta che l'abbiamo colpito, non eseguiamo exec , ma chiamiamo perl "plainly", e fondamentalmente "source" il nostro test.pl script using do (vedi Come faccio a eseguire uno script Perl all'interno di uno script Perl? ).

Ora che abbiamo perldbgcall.sh in /tmp - possiamo cambiare il file test.pl , in modo che si riferisca a questo file eseguibile sulla sua riga shebang (invece del solito interprete Perl) - qui è /tmp/test.pl modificato in tal modo:

 #!./perldbgcall.sh # this is test.pl use 5.10.1; use warnings; use strict; my $b = '1'; my $a = sub { "hello $b there" }; $b = '2'; print "YEAH " . $a->() . " CMON\n"; $b = '3'; print "CMON " . &$a . " YEAH\n"; $DB::single=1; # BREAKPOINT $b = '4'; print "STEP " . &$a . " NOW\n"; $b = '5'; print "STEP " . &$a . " AGAIN\n"; 

Ora, test.pl e il suo nuovo gestore shebang, perldbgcall.sh , sono in /tmp ; e abbiamo nc ascolta le connessioni di debug sulla porta 7234 - così possiamo finalmente aprire un'altra finestra di terminale, cambiare directory in /tmp ed eseguire il server web one-liner (che ascolterà le connessioni web sulla porta 8080) lì:

 cd /tmp perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })' 

Dopo averlo fatto, possiamo andare al nostro browser web e richiedere lo stesso indirizzo, http://127.0.0.1:8080/test.pl . Tuttavia, ora quando il server web tenta di eseguire lo script, lo farà tramite perldbgcall.sh shebang - che avvierà perl nella modalità di debugger remoto. Pertanto, l'esecuzione dello script si interromperà e quindi il browser si bloccherà, in attesa di dati. Ora possiamo passare al terminale netcat , e dovremmo vedere il familiare testo debug del Perl - tuttavia, l'output attraverso nc :

 $ nc -l 7234 Loading DB routines from perl5db.pl version 1.32 Editor support available. Enter h or `hh' for help, or `man perldebug' for more help. main::(-e:1): do './test.pl' DB<1> r main::(./test.pl:29): $b = '4'; DB<1> 

Come mostra lo snippet, ora usiamo nc come "terminale", quindi possiamo digitare r (e Invio) per "run" - e lo script verrà eseguito fai l'istruzione breakpoint (vedi anche in perl, qual è la differenza tra $ DB :: single = 1 e 2? ), prima di fermarsi di nuovo (nota a quel punto, il browser si bloccherà ancora).

Quindi, ora possiamo, per esempio, passare attraverso il resto di test.pl , attraverso il terminale nc :

 .... main::(./test.pl:29): $b = '4'; DB<1> n main::(./test.pl:30): print "STEP " . &$a . " NOW\n"; DB<1> n main::(./test.pl:31): $b = '5'; DB<1> n main::(./test.pl:32): print "STEP " . &$a . " AGAIN\n"; DB<1> n Debugged program terminated. Use q to quit or R to restart, use o inhibit_exit to avoid stopping after program termination, hq, h R or ho to get additional info. DB<1> 

... tuttavia, anche a questo punto, il browser blocca e attende i dati. Solo dopo aver chiuso il debugger con q :

  DB<1> q $ 

... il browser smette di bloccare e visualizza infine l'output (completo) di test.pl :

 YEAH hello 2 there CMON CMON hello 3 there YEAH STEP hello 4 there NOW STEP hello 5 there AGAIN 

Ovviamente, questo tipo di debug può essere fatto anche senza eseguire il server web - tuttavia, la cosa bella qui, è che non tocchiamo affatto il web server; inneschiamo l'esecuzione "nativamente" (per CGI) da un browser web - e l'unica modifica necessaria nello script CGI stesso, è il cambio di shebang (e, naturalmente, la presenza dello script wrapper shebang, come file eseguibile nello stesso directory).

Bene, spero che questo aiuti qualcuno - sicuramente mi sarebbe piaciuto incappare in questo, invece di scriverlo da solo :)
Saluti!

Stai usando un gestore di errori mentre esegui il debug?

die dichiarazioni die e altri errori di run-time e compilazione fatali vengono stampati su STDERR , che può essere difficile da trovare e può essere confuso con messaggi provenienti da altre pagine Web sul tuo sito. Mentre esegui il debug del tuo script, è una buona idea che i messaggi di errore fatali vengano visualizzati nel browser in qualche modo.

Un modo per farlo è chiamare

  use CGI::Carp qw(fatalsToBrowser); 

nella parte superiore del tuo script. Quella chiamata installerà un gestore $SIG{__DIE__} (vedi perlvar ) e visualizzerà errori fatali nel browser, anteponendolo con un’intestazione valida se necessario. Un altro trucco di debug in CGI che ho usato prima che io abbia mai sentito parlare di CGI::Carp stato quello di utilizzare eval con le strutture DATA e __END__ sullo script per catturare gli errori in fase di compilazione:

  #!/usr/bin/perl eval join'', ; if ([email protected]) { print "Content-type: text/plain:\n\nError in the script:\[email protected]\n; } __DATA__ # ... actual CGI script starts here 

Questa tecnica più prolissa ha un leggero vantaggio rispetto a CGI::Carp in quanto cattura più errori in fase di compilazione.

Aggiornamento: non l’ho mai usato, ma sembra CGI::Debug , come suggerito da Mikael S, è anche uno strumento molto utile e configurabile per questo scopo.

Per me, io uso log4perl . È abbastanza utile e facile.

 use Log::Log4perl qw(:easy); Log::Log4perl->easy_init( { level => $DEBUG, file => ">>d:\\tokyo.log" } ); my $logger = Log::Log4perl::get_logger(); $logger->debug("your log message"); 

Onestamente puoi fare tutte le cose divertenti sopra questo post. Sebbene la soluzione più semplice e protriggers che ho trovato fosse semplicemente “stamparla”.

Nell’esempio: (codice normale)

 `$somecommand`; 

Per vedere se sta facendo quello che voglio veramente fare: (Risoluzione dei problemi)

 print "$somecommand"; 

Probabilmente vale anche la pena ricordare che Perl ti dirà sempre su quale riga si verifica l’errore quando esegui lo script Perl dalla riga di comando. (Una sessione SSH per esempio)

Lo farò di solito se tutto il resto fallisce. Inserirò SSH nel server ed eseguirò manualmente lo script Perl. Per esempio:

 % perl myscript.cgi 

Se c’è un problema allora Perl te lo dirà. Questo metodo di debug elimina tutti i problemi relativi ai permessi dei file o i problemi del browser web o del server web.

È ansible eseguire il perl cgi-script nel terminale utilizzando il comando seguente

  $ perl filename.cgi 

Interpreta il codice e fornisce risultati con codice HTML. Segnalerà l’eventuale errore.