Dennis: gzip-komprimierte Daten mit PHP-Client von Server empfangen

Hi liebes Forum,

für ein kleines Programm schreibe ich gerade einen Clienten in PHP - dieser soll von einem Server über HTTP-Request eine Seite empfangen, dazu gehe ich so vor (gekürzt):

$fp = fsockopen("www.escape-to-space.de", 80, $errno, $errstr, 30);  
$out = "GET /pfad/seite HTTP/1.1\r\n"  
     . "Host: www.escape-to-space.de\r\n"  
     . "User-Agent: PHP-Client"  
     . "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n"  
     . "Accept-Language: de-de,de;q=0.7,en;q=0.3\r\n"  
     . "Accept-Encoding: gzip,deflate\r\n"  
     . "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"  
     . "Keep-Alive: 300\r\n"  
     . "Connection: keep-alive\r\n";  
fwrite($fp, $out);

Anschließend lese ich die Rückgabe wie auch im PHP Manual aus, mit der nachfolgenden Funktion zerlege ich dann die Header vom Inhalt:

function split_data($data) {  
  $pos = strpos($data, "\r\n\r\n");  
  $head = substr($data, 0, $pos);  
  $body = substr($data, $pos + 4);  
  if(preg_match("/Content-Encoding: gzip/", $head)) {  
    $body = gzuncompress($body); // Zeile 45  
  }  
  return array($head, $body);  
}

Ich habe sowohl die kompletten Sachen die ich vom Server erhalte in eine Datei geloggt, als das was beim Zerlegen in Header und Body bei mir raus gekommen ist.

Jetzt gibt es jedoch folgende Fehlermeldung von PHP: Warning: gzuncompress(): data error in /home/driehle/privat/ets.php on line 45

Zeile 45 ist die oben gekennzeichnete, also das dekodieren der empfangenen Daten - das scheint hier schief zu gehen.

Wie bereits gesagt habe ich die Variable $body mal in eine Datei geloggt - anschließend habe ich diese Datei über die Shell mit gunzip dekodiert, dies hat EINWANDFREI geklappt! Deshalb gehe ich davon aus, dass meine Methode zum Teilen von Headern und Body funktioniert.

Ich vermute, dass irgendwie die Informationen so von PHP nicht verarbeitet werden können, weil vl. irgendwelche Meta-Daten vorhanden sind. Gibt es eine Möglichkeit die Daten zu dekomprimieren ohne Sie in eine temporäre Datei zu schreiben?

