Henryk Plötz: HEADER manipulieren

Beitrag lesen

Moin

Jetzt meine Frage, wie baue ich so einen HEADER in PHP auf? Ich fange mal so an:

header ("
POST /sript.php HTTP/1.1\n
.
.
.
var1=$wert1&var2=$wert2...\n
");

Das ist erstens Falsch (pro Header-Zeile sollte man einen eigenen header()-Aufruf verwenden, und keine Zeilenumbrüche drin haben) und wird zweitens nicht funktionieren, da die Dinger ja wie du bereits angemerkt hast, an den Browser gehen.

Dir bleibt wohl nichts anderes übrig einen eigenen kleinen HTTP-Client zu schreiben. Das ist im Prinzip nicht schwer (für GET) aber POST würde ich mir selbst momentan noch nicht zutrauen. Alles was du wissen willst, findest du in RFC 2616 (HTTP/1.1) bzw. RFC 1945 (HTTP/1.0).

Hier ein kleiner Rahmen für den Anfang (ähnliches benutze ich immer häufiger):

<?php
 $fh = fsockopen("www.zielhost.de", 80, $errno, $errstr, 15); // Socket zu www.zielhost.de Port 80 aufmachen, fehlermeldungen landen in $errno und $errstr, Verbindungstimeout 15 Sekunden

if(!$fh) // Verbindung fehlgeschlagen
  if($errno==0) die("Verbindung fehlgeschlagen. Unbekannter Fehler. Wahrscheinlich konnte der Hostname nicht aufgelöst werden");
  else die("Verbindung fehlgeschlagen. Fehler ($errno): $errstr");

// Verbindung OK
 // Request zusammenbauen
 $req  = "GET / HTTP/1.0\x0d\x0a"; // hole / mit HTTP/1.0
 $req .= "Host: www.zielhost.de\x0d\x0a"; // Host-Angabe für die häufig verwendeten virtual hosts
 $req .= "User-Agent: Henryk war hier\x0d\x0a"; // Nur so; sonstige Header nach dem gleichen Prinzip hinzufügen
 $req .= "\x0d\x0a"; // Eine Leerzeile als krönenden Abschluss des Requests

fwrite($fh, $req); // Request senden

$puffer = ""; $answ_hdrs=Array(); $last_hdr = ""; $status_line = "";// ein paar Variablen leer machen

// Nun die Ergebnisse einlesen
 do{ // Dazu nehmen wir eine Schleife die alles nach Zeilen trennt
  $puffer .= fread($fh, 512); // ein kleines bisschen lesen, wir wollen ja nicht unnötig blockieren
  $zpos1 = strpos($puffer, "\x0d\x0a");  // Es gibt die merkwürdigsten Zeilenendezeichen zwischen Himmel und Erde
  $zpos2 = strpos($puffer, "\x0a"); // Wir suchen mal nach den gebräuchlichsten.
  $zpos3 = strpos($puffer, "\x0d");  // In einer perfekten Welt, wären alle Zeilen der Header mit \0xd\0xa abgetrennt
  if($zpos1 !== FALSE) {  // Ein schönes Zeilenendezeichen
   $zeile = substr($puffer, 0, $zpos1); $puffer = substr($puffer, $zpos1 + 2);
  } else if($zpos2 !== FALSE) { // Ein Unix-Zeilendezeichen
   $zeile = substr($puffer, 0, $zpos2); $puffer = substr($puffer, $zpos2 + 1);
  } else if($zpos3 !== FALSE && ($zpos3 != strlen($puffer)-1 || feof($fh)) ) { // Entweder ein Mac-Zeilenende oder der Anfang eines schönen Zeilenendes, bei letzterem wird es ignoriert
   $zeile = substr($puffer, 0, $zpos3); $puffer = substr($puffer, $zpos3 + 1);
  } else { // Noch keine Zeile vollständig, oder aber ein \x0d am Ende des Puffers gefunden, einfach noch ein bisschen mehr einlesen
   continue;
  }
  // In $zeile liegt jetzt - oh wunder - eine Zeile, also schmeissen wir mit ein paar regulären Ausdrücken nach ihr
  $zeile = trim($zeile);  // Vorne und hinten überflüssige Whitespaces wegmachen. RFC 2616 sagt dass wir das dürfen
  if(preg_match('!^HTTP/(\d+).(\d+)\s+(\d+)\s+(.*)$!', $zeile, $matches)) {
   $status_line = $zeile;
   // Das ist eine wunderhübsche Statuszeile. In $matches[1] und $matches[2] liegt die HTTP-Version, in $matches[3] der Statuscode und in $matches[4] die Meldung
   // Damit kann man jetzt ein bisschen Spaß haben
   if($matches[3] > 399) die("Naja, das war wohl nichts: $zeile");
  } else if(preg_match('!^([^:]+)\s*:\s*(.*)$!', $zeile, $matches)) {
   // Das ist ein sonstiger Header, in $matches[1] liegt die Header-Bezeichnung, in $matches[2] liegt der Wert
   // Wir machen jetzt ein bisschen Magie. $answ_hdrs soll ein assoziatives Array mit den Header-Werten werden. Wenn der andere sich aber entscheidet einen Header
   //  mehr als einmal zu senden (ist sein gutes Recht), dann sollen alle Werte als weiteres Array vorliegen (ist für deine Zwecke vielleicht ein bisschen Overkill)
   $vo = trim($matches[1]); $hi = trim($matches[2]);  // Ein paar Hilfsvariablen
   if($answ_hdrs[$vo] == "") $answ_hdrs[$vo] = $hi; // Dieser Header kam bis jetzt noch nicht (der Normalfall)
   else { // Der Header wahr schonmal da, lasst uns ein Array aufmachen
    if(is_array($answ_hdrs[$vo])) { // Hmm, wir haben schonmal ein Array aufgemacht, also nehmen wir doch gleich das
     $answ_hdrs[$vo][] = $hi;
    } else {
     $answ_hdrs[$vo]=Array($answ_hdrs[$vo]);
     $answ_hdrs[$vo][] = $hi;
    }
   }
   // OK, der Header ist verzeichnet, jetzt noch merken was für ein Header das wahr, wirst gleich sehen warum
   $last_hdr = $vo;
  } else if($zeile == "") { // Eine Leerzeile verkündet das Ende der Header
   break;
  } else { // Die Zeile hat die Form "     blabla", laut Standard kann das bedeuten dass der zuletzt gesendeten Header fortgesetz werden soll, jedoch nicht alles in eine Zeile passt
   if(is_array($answd_hdrs[$last_hdr])) $answ_hdrs[$last_hdr][ count($answ_hdrs[$last_hdr])-1 ] .= " ".$zeile; // Ans Ende des letzten Array-Eintrag setzen
   else $answ_hdrs[$last_hdr] .= " ".$zeile;
  }
 } while(!feof($fh));  // Solange wie noch Zeichen kommen

// <sing> Alle Header sind schon da, alle Header, alle </sing> Jetzt fehlt nur noch Body

if(!feof($fh))
  if(isset($answ_hdrs["Content-Length"]))
   $puffer .= fread($fh, $answ_hdrs["Content-Length"] - strln($puffer)); // Alle angekündigten Daten empfangen
  else
   while(!feof($fh)) $puffer .= fread($fh, 2048); // Den Rest der Verbindung empfangen

// Fertsch. In $answ_hdrs hast du jetzt alle Header, in $status_line die Statuszeile und in $puffer den empfangenen Body

// Kleine Beispielausgabe
 echo "<h1>".htmlentities($status_line)."</h1><h2>Response Header</h2><pre>"; print_r($answ_hdr); echo "</pre><h2>Response Body</h2><pre>".htmlentities($puffer)."</pre>";
?>

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