Zurück zur Übersicht "CGI mit Perl"
  

Das Common Gateway Interface (CGI)
eine wichtige Community-Komponente
von A. Grupp

Zu jeder Community gehört sicherlich ein WWW-Server. Er kommt auf jeden Fall bei der Veröffentlichung von Chat-Terminen oder als Download-Server für interaktive Java-Applets zum Einsatz. Darüber hinaus besteht natürlich die Möglichkeit den WWW-Server selbst als interaktives Instrument zu benutzen. Neben dem üblichen Abrufen von vorgefertigten HTML-Seiten kann der Server nämlich, mit Hilfe des "Common Gateway Interface (CGI)", auch eine interaktive Kommunikation mit dem Anwender durchführen.

Um das CGI besser von normalen HTML-Seiten unterscheiden zu können, werden die beiden Varianten nachfolgend getrennt voneinander betrachtet.

Abruf statischer HTML-Seiten von einem WWW-Server

Häufig handelt es sich bei den Inhalten des WWW um nicht dynamische Informationen. Diese Informationen wurden vom jeweiligen Autor als statische Datei im HTML-Format erzeugt und im Dateisystem des Webservers abgelegt. Der Webserver selbst ist ein Rechner auf dem die Webserver-Software, auch http-Dämon (httpd) genannt, gestartet wurde. Diese Serversoftware wartet auf Anfragen von Clients aus dem Internet und reagiert entsprechend ihrer Konfiguration.