MfG, Dennis.

  1. Hallo,

    selbes Problem konnte ich bis jetzt auch noch nicht lösen. Es scheint ein Bug in PHP zu sein...

    Gruß aus Berlin!
    eddi

    --
    PHP: unter Linux installieren, in Bearbeitung Konfigurieren
    1. Hi eddi,

      selbes Problem konnte ich bis jetzt auch noch nicht lösen. Es scheint ein Bug in PHP zu sein...

      Ah, das hatte ich weder gefunden noch damals gelesen ;-) Und wie hast du es gelöst? Die Daten in eine temporäre Datei schreiben?

      MfG, Dennis.

      1. Re:

        selbes Problem konnte ich bis jetzt auch noch nicht lösen. Es scheint ein Bug in PHP zu sein...

        Ah, das hatte ich weder gefunden noch damals gelesen ;-) Und wie hast du es gelöst? Die Daten in eine temporäre Datei schreiben?

        Leider gar nicht und zu Deiner Probembeschreibung reiht sich auch ein, daß deflate die selben Fehler aufweist. Bei meinen Test hilten allerdings die Scripte nicht bei alle Resourcen unter Fehlermeldung inne. Einige Dokumente ließen sich ohne weiteres dekomprimieren. Meine Überlegung geht nun dahin, das man vielleicht ein oder zwei Bits am Anfangs- EndByte nur verschieben muß...

        Ich habe auf den Accept-Encoding-Header schlichtweg verzichtet. Wurmen tut es mich aber dennoch.

        Gruß aus Berlin!
        eddi

        --
        PHP: unter Linux installieren, in Bearbeitung Konfigurieren
        1. Hi eddi,

          Leider gar nicht und zu Deiner Probembeschreibung reiht sich auch ein, daß deflate die selben Fehler aufweist.

          Stimmt, das hatte ich auch schon ausprobiert. Ich habe es jetzt so gelöst:

          $tmp_file = "./body-" . time() . "-" . posix_getpid();  
          file_put_contents($tmp_file . ".gz");  
          exec("gunzip " . escapeshellarg($tmp_file));  
          $body = file_get_contents($tmp_file);  
          unlink($tmp_file);
          

          Sauberer wäre es natürlich noch, die Dateien in irgendeinem tmp-Verzeichnis abzulegen ;-)

          Meine Überlegung geht nun dahin, das man vielleicht ein oder zwei Bits am Anfangs- EndByte nur verschieben muß...

          So etwas vermute ich auch, deshalb dachte ich, es gäbe vl. irgendeine einfache Lösung...

          MfG, Dennis.

          1. Hallo,

            $tmp_file = "./body-" . time() . "-" . posix_getpid();

            file_put_contents($tmp_file . ".gz");
            exec("gunzip " . escapeshellarg($tmp_file));
            $body = file_get_contents($tmp_file);
            unlink($tmp_file);

              
            Könnte man sich hierbei die Datei nicht sparen? Aus der gunzip Hilfe:  
            \------------------------------------------------------------------------------------  
            usage: gunzip [-cdfhlLnNrtvV19] [-S suffix] [file ...]  
              
             -c --stdout      write on standard output, keep original files unchanged  
             file...          files to (de)compress. If none given, use standard input.  
            \-------------------------------------------------------------------------------------  
              
            Und mit PHPs [proc_open()](http://se.php.net/proc-open) und den ganzen fwrite() fread() Funktionen sollte es doch eigentlich leicht möglich sein auch ohne diese temporären Dateien zu arbeiten, unter Umständen auch ein bischen schneller weil das mehrmalige schreiben auf und lesen von Festplatte eventuell entfällt. Ob sich der Aufwand allerdings lohnt musst du selbst herausfinden ;-)  
              
              
            Grüße  
            Jeena Paradies
            
            -- 
            [Cronjobs von Zuhause aus](http://jeenaparadies.net/weblog/2006/apr/cronjob-mit-curl) mit cURL | [Jlog](http://jeenaparadies.net/webdesign/jlog/) | [Gourmetica Mentiri](http://jeenaparadies.net/gourmetica-mentiri/)
            
            1. Hallo,

              Ob sich der Aufwand allerdings lohnt musst du selbst herausfinden ;-)

              lohnt sich nicht: Lösung

              Gruß aus Berlin!
              eddi

      2. Hello Dennis,

        poste doch mal einen Header, mit dem es dann zumindest nach Abspeichern in eine Datei funktioniert hat.

        Was sagt dann Content-Transfer-Encoding?

        Harzliche Grüße vom Berg
        http://www.annerschbarrich.de

        Tom

        --
        Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
        Nur selber lernen macht schlau

    2. Hallo,

      function getHTTP($proto,$host,$port,$pfad){  
              # sendet einen HTTP-Request  
              $flag=true;  
              $conn=@[link:http://de3.php.net/manual/de/function.stream-socket-client.php@title=stream_socket_client]((([link:http://de3.php.net/manual/de/function.substr.php@title=substr]($proto,-1)=='p') ? 'tcp://' : 'ssl://').$host.':'.$port,$er,$es,10);  
        
              if([link:http://de3.php.net/manual/de/function.is-resource.php@title=is_resource]($conn)){  
                      if($host==$this->host)  
                              [link:http://de3.php.net/manual/de/function.fwrite.php@title=fwrite]($conn,'GET '.$pfad." HTTP/1.1\r\n");  
                      else{  
                              [link:http://de3.php.net/manual/de/function.fwrite.php@title=fwrite]($conn,'HEAD '.$pfad." HTTP/1.1\r\n");  
                              $flag=false;  
                      }  
        
                      [link:http://de3.php.net/manual/de/function.fwrite.php@title=fwrite]($conn,'Host: '.$host."\r\n");  
                      [link:http://de3.php.net/manual/de/function.fwrite.php@title=fwrite]($conn,"Accept: */*;q=0.9\r\n");  
                      [link:http://de3.php.net/manual/de/function.fwrite.php@title=fwrite]($conn,"Accept-Encoding: gzip,deflate;q=0.9\r\n");  
                      [link:http://de3.php.net/manual/de/function.fwrite.php@title=fwrite]($conn,"Keep-Alive: 60\r\n");  
                      [link:http://de3.php.net/manual/de/function.fwrite.php@title=fwrite]($conn,"Connection: keep-alive\r\n\r\n");  
        
                      $size=0;  
                      $cont=false;  
                      $tran=false;  
        
                      if([link:http://de3.php.net/manual/de/function.substr.php@title=substr]([link:http://de3.php.net/manual/de/function.fgets.php@title=fgets]($conn),9,3)==200){  
                              do{     $t=[link:http://de3.php.net/manual/de/function.fgets.php@title=fgets]($conn,21000);  
                                      $t=[link:http://de3.php.net/manual/de/function.explode.php@title=explode](":",$t,2);  
                                      if([link:http://de3.php.net/manual/de/function.stristr.php@title=stristr]($t[0],'content-encoding')!==false)            $cont=[link:http://de3.php.net/manual/de/function.trim.php@title=trim]($t[1]);  
                                      elseif([link:http://de3.php.net/manual/de/function.stristr.php@title=stristr]($t[0],'content-length')!==false)          $size=[link:http://de3.php.net/manual/de/function.trim.php@title=trim]($t[1]);  
                                      elseif([link:http://de3.php.net/manual/de/function.stristr.php@title=stristr]($t[0],'transfer-encoding')!==false)       $tran=[link:http://de3.php.net/manual/de/function.trim.php@title=trim]($t[1]);  
                              }while($t[0]!="\r\n");  
        
                              if($flag){  
                                      $t='';  
                                      if($tran=='chunked')  
                                              while(($c=[link:http://de3.php.net/manual/de/function.hexdec.php@title=hexdec]([link:http://de3.php.net/manual/de/function.trim.php@title=trim]([link:http://de3.php.net/manual/de/function.fgets.php@title=fgets]($conn))))!=0){  
                                                      $x='';  
                                                      while([link:http://de3.php.net/manual/de/function.strlen.php@title=strlen]($x.=[link:http://de3.php.net/manual/de/function.fread.php@title=fread]($conn,$c-[link:http://de3.php.net/manual/de/function.strlen.php@title=strlen]($x)))<$c);  
                                                      $t.=$x;  
                                                      [link:http://de3.php.net/manual/de/function.fgets.php@title=fgets]($conn);  
                                              }  
                                      elseif($size<1048577)  
                                              if($size>0)  
                                                      while([link:http://de3.php.net/manual/de/function.strlen.php@title=strlen]($t.=[link:http://de3.php.net/manual/de/function.fread.php@title=fread]($conn,$size-[link:http://de3.php.net/manual/de/function.strlen.php@title=strlen]($t)))<$size);  
                                              else  
                                                      while(![link:http://de3.php.net/manual/de/function.feof.php@title=feof]($conn)){  
                                                              $t.=($c=[link:http://de3.php.net/manual/de/function.fread.php@title=fread]($conn,4096));  
                                                              if([link:http://de3.php.net/manual/de/function.strlen.php@title=strlen]($t)>1048575){  
                                                                      $this->error='Resource über 1 MB groß';  
                                                                      break;  
                                                              }  
                                                      }  
                                      else    return('Resource über 1 MB groß');  
                                      if($cont && $t!='')  
                                              if([link:http://de3.php.net/manual/de/function.stristr.php@title=stristr]($cont,'deflate')!==false)     $t=[link:http://de3.php.net/manual/de/function.gzuncompress.php@title=gzuncompress]($t);  
                                              elseif([link:http://de3.php.net/manual/de/function.stristr.php@title=stristr]($cont,'gzip')!==false)    $t=[link:http://de3.php.net/manual/de/function.gzinflate.php@title=gzinflate]([link:http://de3.php.net/manual/de/function.substr.php@title=substr]($t,10));  
                              }  
                              return($t);  
                      }  
                      else    return('Resource nicht da');  
              }  
              else    return('keine Verbindung');  
      }
      

      Gruß aus Berlin!
      eddi

      1. Hi eddi,

        Ah ja - es scheint wohl genau dann zu sein, wenn sowohl Transfer-Encoding als auch Content-Encoding stattfindet. Wir beide haben bis jetzt ja nur das Content-Encoding beachtet.

        while(($c=link:http://de3.php.net/manual/de/function.hexdec.php@title=hexdec)!=0){

        $x='';
           while(link:http://de3.php.net/manual/de/function.strlen.php@title=strlen<$c);
           $t.=$x;
           link:http://de3.php.net/manual/de/function.fgets.php@title=fgets;
        }

          
        Dieser Code Teil scheint also das Transfer-Encoding aufzuheben, so denn ein Transfer-Encoding stattgefunden hat. Ist dies irgendwo dokumentiert, wie ich das zu entschlüsseln habe?  
          
        
        > ~~~php
        
        if([link:http://de3.php.net/manual/de/function.stristr.php@title=stristr]($cont,'deflate')!==false)  
        
        >     $t=[link:http://de3.php.net/manual/de/function.gzuncompress.php@title=gzuncompress]($t);  
        >  elseif([link:http://de3.php.net/manual/de/function.stristr.php@title=stristr]($cont,'gzip')!==false)  
        >     $t=[link:http://de3.php.net/manual/de/function.gzinflate.php@title=gzinflate]([link:http://de3.php.net/manual/de/function.substr.php@title=substr]($t,10));
        
        

        Was mich hieran etwas iritiert ist, dass wenn Content-Encoding deflate genutzt wurde, es mit gzuncompress() entpackt wird und wenn Content-Encoding gzip genutzt wurde, es mit gzinflate() entpackt wird... Für mich wäre es irgendwie logischer, wenn es genau anders herum wäre!

        MfG, Dennis.

        1. Re:

          Ist dies irgendwo dokumentiert, wie ich das zu entschlüsseln habe?

          http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1

          if(link:http://de3.php.net/manual/de/function.stristr.php@title=stristr!==false)

          $t=link:http://de3.php.net/manual/de/function.gzuncompress.php@title=gzuncompress;
          elseif(link:http://de3.php.net/manual/de/function.stristr.php@title=stristr!==false)
              $t=link:http://de3.php.net/manual/de/function.gzinflate.php@title=gzinflate;

          
          >   
          > Was mich hieran etwas iritiert ist, dass wenn Content-Encoding deflate genutzt wurde, es mit gzuncompress() entpackt wird und wenn Content-Encoding gzip genutzt wurde, es mit gzinflate() entpackt wird... Für mich wäre es irgendwie logischer, wenn es genau anders herum wäre!  
            
          Frage mich bitte etwas leichteres, denn ich habe auch nicht schlecht gestaunt. Jedenfalls wird der Stream so ordnungsgemäß dekomprimiert.  
            
            
          Gruß aus Berlin!  
          eddi  
          
          -- 
          PHP: [PHP unter Linux installieren](http://de.wikibooks.org/wiki/Websiteentwicklung:_PHP:_Installation#UNIX-artige_Systeme), in Bearbeitung [PHP Konfigurieren](http://de.wikibooks.org/wiki/Websiteentwicklung:_PHP:_Konfiguration)  
            
          ![](http://212.227.99.60/gentoo.jpg)
          
  2. Ein paar Verbesserungen auf HTTP-Ebene für Dein Script:

    $fp = fsockopen("www.escape-to-space.de", 80, $errno, $errstr, 30);

    $out = "GET /pfad/seite HTTP/1.1\r\n"
         . "Host: www.escape-to-space.de\r\n"
         . "User-Agent: PHP-Client"

    UA = unnützer Traffic

    . "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n"

    "Accept: text/*,application/xhtml+xml;q=0.9,*/*;q=0.5\r\n" // text/plain ist zwar dann auch 0.9 aber so schlimm ist das nicht

    . "Accept-Language: de-de,de;q=0.7,en;q=0.3\r\n"
         . "Accept-Encoding: gzip,deflate\r\n"
         . "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"

    "Accept-Charset: ISO-8859-1,utf-8,*;q=0.7\r\n"

    . "Keep-Alive: 300\r\n"
         . "Connection: keep-alive\r\n";

    wenn Du nicht mehrere Resourcen über diesen Socket beziehen willst, ist das für den angesprochenen Server kontraproduktiv; dementsprechend dann bitte:
             "Connection: close\r\n\r\n";

    fwrite($fp, $out);

    Gruß aus Berlin!
    eddi

    1. Hi eddi,

      . "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n"
               "Accept: text/*,application/xhtml+xml;q=0.9,*/*;q=0.5\r\n"
           . "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
               "Accept-Charset: ISO-8859-1,utf-8,*;q=0.7\r\n"

      An diesen Stellen möchte ich mich identisch zu einem Firefox 1.5 verhalten - und mein FF sendet genau die von mir genannten Header. Warum ich mich identisch zu einem Firefox 1.5 verhalten möchte? Ich möchte in jeden Fall dasselbe Ergebnis bekommen, egal wie auch immer der Server auf Accept oder Accept-Charset reagieren mag.

      wenn Du nicht mehrere Resourcen über diesen Socket beziehen willst, ist das für den angesprochenen Server kontraproduktiv; dementsprechend dann bitte:
               "Connection: close\r\n\r\n";

      Stimmt - zur Zeit setzte ich nur einen Request ab, allerdings werden es vermutlich noch zwei werden. Da diese jedoch nicht direkt nacheinander erfolgen werden ist die Frage, ob ich da lieber die Verbindung offen halte oder nicht... Wie setze ich denn über die offene Verbindung einen zweiten Request ab?

      MfG, Dennis.

      1. Re:

        Ich möchte in jeden Fall dasselbe Ergebnis bekommen, egal wie auch immer der Server auf Accept oder Accept-Charset reagieren mag.

        Dann denke bitte auch an die Browserweichen und nutze im U-A den identischen String.

        Wie setze ich denn über die offene Verbindung einen zweiten Request ab?

        Halte die Verbindung offen. Vor dem erneuten Request, würde ich allerdings noch mal mit if(stream_socket_get_name($this->connect,true)===false) nachfragen, ob sich die Gegenseite auch melden - sprich: die Verbindung noch offen ist. Danach schreibst Du ganz normal Deine Header in die Verbinung und liest den Response aus.

        Gruß aus Berlin!
        eddi