Oliver: Error 401, 403, 404, 500 - Redirect

Hi!
Ich habe ein CGI-Script installiert, das die 401-, 404-, 403- und 500-Error-Fehler abfängt und den User auf eine jeweils eigene, dem Look & Feel der Site angepasste HTML-Seite umlenkt ("Redirect"). Dies erreicht man dadurch, dass man eine .htaccess-Datei ins Root-Verzeichnis erstellt mit folgendem Inhalt:

ErrorDocument 401 /cgi-bin/error_director/error.cgi?401
ErrorDocument 403 /cgi-bin/error_director/error.cgi?403
ErrorDocument 404 /cgi-bin/error_director/error.cgi?404
ErrorDocument 500 /cgi-bin/error_director/error.cgi?500

Der Redirect klappt beim 404-Error ("Seite nicht gefunden")auch gut. Diese Fehlermeldung erscheint ja, wenn der Besucher eine Seite aufruft, die es nicht oder nicht mehr gibt (vgl. http://www.story-development.de/index.html und http://www.story-development.de/index.shtml).
Nur beim 401-Error, der beim Versuch, eine passwortgeschützte Seite mit falschem oder ohne Passwort aufzurufen, zuschlägt, klappt das nicht korrekt, da zwar die neue Error-Seite aufgerufen wird, aber der Einlog-Dialog (Benutzername und Passwort) einfach übersprungen wird (z. B. http://www.story-development.de/members/html/members/) - d. h. der User *kann sich gar nicht einloggen*.
Eine Lösung wäre, wenn ich in der .htaccess direkt auf die entsprechende HTML-Error-Seite und nicht auf das Script verweise. Dann brauche ich aber gar nicht das Skript, das ja alles mitloggt und mir dann eine Mail schickt.

Weiss jemand Bescheid??

Liebe Grüße aus Berlin, Oliver

  1. Hi Olli!

    "If the ErrorDocument specifies a local redirect to a CGI script, the script should include a "Status:" header field in its output in order to ensure the propagation all the way back to the client of the error condition that caused it to be invoked."
    http://httpd.apache.org/docs/custom-error.html unten

    Also mal "Status: 401 Not Authorized\n" ausgeben und schauen, ob's klappt.

    HTH, So long

    1. Hi HTH!

      Danke!

      Also mal "Status: 401 Not Authorized\n" ausgeben und schauen, ob's klappt.

      Wo ausgeben (.htaccess; telnet oder ...) ?

      Liebe grüße, Oliver

      1. Hi HTH!

        Sorry! Hi Calocybe!

        1. Hi HTH!
          Sorry! Hi Calocybe!

          HTH heisst Hope that helps. Und um Deine Frage zu beantworten, natuerlich sollst Du das von dem Script ausgeben lassen, zusammen mit den anderen Headern wie z.B. dem Content-type. Aber das siehst Du eigentlich auf der verlinkten Seite. (Denk dran, doppelt \n erst nach dem letzten Header.)

          So long

          1. Und um Deine Frage zu beantworten, natuerlich sollst Du das von dem Script ausgeben lassen, zusammen mit den anderen Headern wie z.B. dem Content-type. Aber das siehst Du eigentlich auf der verlinkten Seite. (Denk dran, doppelt \n erst nach dem letzten Header.)

            Ich seh schon, Du bist ein wahrer Profi! Dein Vorschlag ist sicher richtig, nur bin ich leider kein Perl-Programmierer - das Script (Error Director) habe ich gedownloaded unter

            http://webmasters.winnfreenet.com/

            Wie gesagt, sonst klappt alles einwandfrei - auch die Mail mit den Error-Messages, die ich als Admin bekomme.
            Meinst Du, wenn ich Deinen Vorschlag dem Autor maile, kann der etwas damit anfangen? Oder kannst Du das gegen einen Obolus übernehmen? Oder gibt es noch einen anderen Weg - der Hoster Strato (... ja ich weiss ...)  ist nicht wirklich auskunfstfreudig.

            Nochmals danke!

            Oliver

            1. Hallo ihr zwei

              Oder kannst Du das gegen einen Obolus übernehmen?

              ich will ich will *fg* :o) 1000 DM und ich bin dein!

              Oder gibt es noch einen anderen Weg

              Selbermachen ;-)... ist auch nicht so schwer! Nimm Notepad, öffne das error.cgi, und suche nach:

              print "Status: 301 Found\n";

              Diese Zeile ersetzt du durch das was in Calocybes Link steht:
              printf "Status: %s Condition Intercepted\n", $ENV{"REDIRECT_STATUS"};

              [es ist mir zwar neu dass in Perl auch C-Syntax erlaubt ist, aber wird schon stimmen *g* schliesslich ist mir noch vieles neu in Perl]

              Ansonsten probier einfach
              print "Status: $ENV{'REDIRECT_STATUS'} Condition Intercepted\n";

              Die Frage ist nur: Es wird ja ein Status ausgegeben (301) ?!? Muss es denn genau der richtige sein, oder einfach "Hauptsache überhaupt"?

              lg bernhard

              1. Hi,

                [es ist mir zwar neu dass in Perl auch C-Syntax erlaubt ist,

                das ist es nicht. Allerdings gibt es einige Funktionen, die durchaus an C angelehnt sind - printf gehört dazu.

                Die Frage ist nur: Es wird ja ein Status ausgegeben (301) ?!? Muss es denn genau der richtige sein, oder einfach "Hauptsache überhaupt"?

                Ich halte es für sinnvoll, den richtigen Status auszugeben. Daß die Fehlerseite "Moved Permanently" ist, ist a) uninteressant und b) u.U. falsch... es führt dazu, daß entsprechend fähige Clients in Zukunft statt der ursprünglichen, evtl. nur kurzfristig fehlerhaften Seite gleich das Error-Script anfordern, was ja kaum im Sinne des Erfinders ist.

                Cheatah

                1. Ich habe Euren Ratschlag befolgt und die Zeile

                  print "Status: 301 Found\n";

                  ersetzt durch

                  print "Status: 401 Not Authorized\n";

                  Ergebnis: Der Login-Dialog wird aufgerufen - aber sehr zäh, jedenfalls sehr viel zäher, als wenn ich in der .htaccess auf ein HTML-Script verweisen und nicht das Script aufrufen würde. Nach dem dritten Mal wird leider nicht die 401-Error-Html-Datei aufgerufen. Es erscheint nur eine leere weiße Seite und in der Stausleiste "fertig"

                  Oliver (ratlos...)

                  1. natürlich nicht HTML-Script, sondern HTML-Datei

              2. Moin!

                Selbermachen ;-)... ist auch nicht so schwer! Nimm Notepad, öffne das error.cgi, und suche nach:
                print "Status: 301 Found\n";

                Das steht ja eben noch nicht drin, auch nicht in dieser hartcodierten Weise.

                Cheatah:

                Ich halte es für sinnvoll, den richtigen Status auszugeben. Daß die Fehlerseite "Moved Permanently" ist, ist a) uninteressant und b) u.U. falsch... es führt dazu, daß entsprechend fähige Clients in Zukunft statt der ursprünglichen, evtl. nur kurzfristig fehlerhaften Seite gleich das Error-Script anfordern, was ja kaum im Sinne des Erfinders ist.

                Dazu muesste das Error-Script noch einen Location-Header auf sich selbst ausgeben. Ohne diese Information waere eine 301/302-Antwort ziemlich unbrauchbar, obwohl laut RFC2068 offenbar standardkonform.

                So long

                1. Hallo Calocybe!

                  Selbermachen ;-)... ist auch nicht so schwer! Nimm Notepad, öffne das error.cgi, und suche nach:
                  print "Status: 301 Found\n";

                  Das steht ja eben noch nicht drin, auch nicht in dieser hartcodierten Weise.

                  Doch! Das ist es ja warum es mich so schaudert und wundert:
                  http://www.winnfreenet.com/webmasters/mmerror.html

                  Dazu muesste das Error-Script noch einen Location-Header auf sich selbst ausgeben. Ohne diese Information waere eine 301/302-Antwort ziemlich unbrauchbar, obwohl laut RFC2068 offenbar standardkonform.

                  Tut es ja auch:
                  --- Auschnitt ---
                  print "Status: 301 Found\n";
                  print "Location: $url_path/$code_page \n\n";
                  -----------------

                  ;-)

                  lg bernhard

                  1. Moin!

                    Doch! Das ist es ja warum es mich so schaudert und wundert:
                    http://www.winnfreenet.com/webmasters/mmerror.html

                    Da bin ich wohl der einzige, der das Script auf der Domain nicht gesucht und sich stattdessen ausschliessliech auf Olivers Angaben bezogen hat. *g* Na, ich werd's mir heut abend mal anschauen, jetzt muss ich erstmal wieder in die Uni. Aber bis dahin hat's sicher auch jemand anderes korrigiert.

                    So long

                    1. Moin!

                      (again)

                      Wenn nicht gestern, dann heute, was? Jetzt habe ich erstmal gesehen, dass das ja ziemlicher Quatsch ist, was in dem Script steht. Naja, ich hab's jetzt mal nicht gleich neu geschrieben, weil mir da die Lust fehlt, aber habe mal das wichtigste geaendert:

                      Zeile 26 alt:
                      $url_path  = "http://www.mydomain.com/error";
                      neu:
                      $errordocs_path = "/yourpath-to-your-errordocs";

                      Zeile 78ff alt:
                      print "Status: 301 Found\n";
                      print "Location: $url_path/$code_page \n\n";
                      neu:
                      {
                          local *ERRDOC;

                      if (open(ERRDOC, "<$errordocs_path/$code_page")) {
                              my $errdoc;
                              local $/; undef $/;

                      $errdoc = <ERRDOC>;
                              close(ERRDOC);

                      print "Status: $ENV{'QUERY_STRING'} $code\n";
                              print "Content-type: text/html\n\n";
                              print $errdoc;

                      } else {
                              print "Status: 500 Internal Server Error\n";
                              print "Content-type: text/html\n\n";
                              print qq~<HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>
                                <BODY>Processing your request could not be completed due to the error <I>$ENV{'QUERY_STRING'} - $code</I>.<BR>
                                Additionally, an internal server error occurred while generating an approriate error document.<BR>
                              ~
                          }
                      }

                      Ist nicht getestet, aber das kriegt Oliver sicher alleine hin. *g*

                      So long

                      1. Zeile 26 alt:
                        $url_path  = "http://www.mydomain.com/error";
                        neu:
                        $errordocs_path = "/yourpath-to-your-errordocs";

                        P.S. Und dann muss da natuerlich das Verzeichnis eingetragen werden, in dem die HTML-Fehlerdateien liegen, und zwar OHNE / am Ende. Und vor allem: Das ist der Pfad auf der Festplatte, nicht der ueber HTTP! (Sollte ueber HTTP sowieso nicht erreichbar sein.) Und falls unter Windows: Statt \ trotzdem / benutzen.

                        So long

                        1. Danke für Deine / Eure ungeheure Mühe - das hätte ich nun wirklich nicht erwartet! Ich hatte längst gedacht, das Thema hätte sich erledigt. Dann schaue ich heute (3.6.) hier herein - und finde eine lebhafte Diskussion... Das meiste (Perl) habe ich zwar nicht verstanden, aber Hauptsache, es hilft :-D.

                          Ich habe die Veränderungen von Calocybe (30.5) übernommen und -

                          ES FUNKTIONIERT!!!! Jetzt muss ich nur noch warten, ob der 401-Fehler auch in der täglichen Log-Datei mitgemailt wird...

                          Meints Du / meint Ihr, der Code ist "robust" genug?? Von wegen der anschließenden Diskussion ...

                          Ich werde dem Autor mal die Änderungen mailen - mal sehen, was der so sagt!

                          Schöne Pfingsten und liebe Grüße aus Berlin!

                          Oliver

                      2. Hallo Calocybe!

                        Also, da gibts jetzt jede Menge Erklärungsbedarf !!!

                        local *ERRDOC;

                        ??? Was macht denn das? der * ist mir aus C-Derivaten bekannt, aber in Perl hab ich das noch nie gesehen. Sag mir bitte nicht dass das jetzt ein "file-pointer" ist!

                        local $/; undef $/;

                        Bei dir scheinen sich die Orakelsprüche mit einem "local" anzukündigen ;-) Was ist denn das für ein $-Dollar-Trick ?

                        lg Bernhard

                        1. Moin!

                          local *ERRDOC;
                          ??? Was macht denn das? der * ist mir aus C-Derivaten bekannt, aber in Perl hab ich das noch nie gesehen. Sag mir bitte nicht dass das jetzt ein "file-pointer" ist!

                          * hat in Perl auf jeden Fall eine andere Bedeutung als in C (dereferenzieren geht in Perl ja mit dem Zeichen des jeweils zu erreichenden Variablentyps). Aber was es ganz genau bedeutet, weiss ich auch nicht.

                          In diesem Falle sorgt 'local' dafuer, dass ERRDOC innerhalb des Blockes (nur deswegen habe ich extra noch die geschweiften Klammern aussenrum gemacht) von einer evtl. schon existierenden globalen ERRDOC Variable abgekoppelt wird. Ist also erstmal so aehnlich wie 'my', jedoch ist dieses ERRDOC jetzt auch noch in jeder aufgerufenen Funktion sichtbar (was bei 'my') nicht der Fall ist. Man sollte das local in diesem Falle verwenden, um Konflikte zu vermeiden (und weil 'my' dafuer nicht funktioniert), genaugenommen waere es wohl nicht noetig, aber ich halte meinen Code eben gerne sauber.

                          Nun wuerde man normalerweise z.B. 'local $variable' schreiben, wenn man sowas machen will, nur haben die FILE-Handles leider kein $, @, % oder & vorne dran. Mit * erwischt man jetzt glaube ich *alle* Variablen mit diesem Namen, also $ERRDOC, @ERRDOC, %ERRDOC, die Funktion &ERRDOC und auch das IO-Handle ERRDOC. Ich finde das ganz schoen komisch und verwirrend, man sieht auf jeden Fall, das Perl urspruenglich ein ganz uebler Hack ohne Konzept war, und diese Altlasten schlagen eben bis heute durch. Allerdings ermoeglicht local auch einige echt coole und nuetzliche Dinge, siehe weiter unten.

                          Es gibt uebrigens das Modul FileHandle, das der bessere Weg ist, um Dateien zu oeffnen. Allerdings wusste ich nicht, ob das irgendwelche Probleme mit den anderen IO-Handles gibt, die in dem Script verwendet werden, deshalb habe ich lieber darauf verzichtet.

                          So, kann sein, dass das jetzt nicht alles ganz korrekt war. Lies einfach mal perlsub durch, da muesste das alles drinstehen.

                          local $/; undef $/;
                          Bei dir scheinen sich die Orakelsprüche mit einem "local" anzukündigen ;-) Was ist denn das für ein $-Dollar-Trick ?

                          $/ ist der Input record separator (siehe perlvar), der angibt, was der Zeilentrenner beim Dateienlesen ist (normalerweise "\n"). Wenn ich den mit undef loesche, wird die gesamte Datei als eine Zeile angesehen, die ich mit einem <ERRDOC> in ein Skalar einlesen kann. Allerdings verlaesst sich ja der Rest des Programms darauf, dass in $/ etwas vernuenftiges drinsteht. Also mache ich mit local so eine Art lokale Kopie, die am Ende des Blocks (in diesem Fall der if-Zweig) wieder vergessen wird. Nach dem if wird also der alte Wert automatisch wiederhergestellt. Besonders an dieser Stelle ist interessant, dass sich dieses local auch auf alle Subroutinen auswirkt, die von diesem Block aufgerufen werden. Aber wie gesagt, das steht auch in perlsub.

                          Noch ein kleiner Trick: Manche Module sind sehr unsauber geschrieben und spucken am laufenden Band Warnungen aus, wenn man mit -w gestartet hat. Dann kann man die Variable $^W (die dem -w Flag entspricht), temporaer auf 0 setzen, ungefaehr so:

                          {
                              local $^W = 0;
                              Module::DirtyFunction();
                          }

                          Manchmal ist Perl eben doch ne coole Sache. *g*

                          So long

                          P.S. Nicht uebelnehmen, ich hab jetzt keine Lust, mir das nochmal durchzulesen, bevor ich es abschicke. *g*

                          1. Hallo Calocybe!

                            Bin Sprachlos! Hab mir die Datei trotz *votevotevote* mal sicherheitshalber abgespeichert. Werde mir das jetzt ca. 1 Woche täglich zum Frühstück, wo der Geist noch am wachesten ist durchlesen, und hoffen das ganze irgendwann durchschaut zu haben ;-)

                            Danke jedenfalls!

                            Noch ein kleiner Trick: Manche Module sind sehr unsauber geschrieben und spucken am laufenden Band Warnungen aus, wenn man mit -w gestartet hat. Dann kann man die Variable $^W (die dem -w Flag entspricht), temporaer auf 0 setzen, ungefaehr so:

                            {
                                local $^W = 0;
                                Module::DirtyFunction();
                            }

                            Also das finde ich ist der absolute Höhepunkt!! Ich kann nicht mehr *fg*

                            P.S. Nicht uebelnehmen, ich hab jetzt keine Lust, mir das nochmal durchzulesen, bevor ich es abschicke. *g*

                            jaja, schon gut, ich schau auch gleich wiedermal rauf, muss mich ein wenig abignorieren ;-)

                            danke und lg
                            Bernhard

                            1. Hi again!

                              Bin Sprachlos! Hab mir die Datei trotz *votevotevote* mal sicherheitshalber abgespeichert. Werde mir das jetzt ca. 1 Woche täglich zum Frühstück, wo der Geist noch am wachesten ist durchlesen, und hoffen das ganze irgendwann durchschaut zu haben ;-)

                              Was, zum Fruehstueck bist Du schon wach? Was bist Du denn fuer einer? ;-)

                              Also das finde ich ist der absolute Höhepunkt!! Ich kann nicht mehr *fg*

                              Ja? Na gut. *g*

                              jaja, schon gut, ich schau auch gleich wiedermal rauf, muss mich ein wenig abignorieren ;-)

                              Ja, werde auch gleich nochmal runterschauen und ein bisschen ignorieren, und dann geht's ab ins Bett. Vielleicht bin ich morgen ja doch mal zur ersten Vorlesung da. *g*

                              So long

                      3. Moin auch,

                        {
                            local *ERRDOC;

                        das nennt sich uebrigens Typeglob (weil es eben alle Typen umfasst)

                        if (open(ERRDOC, "<$errordocs_path/$code_page")) {
                                my $errdoc;
                                local $/; undef $/;

                        ^^^^^^^^^

                        das kannst du dir sparen, passiert automatisch (weil du $/ nichts zuweist).

                        $errdoc = <ERRDOC>;
                                close(ERRDOC);

                        etwas ressourcensparender (nicht, dass es ins Gewicht fallen wuerde ;):

                        if (open(ERRDOC, "<$errordocs_path/$code_page")) {
                          print <<HEADER;
                        Status: $ENV{QUERY_STRING} $code
                        Content-type: text/html

                        HEADER
                          print while (<ERRDOC>);
                        }
                        else {
                          # aber hier nur verschoenert...

                        print <<EFH;
                        Status: 500 Internal Server Error
                        Content-type: text/html

                        <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
                        <HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>
                        <BODY>Processing your request could not be completed due to the error <I>$ENV{QUERY_STRING} - $code</I>.<BR>
                        Additionally, an internal server error occurred while generating an approriate error document.<BR>
                        EFH
                        }

                        auch nicht getestet ;)

                        paranoiderweise koennte man fuer die Headerausgabe natuerlich auch CGI.pm benutzen ;-)

                        Viele Gruesse,

                        n.d.p.

                        1. Ich sollte mal wach werden ;)

                          if (open(ERRDOC, "<$errordocs_path/$code_page")) {
                            print <<HEADER;
                          Status: $ENV{QUERY_STRING} $code
                          Content-type: text/html

                          HEADER
                            print while (<ERRDOC>);

                          close ERRDOC;

                          }
                          else {
                            # aber hier nur verschoenert...

                          aber = ab (hmpf)

                          Status: 500 Internal Server Error

                          den koennte man bestimmt auch durch ein einfaches "die" produzieren, faellt mir gerade ein ;)

                          n.d.p.

            2. Ich seh schon, Du bist ein wahrer Profi!

              Danke, aber bleiben wir mal lieber auf dem Boden der Tatsachen. ;-)

              Meinst Du, wenn ich Deinen Vorschlag dem Autor maile, kann der etwas damit anfangen?

              Halte ich durchaus fuer sinnvoll. Ganz sicher bin ich mir naemlich auch nicht bei der Sache (hab sowas noch nie gemacht), und haette ich sowas selbst geschrieben, waere mir zwar der Gedanke gekommen, dass es an der Stelle Probleme geben koennte, aber ich haette es auf jeden Fall auch erstmal ohne Status: probiert. Ich denke schon, dass der Autor es begruessen wird, wenn Du ihn auf diese Diskussion aufmerksam machst, damit er das selber mal austesten kann.

              Oder kannst Du das gegen einen Obolus übernehmen?

              Wenn Du das Script mal als txt-Datei irgendwo online stellst und es nicht gar zu kompliziert ist, schaff ich das bestimmt auch ohne Obulus. Wenn das dann schoen funktioniert, kannst Du die Aenderung ja gleich dem Autor mehlen. That's the way Open Source works.

              So long