Einzelschritte beim Abruf einer statischen HTML-Seite:

  1. Über den Browser des Clients wird per http eine Anfrage an einen Webserver abgeschickt. Der Benutzer selbst gibt dabei eine URL an die vom Browser in eine http-Anfrage verpackt wird.
    Beispiel einer http-Anfrage durch den Browser (das kann man z.B. mit einem beliebigen Telnet-Client über Port 80 des Servers nachvollziehen):

    GET /smv/index.html HTTP/1.1
    Host: www.nowhere.com

    Anmerkung: Die angeforderte Ressource (hier /smv/index.html) muss mindestens aus einem Slash "/" bestehen. Die Ressource darf nicht wie in einem URI mit Protokollinformationen (http://) und dem Hostnamen versehen sein. Die Host-Angabe beinhaltet nur den Rechnernamen - keine Protokollinformationen!

  2. Der Webserver überprüft anhand seiner Konfiguration, ob es sich um eine "normale" HTML-Datei handelt. Im vorliegenden Szenario soll das erst mal der Fall sein. Entsprechend sucht der Webserver die angeforderte Information (als Datei) in seinem Dateisystem.

  3. Die HTML-Datei wird vom Webserver eingelesen.

  4. Anschließend wird die ursprüngliche Anfrage beantwortet. Zu einer vollständigen Antwort gehört der http-Header (gibt z.B. den Typ der nachfolgenden Datei oder das "Haltbarkeitsdatum" der Information an) und die eigentliche Information.

Abruf dynamischer Seiten aus dem CGI-Bereich eines WWW-Servers

Aus Sicht des Clients bleibt alles wie gehabt. Er stellt eine Anfrage an den Webserver und erhält eine entsprechende Rückantwort im HTML-Format. Beim Server liegen die Dinge aber anders. Im Gegensatz zum vorigen Abschnitt, gibt es die angefordert HTML-Datei nämlich gar nicht. Die angeforderte Seite ist in Wirklichkeit der Name eines Programms das ebenfalls im Dateisystem des Webservers abgelegt ist. Der Webserver schickt nun aber nicht das Programm an den Client. Der könnte damit normalerweise auch nichts anfangen. Er startet vielmehr das Programm und stellt diesem eine entsprechende Arbeitsumgebung zur Verfügung. Das Programm wurde natürlich für eine spezielle Aufgabe programmiert, die es nun erfüllt. Zusätzlich gibt das Programm Informationen über sein Arbeitsergebnis aus. Im Unterschied zu einem normalen Programm formatiert es jedoch seine Ausgabe mit Hilfe von HTML-Anweisungen. Diese Ausgabe empfängt nun der Webserver. Um eine gültige HTTP-Antwort (Response) zu erzeugen, wird vom Server zuerst ein HTTP-Header an den Client gesendet. Anschliessend wird die vom Programm erzeugte Ausgabe an den Client weitergeleitet.

So richtig interaktiv kann dieser Vorgang natürlich nur werden, wenn der Benutzer des Clients dem Programm auch Eingabe- bzw. Verarbeitungsdaten übermitteln kann. Dies erfolgt im WWW z.B. mit Hilfe eines HTML-Formulars. Nachdem der Benutzer das Formular ausgefüllt hat, wird eine Schaltfläche zum Absenden betätigt. Technisch gesehen handelt es sich, wie bereits in den obigen Fällen, um eine http-Anfrage. Allerdings werden zusätzlich die vom Benutzer ausgefüllten Formulardaten an den Webserver übermittelt:

Beispiel 1 (bei Formularen mit GET-Methode durch Anhängen der Daten an den URL):

GET /cgi-bin/test.pl?nn=Meier&vn=Rolf HTTP/1.1
Host: www.nowhere.com

Beispiel 2 (bei Formularen mit POST-Methode durch Anfügen eines Entity-Bodys an den HTTP-Header):

POST /cgi-bin/test.pl HTTP/1.1
Host: www.nowhere.com
Content-Length: 16

nn=Meier&vn=Rolf

Der Ablauf im Überblick:

Betrachten wir nun den Ablauf nochmals in Einzelschritten:

  1. Der Benutzer erhält während seines Aufenthalts im WWW ein Formular angezeigt. Er füllt das Formular aus und betätigt eine Schaltfläche zum Absenden des ausgefüllten Formulars. Diese Schaltfläche hat ähnliche Eigenschaften wie ein normaler Hyperlink.

  2. Der Browser erstellt eine http-Anfrage an den Webserver und übermittelt zusätzlich die vom Benutzer erstellten Formulardaten.

  3. Der Webserver erhält die Anfrage. Wie bereits weiter oben beschrieben überprüft er mit Hilfe seiner Konfigurationsdaten, ob es sich um eine normale HTML-Datei handelt. Im vorliegenden Fall ist die angeforderte Seite ein CGI-Programm. Insofern prüft der Webserver hier die Existenz des angeforderten Programms im Dateisystem.

  4. Der Webserver startet das Programm auf Betriebssystemebene und stellt im eine entsprechende Arbeitsumgebung zur Verfügung. Diese beinhaltet u.a.:

  5. Das Programm erledigt nun die ihm zugedachte (einprogrammierte) Aufgabe. Es verwendet dazu zusätzlich die Informationen aus den vorhandenen Umgebungsvariablen bzw. des Eingabekanals. Die Rückmeldungen versieht das Programm mit HTML-Anweisungen und gibt sie über das Ausgabemedium aus.

  6. Das Ausgabemedium ist aber der Webserver. Er empfängt die mit HTML angereicherte Ausgabe des Programms und leitet sie, mit vorangestelltem HTTP-Header, an den Client weiter.

    Nachfolgend der Start einer derartigen HTTP-Antwort bis zum <HTML>-Tag (Rest der HTML-Datei wurde weggelassen).

    HTTP/1.1 200 OK
    Date: Wed, 05 Apr 2000 14:49:50 GMT
    Server: Apache/1.3.11 (Unix) PHP/3.0.12 mod_perl/1.21
    Expires: Wed, 05 Apr 2000 15:50:37 GMT
    Transfer-Encoding: chunked
    Content-Type: text/html
    
    ee5
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
    <HTML>

Wie erstelle ich CGI-Programme?

Prinzipiell kann jedes Programm oder Skript auch im CGI-Bereich verwendet werden. Das Programm muß nur in der Lage sein

Damit sind z.B. folgende Formen von Programmen verwendbar:

Die am häufigsten verwendete Lösung im CGI-Bereich sind Perl-Skripte. Dies liegt an den Fähigkeiten der Programmiersprache Perl. Sie besitzt sämtliche Elemente der populären Hochsprachen, hat eine einfache Art der Variablenverwaltung zu bieten und ist seit Version 5 auch objektorientiert programmierbar. Perl ist zudem unter der GNU Public License als Freeware verfügbar und zwischenzeitlich auf beinahe allen Betriebssystemen im WWW-Bereich kostenlos verfügbar (z.B. sämtliche Unix-Varianten, Microsoft NT, Win9x, MacOS, ...).

Verwendung von Perl-Skripten unter CGI

Nach der Installation von Perl im Betriebssystem ist in Zusammenarbeit mit einem beliebigen Texteditor die Entwicklungsumgebung bereits vollständig. Der Quellcode wird als normale ASCII-Datei verfaßt und abgespeichert. Bei Betriebssystemen die den Inhalt einer Datei an der Dateiendung erkennen, sollte .pl als Endung verwendet werden (z.B. test.pl). Bei allen Unix-Varianten wird die bereits von Shell-Skripten bekannte Art verwendet. Dazu wird in der ersten Zeile des Programms eine spezielle "Kommentar"-Anweisung untergebracht. Für Perl wäre dies:

#!/usr/bin/perl

Auch beim Programmieren auf anderen OS-Plattformen sollte dieser Syntax verwendet werden. Derartige Perl-Skripte sind nämlich ohne jegliche Änderung auch auf beliebigen anderen Systemen verwendbar. Außerdem sind zusätzliche Interpreter-Steuer-Schalter in dieser Zeile angebar die auch von nicht Unix-Systemen eingelesen werden.

Nun gilt es noch ihren Webserver für die Verwendung von CGI zu konfigurieren, die Perl-Skripte an der richtigen Stelle im Dateisystem abzulegen und evtl. noch mit den richtigen Rechten zu versehen.

"Gerippe" eines CGI-Perl-Skripts

Jedes CGI-Perl-Skript (ohne Verwendung externer Bibliotheksmodule) hat denselben Grundaufbau. Das Skript muß nämlich zuerst die übermittelten Formulardaten einlesen, diverse MIME-Dekodierungen rückgängig machen, Sicherheitschecks durchführen und die so aufbereiteten Formulardaten in einer geeigneten Variablen abspeichern.

Nachfolgend die einzelnen Bestandteile des Skripts in kleinen Häppchen.
#!/usr/bin/perl -w -T
# -w => ausfuehrlichere Warnungen/Fehlermeldungen
# -T => Sicherheitseinstellung: Tainted Mode

Wie bereits weiter oben erläutert wird der Perl-Interpreter in Unix-Systemen auf diese Art aufgerufen. Die Pfadangabe wird von Nicht-Unix-Systemen ignoriert. Nicht so die zusätzlichen Schalter -w und -T. Der Schalter -w sorgt für etwas ausführlichere Warnungen und Fehlermeldungen. Der Schalter -T sorgt dafür, daß der Perl-Interpreter im tainted Mode läuft. Kurz und nicht ausreichend kann man sagen, daß der Perl-Interpreter in diesem Modus den Benutzereingaben mißtraut. Er reicht solche Parameter nicht an Betriebssystembefehle weiter. Der Schalter kann das Programmieren erschweren ist aber aus Sicherheitsgründen zu empfehlen. Achtung: Verwenden sie den Schalter -T nicht falls ihre Skripte über mod_perl abgearbeitet werden. In diesem Fall müssen sie den Tainted Mode über die WWW-Server-Konfigurationsdatei einstellen.
# Perl-Pragma um u.a. Variablendeklaration zu erzwingen
# Insbesondere in Hinblick auf Apache-httpd mit mod_perl
# zu empfehlen.
use strict; 

Eigentlich muß man in Perl keine Variablen deklarieren (einen Variablentyp gibt es sowieso nicht). Um aber Nebeneffekte durch zu große Lebensdauer der Variablen oder Mehrfachverwendung des gleichen Variablennamens zu vermeiden, empfiehlt sich auch hier der Einsatz dieser Einschränkung. Zunehmend werden auch Webserver mit integriertem Perl-Interpreter (z.B. Apache httpd mit mod_perl) eingesetzt. Diese Webserver übersetzen den Perl-Code nur beim ersten Aufruf und halten ihn anschließend im Hauptspeicher um die Ausführungsgeschwindigkeit zu erhöhen. Leider werden dabei auch die Variableninhalte zwischen unabhängigen Aufrufen gespeichert. Dies ist nur durch die Einschränkung der Variablenlebensdauer zu vermeiden.
# Variablendeklaration
my ($daten, @formulardaten, $formfeld, $name, $wert, %feld); 

Die Variablen werden mit my nach dem obigen Syntax deklariert. Außerdem wird damit ihre Lebensdauer auf den einmaligen Skriptablauf beschränkt.
# Je nach Formular-Method (GET/POST/PUT) werden die Formulardaten
# in die skalare Variable $daten eingelesen.
if($ENV{'REQUEST_METHOD'} eq 'GET'){
   # Formular hat GET-Methode verwendet
   $daten = $ENV{'QUERY_STRING'};
}
else{
   # Formular hat POST-Methode verwendet
   read(STDIN, $daten, $ENV{'CONTENT_LENGTH'} );
} 

Jedes HTML-Formular kann zum Versenden seiner Formulardaten die Methoden GET oder POST verwenden.

Welche Methode das Formular verwendet hat, erfährt das Skript über die Umgebungsvariable "REQUEST_METHOD".

Üblicherweise stammt das HTML-Formular aus der gleichen "Feder" wie das zugehörige Verarbeitungs-Skript. Insofern muß der Programmierer nur eine Methode implementieren. Die obige Variante ist also eine Universallösung die selbständig zwischen den beiden Formen unterscheiden kann.
# Aufspaltung der Formulardaten in die einzelnen Felder
@formulardaten = split(/&/, $daten); 

Unabhängig von der verwendeten Methode, übermittelt der Client-Browser die Formulardaten in folgendem Format:

<Feldname1>=<Feldwert1>&<Feldname2>=<Feldwert2>& ...

Mit Hilfe der Split-Anweisung werden die einzelnen Felder nun aus diesem String herausgelöst und in das eindimensionale Array @formulardaten geschrieben. Der Inhalt des Arrays sieht dann folgendermaßen aus:

$formulardaten[1]= <Feldname1>=<Feldwert1>
$formulardaten[2]= <Feldname2>=<Feldwert2>
...
# Jedes Formularfeld einzeln bearbeiten und Ergebnis
# im Hash $feld ablegen.
foreach $formfeld (@formulardaten){
   # Aufspaltung eines Felds in Feldname und -wert
   ($name, $wert) = split(/=/, $formfeld);
   # MIME-Kodierung der Leerzeichen aufheben
   $wert =~ tr/+/ /;
   # MIME-Kodierung von Sonderzeichen aufheben<
   $wert =~ s/%([a-fA-F0-9].)/pack("C", hex($1))/eg;
   # XSSI-Anweisungen entfernen!  Sicherheit!!!
   $wert =~ s/<!--(.|\n)*-->//g;
   # Neue Hashzeile mit Feldname und gefiltertem Feldwert
   $feld{$name}=$wert;
} 

Die einzelnen Werte des Arrays $formulardaten müssen nun noch in Feldname und Feldwert aufgetrennt werden. Um das ganze Array elegant abzuarbeiten wird die Perl-Kontrollstruktur foreach() verwendet. Dabei wird jeweils ein Arrayelement aus dem Array extrahiert und hier der Variablen $formfeld zugewiesen. Danach wird der Schleifenkörper abgearbeitet. Innerhalb des Schleifenkörpers werden die folgenden Einzelschritte durchgeführt:


# Content-Type und Headerende des http-Headers erstellen
print "Content-Type: text/html\n\n";
# Eroeffnende HTML-Anweisungen ausgeben
print "<HTML><HEAD><TITLE>CGI-Test</TITLE></HEAD><BODY>\n";
print "<H1>&Uuml;bergebene Formulardaten</H1>\n";
# Die Daten jedes Formularfelds in eigener Zeile ausgeben
foreach $name (keys(%feld)){
   print "Inhalt des Felds $name ist $feld{$name}<BR>\n";
}
# HTML-Ausgabe abschliessen
print "</BODY></HTML>\n";

Nun wird noch der letzte Teil des http-Headers (Content-Type gefolgt von einer Leerzeile) erzeugt und die HTML-formatierte Ausgabe erzeugt. Die verwendeten Perl-Anweisungen sind bis auf keys() bereits erklärt. keys() extrahiert aus einem assoziativen Feld (Hash) die Schlüsselspalte und liefert als Rückgabewert ein eindimensionales Feld.


Bis auf den letzten Teil sieht jedes Perl-Skript das keine Bibliotheken verwendet identisch aus. Der letzte Teil ist von der Aufgabe des Perl-Skripts abhängig und muß individuell erstellt werden. Denkbar sind hier eine Fülle von Aufgaben:

...


Eine Fülle derartiger Skripte ist im Internet als Free-, Share- und Löhnware verfügbar.

Weitere Informationen


 Lokal bzw. Remote-Zugriffe seit dem 17. April 1999