Andreas Korthaus: Header und Body eines HTTP-Response sicher trennen

Hallo!

Ich öffne mit fsockopen() eine HTTP-Verbindung und bekomme eine Antwort mit einem Header und einem Body mit binären Daten(gzip). Trennen  kann ich header und body ja einfach mit:

print_r(explode("\r\n\r\n",$result));

Aber ist das auch sicher? Zum einen, wird das auf jedem Server mit \r\n\r\n umgebrochen?

Aber was noch wichtiger ist, könnte nicht zufällig sowas auch mal im body auftauchen so das mir Daten verloren gehen, denn dann hätte der Array mehr als 2 Elemente!

Was würdet Ihr da machen?

Viele Grüße
Andreas

  1. Moin!

    Ich öffne mit fsockopen() eine HTTP-Verbindung und bekomme eine Antwort mit einem Header und einem Body mit binären Daten(gzip). Trennen  kann ich header und body ja einfach mit:

    print_r(explode("\r\n\r\n",$result));

    Aber ist das auch sicher? Zum einen, wird das auf jedem Server mit \r\n\r\n umgebrochen?

    Ja, das steht so im HTTP-Standard: Der Body eines HTTP-Responses beginnt nach einer Leerzeile.

    Du solltest bedenken, dass eine Leerzeile durchaus auch durch "\n\n" realisiert werden kann - ich schreibe in meinen alten Perl-Skripten jedenfalls immer
    print "Content-type: text/html\n\n";
    um den HTTP-Header auszugeben und danach mit dem Body weiterzumachen. Insofern solltest du dich nicht auf "\r\n" als einziges Zeilentrennzeichen versteifen.

    Außerdem solltest du sicherstellen, dass du nur das _erste_ Auftreten einer Leerzeile als Trennzeichen erkannt wird, denn sonst trennst du, wie du schon selbst erkannt hast, Teile ab, die nicht abgetrennt werden dürfen.

    - Sven Rautenberg

    1. Hi!

      Ich öffne mit fsockopen() eine HTTP-Verbindung und bekomme eine Antwort mit einem Header und einem Body mit binären Daten(gzip). Trennen  kann ich header und body ja einfach mit:

      print_r(explode("\r\n\r\n",$result));

      Aber ist das auch sicher? Zum einen, wird das auf jedem Server mit \r\n\r\n umgebrochen?

      Ja, das steht so im HTTP-Standard: Der Body eines HTTP-Responses beginnt nach einer Leerzeile.

      Du solltest bedenken, dass eine Leerzeile durchaus auch durch "\n\n" realisiert werden kann - ich schreibe in meinen alten Perl-Skripten jedenfalls immer
      print "Content-type: text/html\n\n";
      um den HTTP-Header auszugeben und danach mit dem Body weiterzumachen. Insofern solltest du dich nicht auf "\r\n" als einziges Zeilentrennzeichen versteifen.

      Ja, aber wie? Sind denn "\r\n\r\n" und "\n\n" die einzigen Möglichkeiten? Vor wenn ich nach "\r\n\r\n" suche(z.B. mit strstr()), dann kann es ja sein das ich es finde, aber im body und nach dem header wir mit "\n\n" umgebrochen! Oder was mit

      preg_match("^(.*?)(\r\n|\n){2}(.*)//",$html,$matches);
      $body = $matches[3];

      wäre das sicher?

      Außerdem solltest du sicherstellen, dass du nur das _erste_ Auftreten einer Leerzeile als Trennzeichen erkannt wird, denn sonst trennst du, wie du schon selbst erkannt hast, Teile ab, die nicht abgetrennt werden dürfen.

      Sollte mit obiger Version sichergestellt sein, soder vielleicht doch mit explode, und alle Elemente des Array außer dem ersten mit implode zu einem String verschmelzen?

      Grüße
      Andreas

  2. Hallo,

    Aber was noch wichtiger ist, könnte nicht zufällig sowas auch mal im body auftauchen so das mir Daten verloren gehen, denn dann hätte der Array mehr als 2 Elemente!

    Was würdet Ihr da machen?

    Entsprechend der Dokumentation;-)
    (http://www.php.net/manual/de/function.explode.php)

    $truemmer = explode("\r\n\r\n",$result,2);

    Grüße
      Klaus

    1. Hi!

      Entsprechend der Dokumentation;-)
      (http://www.php.net/manual/de/function.explode.php)

      $truemmer = explode("\r\n\r\n",$result,2);

      Dass ist mal eine gute Idee ;-)

      Bleibt nur noch die Frage, wenn ich erreichen will, dass das ganze nicht nur auf einem Server funktioniert, sondern auch wenn der \n\n als Trennzeichen verwendet, was mache ich dann? Wie finde ich das verläßlich heraus? Gibt es noch mehr Varianten?

      Grüße
      Andreas

      1. Hallo,

        Bleibt nur noch die Frage, wenn ich erreichen will, dass das ganze nicht nur auf einem Server funktioniert, sondern auch wenn der \n\n als Trennzeichen verwendet, was mache ich dann? Wie finde ich das verläßlich heraus? Gibt es noch mehr Varianten?

        Auch da hilft eine gezielte Suche in der Dokumentation.

        split() wäre das Zauberwort, das kann nämlich, analog zu Perl, mit einer Regex auftrennen. split('(\r\n|\n|\r){2}',$result,2) sollte da was bringen, denke ich  mal, aber ich bin ja nicht so der Regex-Experte, und PHP kenne ich ja auch nicht

        *gg*rüsse
          Klaus

  3. Moin,

    Aber ist das auch sicher? Zum einen, wird das auf jedem Server mit \r\n\r\n umgebrochen?

    Wenn du dir _wirklich_ganz_sicher_ bist, dass dein Gegenüber richtiges HTTP spricht, kannst du das so machen (zusammen mit der genannten Einschränkung auf 2 Ergebnisse). Da wir aber nicht in einer perfekten Welt leben hat sich schon vor längerer Zeit die Empfehlung "be strict in what you send and liberal in what you accept" (in verschiedenen Variationen) verbreitet. Wenn dein Gegenüber also an einem Mac sitzt kann dir schon mal ein \r\r entgegenfliegen, an Unixen ein \n\n und wenn es ganz wild kommt auch diverse Variationen. Ich hatte im Archiv glaube ich schonmal eine Variante mit strpos (o.ä.) gepostet die versuchte halbwegs intelligent zu sein.

    Wenn du eine Lösung mit nur einem Befehl willst, gefällt mir Klaus' zweite Variante am besten, bis auf zwei Probleme: a) würde der regexp auch \r\n als Trenner aktzeptieren und b) ist laut Doku split() nicht binärsicher. Versuch' lieber etwas in der Art von (\r\n\r\n|\r\r|\n\n) zusammen mit preg_split. Wenn du magst, kannst du auch noch \r\r\n und \r\n\n als besonders komische Konstrukte aufnehmen (die anderen möglichen Kombination sind glaube ich alle uneindeutig).

    --
    Henryk Plötz
    Grüße aus Berlin