H. aus G.: link parsen

Hallo Community,

ich bin gerdade dabei ein Windowsprogramm im Stil von WebDown zu programmieren. Ich hätte da noch ein paar Fragen zum auswerten von Links die im html Code eingebettet sind.

Vorab eine kurze Erklärung wie weit ich schon bin mit der Auswertung, hier ein Beispiellink:

http://user:password@www.homepage.de:80/verz1/../verz%202/index.html#headline1

Sähe der Link so aus, ist meine Auswertung bis zum "#headline1" fertig.

Meine Frage hier: Welche Zeichen hinter "index.html" kann ich erwarten? Bekannt ist mir "#" für eine Textmarke und "?" um eine Parameterübergabe an, z.B. ein cgi-Script einzuleiten. Welche Zeichen habe ich nun noch nicht berücksichtigt und für was sind sie dann gut?

Gilt für die Parameterübergabe die gleiche Konvention bezüglich nicht erlaubter Zeichen wie bei der URL? Also "LEERZEICHEN" wird zu "%20"?

Ich hoffe, hier kann jemand helfen, danke schon mal vorab.

Gruß Hans

  1. Hallo,

    Vorab eine kurze Erklärung wie weit ich schon bin mit der Auswertung, hier ein Beispiellink:
    http://user:password@www.homepage.de:80/verz1/../verz%202/index.html#headline1

    da geht's schon los - die Übergabe von Benutzername und Password als Teil der URL ist zwar bei einigen Protokollen (z.B. FTP) üblich, bei HTTP aber ausdrücklich *nicht*. Diese Notation wird freundlicherweise und aus Kulanz von vielen Browsern auch akzeptiert, und sie setzen das intern in einen korrekten Request um (Credentials als separate HTTP-Header), richtig ist sie aber dennoch nicht.

    Sähe der Link so aus, ist meine Auswertung bis zum "#headline1" fertig.

    Gut, dann kannst du die Passwort-Auswertung ja auch drinlassen - vorausgesetzt, du bereitest sie auch zu einem korrekten Request auf.

    Meine Frage hier: Welche Zeichen hinter "index.html" kann ich erwarten?

    So ziemlich alle. ;-)
    Vor allem darfst du auf der Clientseite den Teil zwischen Hostnamen und Hash nicht stur nach Muster interpretieren. Was in deinem Beispiel als "verz1/../verz%202/index.html" notiert ist, könnte mit Verzeichnissen und Dateien auf dem Server korrelieren, muss aber nicht. Auch http://example.com/de/title/anything/p sieht für den oberflächlichen Betrachter aus, als würde man eine Ressource "p" abrufen, die im Verzeichnis /de/title/anything liegt. Das ist aber reine Mutmaßung, es könnte ebensogut ein Script /de sein, das den restlichen URL-Schwanz /title/anything/p selbst auswertet.
    Was ich sagen will: Es ergibt keinen wirklichen Sinn, diesen Teil weiter aufzudröseln, weil man sich damit in das Reich von Mutmaßungen begibt, aber keinen Nutzen daraus zieht.

    Bekannt ist mir "#" für eine Textmarke und "?" um eine Parameterübergabe an, z.B. ein cgi-Script einzuleiten.

    Ja, wobei das '?' aus der Sicht des Clients auch keine besondere Rolle spielt. Es wird erst vom Server ausgewertet und bekommt dort seine besondere Bedeutung. Das Raute-Zeichen '#' dagegen wird direkt vom Client ausgewertet - alles, was danach kommt, wird gar nicht erst an den Server übermittelt, weil es ja nur für eine Sprungmarke innerhalb des Dokuments steht.

    Welche Zeichen habe ich nun noch nicht berücksichtigt und für was sind sie dann gut?

    Auf die Schnelle fallen mir keine mehr ein.

    Gilt für die Parameterübergabe die gleiche Konvention bezüglich nicht erlaubter Zeichen wie bei der URL? Also "LEERZEICHEN" wird zu "%20"?

    Nicht ganz - im Query-String wird ein Leerzeichen üblicherweise als '+' codiert (ein echtes '+' dann als %2B), aber als %20 ist ebenso zulässig. Und alle Zeichen, die entweder im Bereich der ASCII-Steuercodes liegen (also 0x00..0x1F), sowie alle Zeichen außerhalb des ASCII-Bereichs (also ab 0x80 aufwärts) sollten (müssen?) URL-codiert sein, also mit Prozent-Zeichen und dem Bytewert in zweistelliger Hex-Notation.

    Schönes Wochenende,
     Martin

    --
    Jungs sind wie Waschmaschinen: Wenn man sie anmacht, kommen sie ins Schleudern.
    Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
    1. Hallo Martin,

      vielen Dank für deine schnelle und ausführliche Antwort

      da geht's schon los - die Übergabe von Benutzername und Password als Teil der URL ist zwar bei einigen Protokollen (z.B. FTP) üblich, bei HTTP aber ausdrücklich *nicht*. Diese Notation wird freundlicherweise und aus Kulanz von vielen Browsern auch akzeptiert, und sie setzen das intern in einen korrekten Request um (Credentials als separate HTTP-Header), richtig ist sie aber dennoch nicht.

      Ich hab mich da wohl nicht ganz eindeutig ausgedrückt, wenn im Linkquelltext user:password vorhanden sind, werte ich diese korrekt aus und füge bei meinen GET Request das Headerfeld "Authorization" mit base64 codierter user:passwort Kombination hinzu.

      Ja, wobei das '?' aus der Sicht des Clients auch keine besondere Rolle spielt.

      Bei dieser Frage ging es vor allem darum wie ich nun sinnvolle und auch zulässige Dateinamen erzeuge um die Daten auf der Festplatte zu speichern. Also für "#" ist das jetzt klar, ich muss Links die auf verschiedene Textmarken ein und desselben Dokuments verweisen nur einmal herunterladen. Anderst siehe es bei Links aus die Parameter mit "?" übergeben, da der Inhalt ja erst auf dem Server erzeugt wird. Hier muss ich ggf. mehrere Versionen des des Dokuments speichern. Dies ist dahingehend für mich wichtig, da ich die originalen Links in den emfangenen html, etc. Dateien gegen Links austauschen die auf die Position der Daten auf meiner Festplatte verweisen (offline Browsing).

      Gruß Hans

      1. Hallo Hans,

        die Übergabe von Benutzername und Password als Teil der URL ist zwar bei einigen Protokollen (z.B. FTP) üblich, bei HTTP aber ausdrücklich *nicht*.
        Ich hab mich da wohl nicht ganz eindeutig ausgedrückt, wenn im Linkquelltext user:password vorhanden sind, werte ich diese korrekt aus und füge bei meinen GET Request das Headerfeld "Authorization" mit base64 codierter user:passwort Kombination hinzu.

        ah, verstehe. Das ging aus deiner Beschreibung tatsächlich nicht eindeutig hervor, ist aber genau das, was ich mit "aus Kulanz" meinte - und dass ich diese Funktion, wenn sie sowieso schon existiert, auch drinlassen würde.

        Ja, wobei das '?' aus der Sicht des Clients auch keine besondere Rolle spielt.
        Bei dieser Frage ging es vor allem darum wie ich nun sinnvolle und auch zulässige Dateinamen erzeuge um die Daten auf der Festplatte zu speichern.

        Ach daher weht der Wind. ;-)
        Dann kommt aber ein weiteres Problem ins Spiel. Viele Zeichen, die in URLs verwendet werden, sind in Dateinamen nicht erlaubt, wobei die Menge der erlaubten bzw. nicht erlaubten Zeichen sogar noch je nach Dateisystem unterschiedlich ist. Zeichen wie etwa das Fragezeichen oder der Stern '*' sind aber in keinem mir bekannten Dateisystem zulässig, also musst du sie durch irgendwas anderes ersetzen. Den Slash '/' wirst du vermutlich auf eine Verzeichnisstruktur abbilden wollen. Auch '=', '+', ':' sowie '<' und '>' sind oft verboten.
        Du könntest natürlich alle potentiell kritischen Zeichen durch eine Escape-Sequenz ersetzen. Ich weiß nicht, ob das Prozent-Zeichen überall erlaubt ist; wenn ja, könntest du für problemträchtige Zeichen generell wieder ein URL-Encoding anwenden.

        Also für "#" ist das jetzt klar, ich muss Links die auf verschiedene Textmarken ein und desselben Dokuments verweisen nur einmal herunterladen.

        Genau.

        Anderst siehe es bei Links aus die Parameter mit "?" übergeben, da der Inhalt ja erst auf dem Server erzeugt wird. Hier muss ich ggf. mehrere Versionen des des Dokuments speichern.

        Ja, wobei sich das automatisch ergibt, wenn du z.B. Fragezeichen und '=' durch andere, unkritische Zeichen oder Zeichenkombinationen ersetzt.

        Ciao,
         Martin

        --
        Dürfen Finanzbeamte eigentlich ihren Kaffee schwarz trinken? - Ich glaube ja. Aber sie dürfen ihre Tasse nicht absetzen.
          (gehört auf SWR3)
        Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
        1. Tach!

          Zeichen wie etwa das Fragezeichen oder der Stern '*' sind aber in keinem mir bekannten Dateisystem zulässig, also musst du sie durch irgendwas anderes ersetzen. Den Slash '/' wirst du vermutlich auf eine Verzeichnisstruktur abbilden wollen. Auch '=', '+', ':' sowie '<' und '>' sind oft verboten.

          Bis auf den / hab ich unter ext4 alle Zeichen in Dateinamen verwenden können. Einige mussten nur durch ein \ maskiert werden.

          Also für "#" ist das jetzt klar, ich muss Links die auf verschiedene Textmarken ein und desselben Dokuments verweisen nur einmal herunterladen.
          Genau.

          Zunehemd hat auch der Teil nach dem # eine inhaltliche Bedeutung, vor allem bei Ajax-verwendenden Seiten. Aber die kann man mit einem einfachen Downloader sowieso nicht abschnorcheln.

          dedlfix.

          1. Hallo nochmal,

            danke an euch alle für eure Mühe mir behilflich zu sein. Ich verstehe die Materie jetz schon etwas besser. Mein anfänlicher Denkfehler, war dass ich die Links die in der Webseite eingebettet sind erstmal aufbereiten muss um die Daten mit GET vom Server zu holen, wie sich jetzt bei zahlreichen Test herausgestellt hat ist ein einfaches zusammensetzen des aktuellen Verzeichnisses mit dem Link vollkommend ausreichend (oder der Link steht auch schon mal mit Servernamen zur Verfügung) um die Daten korrekt anzufordern. Ich hatte nicht gedacht dass man z.B. einen solchen Link "../123/./.././456/index.cgi?890" ohne
            weiteres vom Server anfordern kann und man bekommt das zurück was man erwartet hat. Ich muss mir jetzt nur noch ein passendes System überlegen die heruntergeladenen Daten mit sinvollen Namen zu speichern, und die originalen Links in den heruntergeladenen Webseiten entsprechend zu ersetzen um Offline Browsing verfügbar zu machen.

            Also schönes Wochenende noch.

            Gruß Hans

            1. Tach!

              Ich hatte nicht gedacht dass man z.B. einen solchen Link "../123/./.././456/index.cgi?890" ohne weiteres vom Server anfordern kann und man bekommt das zurück was man erwartet hat.

              Eigentlich sollte das in der Form nur ein Browser können, denn der kann diesen relativen Verweis (weil mit .. beginnend) zu einem geforderten absoluten Verweis umrechnen, indem er dazu die Pfadangabe der aktuellen Seite verwendet oder wenn vorhanden die Angabe im base-Element. Ein Server hat keine Ahnung von einem Woher und ist auf die Übertragung einer absoluten Pfadangabe angewiesen.

              Ich muss mir jetzt nur noch ein passendes System überlegen die heruntergeladenen Daten mit sinvollen Namen zu speichern, und die originalen Links in den heruntergeladenen Webseiten entsprechend zu ersetzen um Offline Browsing verfügbar zu machen.

              Warum nimmst du eigentlich keinen von den bereits existierenden Downloadern?

              dedlfix.

              1. Hallo,

                Eigentlich sollte das in der Form nur ein Browser können,..

                Ich habe das jetzt mehrfach auf diversen Homepages ausprobiert, z.B.

                http://www.ghisler.ch/board/././../board/./viewforum.php?f=2

                da wird einwandfrei folgende Resource aufgerufen

                http://www.ghisler.ch/board/viewforum.php?f=2

                Martin schrieb folgendes:

                Auch http://example.com/de/title/anything/p sieht für den oberflächlichen Betrachter aus, als würde man eine Ressource "p" abrufen, die im Verzeichnis /de/title/anything liegt. Das ist aber reine Mutmaßung, es könnte ebensogut ein Script /de sein, das den restlichen URL-Schwanz /title/anything/p selbst auswertet. Was ich sagen will: Es ergibt keinen wirklichen Sinn, diesen Teil weiter aufzudröseln, weil man sich damit in das Reich von Mutmaßungen begibt, aber keinen Nutzen daraus zieht.

                Wenn dies stimmt und ich es richtig verstanden habe, ist es sogar notwendig, die Strings einfach hintereinander zu kopiern, da der Server ja selbst entscheidet welche Resource er bei welchem Aufruf zuteilt.

                Warum nimmst du eigentlich keinen von den bereits existierenden Downloadern?

                Ich habe da schon einige Programme ausprobiert, z.B. WebDown, WinHTTrack und andere. In der Summe würde es ja passen, aber einzeln betrachtet bin ich mit keinem so annähernd glücklich. Da ich gerne Programme schreibe (nur solche, von denen ich auch selber einen Nutzen habe), ist dies eine gute Gelegenheit mich mal wieder einem interessanten Projekt zu widmen.

                Gruß Hans

                1. Tach!

                  Eigentlich sollte das in der Form nur ein Browser können,..
                  Ich habe das jetzt mehrfach auf diversen Homepages ausprobiert, z.B.
                  http://www.ghisler.ch/board/././../board/./viewforum.php?f=2
                  da wird einwandfrei folgende Resource aufgerufen
                  http://www.ghisler.ch/board/viewforum.php?f=2

                  Und wer macht diese Auflösung? Der Browser vor dem Request? Vermutlich, denn eine vollständige URL zu requesten ist nur bei Benutzung eines Proxys üblich. Ansonsten sieht der Request ja (abgekürzt) so aus:

                  GET /irgendwas HTTP/1.1
                  Host: example.com

                  Der Teil nach dem GET fängt immer mit / an, denn relative Angaben kann ein Server nicht auflösen. er wüsste nicht von wo aus er starten soll. Deswegen war dein Beispiel "../123/./.././456/index.cgi?890" nichts für den Webserver, sondern muss erst noch vom Browser in die absolute Form gebracht werden.

                  Schau nach, was der Browser wirklich sendet. Jeder moderne bringt dazu Debug-Möglichkeiten mit. Außerdem mag ich immer noch die livehttpheaders-Extension für den Firefox.

                  Martin schrieb folgendes:

                  Auch http://example.com/de/title/anything/p sieht für den oberflächlichen Betrachter aus, als würde man eine Ressource "p" abrufen, die im Verzeichnis /de/title/anything liegt. Das ist aber reine Mutmaßung, es könnte ebensogut ein Script /de sein, das den restlichen URL-Schwanz /title/anything/p selbst auswertet. Was ich sagen will: Es ergibt keinen wirklichen Sinn, diesen Teil weiter aufzudröseln, weil man sich damit in das Reich von Mutmaßungen begibt, aber keinen Nutzen daraus zieht.

                  Wenn dies stimmt und ich es richtig verstanden habe, ist es sogar notwendig, die Strings einfach hintereinander zu kopiern, da der Server ja selbst entscheidet welche Resource er bei welchem Aufruf zuteilt.

                  Nö, so einfach geht das nicht. Wenn die aktuelle Ressource die URL http://example.com/foo/bar hat und der Link "../qux" lautet, ergibt das Zusammenkopieren http://example.com/foo/bar../qux, was ja schon augenscheinlich nicht stimmt. Richtig wäre http://example.com/foo/qux

                  dedlfix.

                  1. Hallo,

                    Und wer macht diese Auflösung? Der Browser vor dem Request? Vermutlich, denn eine vollständige URL zu requesten ist nur bei Benutzung eines Proxys üblich. Ansonsten sieht der Request ja (abgekürzt) so aus:

                    Um bei obigem Beispiel zu bleiben sieht mein Request wie folgt aus:

                    GET /board/././../board/./viewforum.php?f=2 HTTP/1.1
                    Host: www.ghisler.ch
                    User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0
                    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
                    Accept-Language: en
                    Connection: close

                    Dass ich hierbei die Domain, vom Rest trennen muss, hielt ich nicht für erwähnenswert, da ich dies als essenziell voraussetzte.

                    Der Teil nach dem GET fängt immer mit / an, denn relative Angaben kann ein Server nicht auflösen. er wüsste nicht von wo aus er starten soll. Deswegen war dein Beispiel "../123/./.././456/index.cgi?890" nichts für den Webserver, sondern muss erst noch vom Browser in die absolute Form gebracht werden.

                    Da gebe ich dir vollkommen recht, der Aufruf ist nur dann zielführend, wenn er aus einem Unterverzeichniss heraus gemacht wird und das Unterverzeichnis vor diesen String kopiert wird, mein Beispiel war nicht gut gewählt.

                    Nö, so einfach geht das nicht. Wenn die aktuelle Ressource die URL http://example.com/foo/bar hat und der Link "../qux" lautet, ergibt das Zusammenkopieren http://example.com/foo/bar../qux, was ja schon augenscheinlich nicht stimmt. Richtig wäre http://example.com/foo/qux

                    Auch hier hielt ich es nicht für erwähnenswert, dass der Dateiname der aktuellen Datei abgeschnitten werden muss, um danach den Link anzuhängen. Um bei deinem Beispiel zu bleiben, muss mal eben wissen ob "/bar" eine Datei oder ein Verzeichniss ist (kann man ja vorab über den Aufruf HEAD herausbekommen) ist es ein Verzeichniss sieht der GET Aufruf dann so aus:

                    /foo/bar/../qux

                    ist "bar" eine Datei so:

                    /foo/../qux

                    den Rest erledigt der Server, wenn /qux dann auch wieder ein Verzeichniss ist sieht der Aufruf, dann eben so aus:

                    /foo/../qux/

                    oder so

                    /foo/../qux/

                    Es emfiehlt sich eben zuerst den HEAD Aufruf duchzuführen, auszuwerten, und dann entsprechend den GET Aufruf zu tätigen.

                    Natürlich kann man nicht so lapidar sagen "Da sind nur die Strings hintereinander zu kopieren", aber verglichen mit dem Parsen von "./" und "../" und dem entsprechenden entfernen der Oberverzeichnisse, scheint es auf ein einfaches Kopieren von Strings mit minimaler Vorarbeit herauszulaufen.

                    Gruß Hans

                    1. Tach!

                      Auch hier hielt ich es nicht für erwähnenswert, dass der Dateiname der aktuellen Datei abgeschnitten werden muss, um danach den Link anzuhängen. Um bei deinem Beispiel zu bleiben, muss mal eben wissen ob "/bar" eine Datei oder ein Verzeichniss ist (kann man ja vorab über den Aufruf HEAD herausbekommen)

                      Nein, das muss man nicht wissen, und bei virtuellen Geschichten (sprich, wenn die URL nicht auf etwas real existierendes gemappt wird)
                      kann man das auch nicht wissen. Auch muss der Browser keinen HEAD-Request absetzen. Die Regel ist ganz einfach und die vorliegende URL ist ausreichend, um die neue zu bilden. Alles bis zum letzten / wird abgeschnitten und dann wird die neue relative URL angehängt.

                      dedlfix.

          2. Tach,

            Bis auf den / hab ich unter ext4 alle Zeichen in Dateinamen verwenden können. Einige mussten nur durch ein \ maskiert werden.

            jup, das zweite und letzte nicht verwendbare Zeichen ist \0, war auch in ext3 schon so.

            mfg
            Woodfighter

    2. Hallo,

      die Übergabe von Benutzername und Password als Teil der URL ist zwar bei einigen Protokollen (z.B. FTP) üblich, bei HTTP aber ausdrücklich *nicht*.
      Diese Notation wird freundlicherweise und aus Kulanz von vielen Browsern auch akzeptiert, … richtig ist sie aber dennoch nicht.

      Kulanz ist gut, es ist erst einmal im allgemeinen URI-Standard so spezifiziert; dort auch entsprechende Hinweise, es besser nicht zu verwenden bzw. zumindest das Passwort nie im Klartext zu zeigen.

      Du hast aber recht, die HTTP-Spezifikation, die das http-Schema definiert, verzichtet auf das userinfo, gibt aber keine weiteren Hinweise oder Erklärungen dazu, soweit ich das sehe.

      Mathias

  2. Tach!

    http://user:password@www.homepage.de:80/verz1/../verz%202/index.html#headline1
    Sähe der Link so aus, ist meine Auswertung bis zum "#headline1" fertig.

    Warum nimmst du keinen fertigen URL-Parser? Mindestens einer sollte auch in deiner Programmierumgebung verfügbar sein.

    Meine Frage hier: Welche Zeichen hinter "index.html" kann ich erwarten?

    Alle. Wenigstens im Fehlerfall. Der Aufbau von URLs(/URIs) ist bekannt und beschrieben. Wenn du einen Parser selbst bauen willst, solltest du auch die Spezifikation des Delinquenten lesen (können).

    Bekannt ist mir "#" für eine Textmarke und "?" um eine Parameterübergabe an, z.B. ein cgi-Script einzuleiten. Welche Zeichen habe ich nun noch nicht berücksichtigt und für was sind sie dann gut?

    Es kann auch mit / weitergehen. Das nennt sich dann PathInfo. Aber was PathInfo ist oder immer noch Path, weiß nur der Server, weil er dazu wissen muss, ob index.html zum Beispiel schon ein Script ist, an das er die Steuerung übergeben muss.

    Gilt für die Parameterübergabe die gleiche Konvention bezüglich nicht erlaubter Zeichen wie bei der URL? Also "LEERZEICHEN" wird zu "%20"?

    Alles ist URL. Es gibt aber Unterschiede für den Querystring und die Pfadangabe. Siehe PHPs Funktionen urlencode() vs. rawurlencode(). Davon ist vor allem das Leerzeichen betroffen, dass im Querystring eigentlich zu + werden muss.

    dedlfix.