Den Ausgaben normaler CGI-Skripts schickt der Web-Server immer einen Header-Leporello mit HTTP-Protokoll-Informationen voraus. Ein absolut minimales CGI-Skript wie
#!/usr/bin/perl -w print "Content-Type: text/html\r\n\r\n"; print "Hello, CGI!\n";
erzeugt zwar nur eine minimale Ausgabe, schickt aber an den Browser etwa mit
HTTP/1.0 200 OK Connection: close Date: Sat, 04 Apr 1998 05:51:35 GMT Server: Apache/1.2.4 mod_perl/1.05 Content-Type: text/html Client-Date: Sat, 04 Apr 1998 05:51:37 GMT
Hello, CGI!
eine Statusmeldung (HTTP/1.0 200 OK
) und -- zusätzlich zum explizit
angegebenen Content-type
-- eine Reihe von informativen Headern, die
die Art der Verbindung, den Servertyp und das Datum auf Client- und
Serverseite festlegen. Möchte man auf diese Angaben Einfluß nehmen,
muß das Skript im NPH-(Non-Parsed-Header)-Modus laufen, in welchem
es explizit auf diese Leistung des Servers verzichtet und alles
selbst in die Hand nimmt -- ohne Netz und doppelten Boden.
Fängt der Name der Skript-Datei mit nph-...
an, verzichtet
der Server (z. B. der Apache) darauf, seine eigenen Header vor die
eigentliche Ausgabe
eines Skripts zu knallen oder die Ausgabe zu puffern.
Vielmehr gelangen alle Daten, sobald das Skript sie ausgibt, ungefiltert
zum Browser.
Hieraus ergeben sich interessante Anwendungen: Statt die Seite im Browser auf einen Schlag aufzubauen, kann man die Daten schrittweise hineinladen. Das dynamische Fortschreiben der Daten vermittelt dem Brower-Benutzer bei langlaufenden CGI-Skripts den Eindruck, daß es vorwärts geht und nicht etwa der Server hängt oder das Netz dicht ist.
Als völlig sinnloses Beispiel sei in diesem Zusammenhang das
Countdown-Skript nph-append.pl
vorgestellt, das nichts anderes tut,
als im Sekundentakt
zuerst 2
, dann 1
, und dann Boom!
anzuzeigen. Da weder der
Server die Ausgabe puffert (nph-Skript) noch das Skript selbst
($|
enthält einen wahren Wert), füllt sich die im Browser
angezeigte Seite schrittweise.
nph-append.pl
#!/usr/bin/perl -w ###################################################################### # Michael Schilli, 1998 (mschilli@perlmeister.com) ###################################################################### use CGI qw/:standard/; # header() exportieren $| = 1; # Ausgabe entpuffern print header(-nph => 1); # NPH-Header ausgeben print (h1("2")); # 2! sleep(1); print (h1("1")); # 1! sleep(1); print (h1("Boom!")); # Bazong!
nph-append.pl
nutzt natürlich Lincoln Steins allgegenwärtiges
CGI-Paket, das auch die Konstruktion von nph-Skripts unterstützt:
Erhält die header
-Funktion
zusammen mit der -nph
-Option einen wahren Wert überreicht, nimmt
sie die Ausgabe der Status-Meldung zusammen mit allen wichtigen Headern
in die Hand.
Beim sogenannten Server-Push erwartet der Browser vom Server nicht
nur ein Dokument,
sondern mehrere aufeinanderfolgende, von denen er jeweils nur das
neueste darstellt und bei ankommendem Nachschub das bisher dargestellte
einfach überlädt. Die Verbindung des Clients zum Server bleibt dabei
solange erhalten, bis auch das letzte Dokument eingetrudelt ist -- unter
Umständen eine Herausforderung für einen stark beschäftigten Web-Server,
also Vorsicht!
Den Startschuß hierzu liefert im Response-Header der
Eintrag multipart/x-mixed-replace
für den Content-type
.
Dieser spezifiziert gleichzeitig noch einen String, der eindeutig den
Übergang zwischen zwei Dokumenten festlegt. Er muß mit zwei
Gedankenstrichen (--
) beginnen, und so eindeutig sein, daß die
Zeichenfolge nirgends im nachfolgenden Dokument vorkommt.
Der letzte Trennstring des Multi-Dokuments erhält zusätzlich
noch "--"
angehängt. Listing nph-sp.pl
zeigt die Implementierung,
ins cgi-bin
-Verzeichnis und über einen Browser aufgerufen, liefert das
Skript
zunächst die Anzeige 2
, die nach einer Sekunde von 1
überladen
wird, worauf wiederum nach einer Sekunde schließlich Boom!
folgt.
nph-sp.pl
#!/usr/bin/perl -w ###################################################################### # Michael Schilli, 1998 (mschilli@perlmeister.com) ###################################################################### use CGI qw/:standard/; $| = 1; $separator = "DerEindeutigeTrenner"; print header(-nph => 1, # Top-Header -type => "multipart/x-mixed-replace;boundary=$separator" ); print "--$separator\n"; # Erster Trenner print header(-type => 'text/html'), "\n"; # Teil-Dokument print h1(2), "\n"; print "--$separator\n"; # Trenner sleep(1); print header(-type => 'text/html'), "\n"; # Teil-Dokument print h1(1), "\n"; print "--$separator\n"; # Trenner sleep(1); print header(-type => 'text/html'), "\n"; # Teil-Dokument print h1("Boom!"), "\n"; print "--$separator--\n"; # End-Header
Das gleiche Problem löst auch ein CGI-Skript, das den Zählerwert eines hereingereichten Query-Parameters anzeigt, diesen herunterzählt und sich selbst nach einer Sekunde wieder mit dem neuen Wert des Query-Parameters aufruft.
Findet der Browser am Beginn eines HTML-Dokuments die Sequenz
<META HTTP-EQUIV="Refresh" CONTENT="1; URL=http://host/cgi-bin/clientpull.pl?count=2">
veranlaßt ihn das, nach der im CONTENT
-Feld eingestellten Zeitspanne
(eine Sekunde) den ebenfalls dort spezifierten URL anzufordern. Im Beispiel
bezieht sich der URL auf das CGI-Skript, das die HTML-Seite erzeugt hat,
und sich so
nach der GET-Methode mit dem Wert 2
für den Parameter count
aufruft.
Aufgabe des CGI-Skripts ist es dann, eine neue Seite zu generieren, die
den oben dargestellten HTML-Tag mit einem um eins heruntergesetzten Zählerwert
repliziert -- und schon fängt der Countdown an zu laufen.
Zweitens besteht für den Server die Möglichkeit, in den
Response-Header einen Refresh-Eintrag einzupacken. Er hat für die
Nachladezeit 1s
und den URL des Skripts folgende Form:
Refresh: 1; URL=http://host/cgi-bin/clientpull.pl?count=2
Das Skript clientpull.pl
implementiert das geforderte Verhalten.
Solange der überlieferte Zählerwert noch größer als Null ist,
packt es die Nachlade-Anweisung in den Response-Header, bevor es den
aktuellen Zählerstand ausgibt. Die Environment-Variable SCRIPT_NAME
enthält (gemäß Server-Standard) den URL-Pfad des ausgeführten Skripts.
Erreicht der Zähler die Null, gibt clientpull.pl
vor der
letzten Ausgabe einen regulären Header aus und beendet so den
Nachlade-Reigen.
clientpull.pl
#!/usr/bin/perl -w ###################################################################### # Michael Schilli, 1998 (mschilli@perlmeister.com) ###################################################################### use CGI qw/:standard/; $count = param('count'); # CGI-Parameter abfragen $count ||= 3; # Parameter nicht gesetzt? Startwert. $count--; if($count) { print header(-Refresh => "1; URL=$ENV{SCRIPT_NAME}?count=$count"); print h1($count); } else { print header(); print h1("Boom!"); }
Der Redirect-Mechanismus dient dazu, Benutzer beim Klick auf ein Dokument automatisch (und unsichtbar) auf einen anderen URL umzuleiten. Der Server spuckt hierzu lediglich den neuen URL mitsamt einiger Header-Informationen aus, und zack! springt der Browser dorthin - ohne eine entsprechende Meldung anzuzeigen, nur die URL-Anzeige verändert sich. Verschiebt sich der Zugriffspfad einer Webseite oder zieht diese gar auf einen anderen Rechner um, lassen sich Benutzer, die noch den alten URL benutzen, zuverlässig umdirigieren.
Listing nph-redir.pl
zeigt, wie das mit CGI.pm
funktioniert: Die
Option -redirect
der Header-Funktion bewirkt das gewünschte Verhalten.
nph-redir.pl
#!/usr/bin/perl -w ###################################################################### # Michael Schilli, 1998 (mschilli@perlmeister.com) ###################################################################### use CGI qw/:standard/; print redirect(-nph => 1, -uri => 'http://other.host.com');
Allerdings kostet diese Art der Umleitung relativ viel Strom: Der Perl-Interpreter
muß hochfahren, das Skript parsen, CGI.pm
laden -- da können auf einem
lahmen PC schon mal zwei Sekunden verstreichen.
Thema einer der kommenden Folgen wird deswegen
der Apache-Server und mitsamt dem Modul mod_perl
sein. Diese Kombination
stellt eine Schnittstelle zur Verfügung,
um derlei Tricks bei minimaler Serverbelastung zu realisieren.
Damit ein Browser z.B. ein stündlich modifiziertes Dokument nicht über Gebühr in seinem Cache hält und rechtzeitig die neueste Version holt, setzt man einfach serverseitig das Verfallsdatum des Dokuments auf die Erscheinungszeit der neuen Version.
Das CGI-Skript expire.pl
zeigt zu Testzwecken den Stundenanteil
der aktuellen Uhrzeit an und teilt dem Client mit, daß das
Dokument zum nächsten Stundenwechsel seine Gültigkeit verliert.
expire.pl
#!/usr/bin/perl -w ###################################################################### # Michael Schilli, 1998 (mschilli@perlmeister.com) ###################################################################### use CGI qw/:standard/; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time()); $minutes_to_expiration = 60 - $min; print header(-expires => "+${minutes_to_expiration}m"); print h1("Die volle Stunde ist: $hour");
Hierzu rechnet expire.pl
die verbleibenden Minuten bis zum Anfang einer
neuen Stunde aus und schickt mittels der -expires
-Option
der header
-Funktion einen Expires-Header, der besagt,
daß der Browser das Dokument nach X Minuten nicht mehr
aus dem Cache hervorschummeln darf sondern tatsächlich vom Server holen muß.
Ist die aktuelle Uhrzeit auf dem Server etwa 07:18:05
, fehlen
bis zum Stundenwechsel noch etwa 42 Minuten und mit -expires => "+42m"
(die Option versteht die gleiche Syntax wie das letzten Monat vorgestellte
Cookie-expire
)
sehen die zwei ausschlaggebenden Header folgendermaßen aus:
Date: Sat, 18 Apr 1998 07:18:05 GMT Expires: Sat, 18 Apr 1998 08:00:05 GMT
Damit ist zwar keineswegs garantiert, daß der Browser das Dokument in seinem
Cache hält (manche Browser cachen keine CGI-Skripts oder haben
in den Options-Menüs die Cache-Strategie deaktiviert), doch falls er
den Mechanismus unterstützt, führt er bei
weiteren Aufrufen der URL http://host/cgi-bin/expire.pl zwischen
07:18:05
und 08:00:05
keinen Netzzugriff mehr aus, sondern
zeigt die olle Cache-Kopie. Danach oder beim Druck auf den Reload-Button
(bei manchen Browsern CTRL-Reload) holt er sich die neueste Version
vom Server.
So, damit ist erstmal Schluß mit dem CGI-Schmarr'n! Das nächste Mal gibt's einen Clipping-Agenten, der Nachrichten aus allerlei Zeitungen zusammensucht! Da werd's spitz'n! Bleibt's g'sund miteinand'!
Michael Schilliarbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com. |