Advertisement
Guest User

Untitled

a guest
Oct 4th, 2016
154
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 21.71 KB | None | 0 0
  1. <?php
  2.  
  3. function dump($v) {
  4.   echo('<pre>' . var_export($v, true) . '</pre>');
  5. }
  6.  
  7.  
  8. error_reporting(E_ALL);
  9.  
  10. // header("http/1.0              404                \t\t\tNot Found\t");
  11. // header("Content-Type:\t \t  text/plain;charset=windows-1251 =пизда%00;charset   \t\t\t\t= \"utf-8\" foo, bar");
  12. header('cache-control: no-cache');
  13. header('Cache-control: no-store'); // cache-control: no-store
  14. // echo("Тест\n");
  15.  
  16. // list(, $h) = unpack('n', "\0\x20");
  17. // print_r($h);
  18.  
  19. // $f = fopen('long_string.txt', 'r');
  20. // echo(fgets($f, null));
  21. // fgets($f, null);
  22. // fgets($f);
  23. // fread($f, 65536);
  24. // var_dump(fread($f, 1));
  25. // echo(fread($f, null));
  26. // die;
  27.  
  28.  
  29. // preg_match('~;\s*charset\s*=\s*(?:"([^"]+)"|([^\s;]+))~i', "text/html;  charset\t=   utf-8\t\"q;", $matches);
  30. // var_dump($matches);
  31. // echo('charset: ' . (count($matches) ? (isset($matches[2]) ? $matches[2] : $matches[1]) : ''));
  32. // echo('\\\t\'\0 ');
  33.  
  34.  
  35. preg_match_all('/[\x00-\xff]/', 'тест', $m);
  36. // var_dump($m);
  37.  
  38. $headers = "HTTP/1.1 200\r\n" .
  39.            "Content-Type:" .
  40.            "\r\n\ttext/html;" .
  41.            "\r\n\t charset=utf-8";
  42.  
  43. // echo("Headers:\n");
  44. // echo(preg_replace('/\r\n[ \t]+/', ' ', $headers));
  45. // Bad chars: \x00-\x08\x0b\x0c\x0e-\x1f\x7f
  46.  
  47. // set_time_limit(5);
  48. // echo(str_repeat('-', 80) . PHP_EOL);
  49.  
  50. // list($a, $b) = explode(':', '');
  51.  
  52. // die;
  53.  
  54. if (!function_exists('http_build_url')) {
  55.   function http_build_url($parts) {
  56.     if (!is_array($parts)) {
  57.       return false;
  58.     }
  59.  
  60.     return (isset($parts['scheme']) ? $parts['scheme'] . '://' : '') .
  61.       (isset($parts['user']) ?
  62.         $parts['user'] .
  63.         (isset($parts['pass']) ? ':' . $parts['pass'] : '') . '@'
  64.         : '') .
  65.       (isset($parts['host']) ? $parts['host']: '') .
  66.       (isset($parts['port']) ? ':' . $parts['port']: '') .
  67.       (isset($parts['path']) ?
  68.         (substr($parts['path'], 0, 1) != '/' ? '/' : '') . $parts['path']
  69.         : '') .
  70.       (isset($parts['query']) ? '?' . $parts['query']: '') .
  71.       (isset($parts['fragment']) ? '#' . $parts['fragment']: '');
  72.   }
  73. }
  74.  
  75.  
  76. class SocketClient {
  77.   protected $resource;
  78.  
  79.   public function __construct($remoteSocket, $connectionTimeout = null, $flags = null, $context = null) {
  80.     if (is_null($connectionTimeout)) {
  81.       $connectionTimeout = ini_get('default_socket_timeout');
  82.     }
  83.     if (is_null($flags)) {
  84.       $flags = STREAM_CLIENT_CONNECT;
  85.     }
  86.     // http://php.net/manual/ru/function.set-error-handler.php
  87.     set_error_handler(array($this, 'errorHandler'));
  88.     if (is_null($context)) {
  89.       $this->resource = stream_socket_client($remoteSocket, $errNo, $errStr, $connectionTimeout, $flags);
  90.     } else {
  91.       $this->resource = stream_socket_client($remoteSocket, $errNo, $errStr, $connectionTimeout, $flags, $context);
  92.     }
  93.     restore_error_handler();
  94.     // никогда не выполнится?
  95.     if ($this->resource === false) {
  96.       // $errStr = iconv('windows-1251', 'utf-8', $errStr);
  97.       throw new SocketError(sprintf('Connection to %s failed: %s (%d)', $remoteSocket, $errStr, $errNo));
  98.     }
  99.   }
  100.  
  101.   public function write($data) {
  102.     $bytes = @fwrite($this->resource, $data);
  103.     if ($bytes === false) {
  104.       $this->checkTimeout();
  105.       throw new SocketError('Could not write to socket');
  106.     }
  107.     return $bytes;
  108.   }
  109.  
  110.   public function read($size) {
  111.     // Если достигли конца файла, вернет пустую строку. Только в случае ошибки возвращает false
  112.     $data = @fread($this->resource, $size);
  113.     if ($data === false) {
  114.       // $data вроде всегда false, если истек таймаут
  115.       $this->checkTimeout();
  116.       throw new SocketError('Could not read from socket');
  117.     }
  118.     return $data;
  119.   }
  120.  
  121.   public function readLine($size = null) {
  122.     if (is_null($size)) {
  123.       // fgets($fp, null) приводит к ошибке
  124.       // Читает строку пока не встретит \n либо конец файла
  125.       $data = @fgets($this->resource);
  126.     } else {
  127.       $data = @fgets($this->resource, $size);
  128.     }
  129.     if ($data === false) {
  130.       $this->checkTimeout();
  131.       throw new SocketError('Could not read from socket');
  132.     }
  133.     return $data;
  134.   }
  135.  
  136.   public function eof() {
  137.     return $this->isResource() ? feof($this->resource) : true;
  138.   }
  139.  
  140.   public function setTimeout($seconds, $microseconds = 0) {
  141.     return stream_set_timeout($this->resource, $seconds, $microseconds);
  142.   }
  143.  
  144.   public function setBlocking($mode) {
  145.     return stream_set_blocking($this->resource, $mode);
  146.   }
  147.  
  148.   public function enableCrypto($enable, $cryptoType = null, $sessionStream = null) {
  149.     // http://php.net/manual/ru/function.stream-socket-enable-crypto.php
  150.     if (!is_null($sessionStream)) {
  151.       return @stream_socket_enable_crypto($this->resource, $enable, $cryptoType, $sessionStream);
  152.     }
  153.     if (!is_null($cryptoType)) {
  154.       return @stream_socket_enable_crypto($this->resource, $enable, $cryptoType);
  155.     }
  156.     return @stream_socket_enable_crypto($this->resource, $enable);
  157.   }
  158.  
  159.   public function getMetaData($key = null) {
  160.     if ($this->isResource()) {
  161.       $meta = stream_get_meta_data($this->resource);
  162.       if (is_null($key)) {
  163.         return $meta;
  164.       }
  165.       if (isset($meta[$key])) {
  166.         return $meta[$key];
  167.       }
  168.     }
  169.     // return null;
  170.   }
  171.  
  172.   public function isResource() {
  173.     return is_resource($this->resource);
  174.   }
  175.  
  176.   public function close() {
  177.     if ($this->isResource()) {
  178.       fclose($this->resource);
  179.     }
  180.   }
  181.  
  182.   public function __destruct() {
  183.     $this->close();
  184.   }
  185.  
  186.   protected function errorHandler($errNo, $errStr, $errFile, $errLine) {
  187.     throw new SocketError(sprintf('Socket error %d: %s', $errNo, $errStr));
  188.   }
  189.  
  190.   protected function checkTimeout() {
  191.     if ($this->getMetaData('timed_out')) {
  192.       throw new SocketTimeout('Socket timed out');
  193.     }
  194.   }
  195. }
  196.  
  197.  
  198. class SocketError extends Exception {}
  199.  
  200.  
  201. class SocketTimeout extends SocketError {}
  202.  
  203.  
  204. class Header {
  205.   protected $name;
  206.   protected $value;
  207.  
  208.   public function __construct($name, $value) {
  209.     $this->setName($name);
  210.     $this->setValue($value);
  211.   }
  212.  
  213.   public function setName($name) {
  214.     $this->name = $name;
  215.   }
  216.  
  217.   public function setValue($value) {
  218.     $this->value = $value;
  219.   }
  220.  
  221.   public function getName() {
  222.     return $this->name;
  223.   }
  224.  
  225.   public function getValue() {
  226.     return $this->value;
  227.   }
  228.  
  229.   public function __toString() {
  230.     return sprintf('%s: %s', $this->name,  $this->value);
  231.   }
  232. }
  233.  
  234.  
  235. /**
  236.   * Класс для работы с http-заголовками.
  237.   *
  238.   * Имена полей регистронезависимы. Значения полей можно получить, обращаясь к
  239.   * объекту как к массиву. К объекту применимы вызовы функций count, isset и
  240.   * unset, а так же возможен по нему обход с помощью цикла foreach.
  241.   */
  242. class Headers implements ArrayAccess, Countable, IteratorAggregate {
  243.   protected $headers = array();
  244.  
  245.   public function __construct($headers = array()) {
  246.     $this->update($headers);
  247.   }
  248.  
  249.   /**
  250.     * Добавляет новый заголовок.
  251.     */
  252.   public function add($name, $value) {
  253.     $this->headers[strtolower($name)] = new Header($name, $value);
  254.   }
  255.  
  256.   /**
  257.     * Добавляет новый заголовок либо заменяет значение существующего, при этом
  258.     * оригинальное имя сохраняется.
  259.     */
  260.   public function set($name, $value) {
  261.     $lower_name = strtolower($name);
  262.  
  263.     if (isset($this->headers[$lower_name])) {
  264.       $this->headers[$lower_name]->setValue($value);
  265.     } else {
  266.       $this->headers[$lower_name] = new Header($name, $value);
  267.     }
  268.  
  269.     // возвращаем установленное значение
  270.     return $value;
  271.   }
  272.  
  273.   public function update($data) {
  274.     foreach ($data as $name => $value) {
  275.       $this->set($name, $value);
  276.     }
  277.   }
  278.  
  279.   public function get($name, $default = null) {
  280.     $name = strtolower($name);
  281.     return isset($this->headers[$name]) ? $this->headers[$name]->getValue() : $default;
  282.   }
  283.  
  284.   /**
  285.     * Возвращает оригинальное имя заголовка.
  286.     */
  287.   public function getOriginalName($name) {
  288.     $name = strtolower($name);
  289.  
  290.     if (isset($this->headers[$name])) {
  291.       return $this->headers[$name]->getName();
  292.     }
  293.   }
  294.  
  295.   public function has($name) {
  296.     return isset($this->headers[strtolower($name)]);
  297.   }
  298.  
  299.   public function remove($name) {
  300.     unset($this->headers[strtolower($name)]);
  301.   }
  302.  
  303.   public function toArray() {
  304.     $headers = array();
  305.  
  306.     foreach ($this->headers as $header) {
  307.       $headers[$header->getName()] = $header->getValue();
  308.     }
  309.  
  310.     return $headers;
  311.   }
  312.  
  313.   public function __toString() {
  314.     $out = '';
  315.    
  316.     foreach ($this->headers as $header) {
  317.       $out .= $header->__toString() . "\r\n";
  318.     }
  319.  
  320.     return $out;
  321.   }
  322.  
  323.   public function __clone() {
  324.     foreach ($this->headers as &$h) {
  325.       $h = clone $h;
  326.     }
  327.   }
  328.  
  329.   public static function parse($header_str) {
  330.     // new self будет создавать экземпляр класса, где был объявлен метод
  331.     $headers = new static;
  332.  
  333.     if (strlen($header_str)) {
  334.       // normalize LWS
  335.       $header_str = preg_replace('/\r\n[\x20\t]+/', ' ', $header_str);
  336.       $offset = 0;
  337.       $length = strlen($header_str);
  338.  
  339.       while ($offset < $length) {
  340.         $pos = strpos($header_str, "\r\n", $offset);
  341.  
  342.         if ($pos === false) {
  343.           throw new MalformedHeaderError('Header should end with CRLF (\x0d\x0a)');
  344.         }
  345.  
  346.         $line = substr($header_str, $offset, $pos - $offset);
  347.         $offset = $pos + 2;
  348.         $parts = explode(':', $line, 2);
  349.  
  350.         if (count($parts) < 2) {
  351.           throw new MalformedHeaderError('Header without colon (:)');
  352.         }
  353.  
  354.         list($name, $value) = $parts;
  355.         $value = trim($value);
  356.        
  357.         if (isset($headers[$name])) {
  358.           // по rfc заголовки с одинаковыми либо различающимися лишь регистром
  359.           // именами могут быть объеденены в один. Их значения должны быть
  360.           // добавлены к значениям первого заголовка и разделены запятыми.
  361.           //
  362.           // Пример:
  363.           //
  364.           // Cache-Control: no-cache
  365.           // cache-control: no-store
  366.           //
  367.           // После нормализации:
  368.           //
  369.           // Cache-Control: no-cache, no-store
  370.           $headers[$name] .= ', ' . $value;
  371.         } else {
  372.           $headers[$name] = $value;
  373.         }
  374.  
  375.         // if ($offset == $length) {
  376.         //   break;
  377.         // }
  378.       }
  379.     }
  380.  
  381.     return $headers;
  382.   }
  383.  
  384.   public function offsetSet($offset, $value) {
  385.     return $this->set($offset, $value);
  386.   }
  387.  
  388.   public function offsetExists($offset) {
  389.     return $this->has($offset);
  390.   }
  391.  
  392.   public function offsetUnset($offset) {
  393.     $this->remove($offset);
  394.   }
  395.  
  396.   public function offsetGet($offset) {
  397.     return $this->get($offset);
  398.   }
  399.  
  400.   public function count() {
  401.     return count($this->headers);
  402.   }
  403.  
  404.   public function getIterator() {
  405.     return new ArrayIterator($this->toArray());
  406.   }
  407. }
  408.  
  409. class MalformedHeaderError extends Exception {}
  410.  
  411.  
  412. class Request {
  413.   const SCHEME_RE = '/^https?$/i';
  414.   const HTTP_PORT = 80;
  415.   const HTTPS_PORT = 443;
  416.  
  417.   // объявим публичными чтобы не писать кучу методов типа getFoo, setFoo
  418.   public $connectionTimeout;
  419.   public $timeout;
  420.  
  421.   protected $defaultHeaders = array(
  422.     'Accept' => '*/*',
  423.     'Accept-Encoding' => 'gzip, deflate',
  424.     'User-Agent' => 'Mozilla/5.0'
  425.   );
  426.  
  427.   protected $method;
  428.   protected $url;
  429.   protected $username;
  430.   protected $password;
  431.   protected $headers;
  432.  
  433.   public function __construct($method, $url, $username = null, $password = null) {
  434.     $this->method = $method;
  435.     $this->url = $url;
  436.     $this->username = $username;
  437.     $this->password = $password;
  438.     $this->headers = new Headers($this->defaultHeaders);
  439.   }
  440.  
  441.   public function setHeader($name, $value) {
  442.      $this->headers[$name] = $value;
  443.   }
  444.  
  445.   public function send($data = '') {
  446.     $url = parse_url($this->url);
  447.  
  448.     if ($url === false || !isset($url['scheme']) || !isset($url['host'])) {
  449.       throw new RequestError('Bad URL');
  450.     }
  451.  
  452.     $scheme = $url['scheme'];
  453.  
  454.     if (!preg_match(static::SCHEME_RE, $scheme)) {
  455.       throw new RequestError(sprintf('Unsupported scheme: %s', $scheme));
  456.     }
  457.  
  458.     $hostname = $url['host'];
  459.     $isHttps = strtolower($scheme) == 'https';
  460.     $port = isset($url['port']) ? $url['port'] : ($isHttps ? static::HTTPS_PORT : static::HTTP_PORT);
  461.     $address = sprintf('%s://%s:%d', $isHttps ? 'tls' : 'tcp', $hostname, $port);
  462.     // http://php.net/manual/ru/context.ssl.php
  463.     $options = array(
  464.       'ssl' => array(
  465.         'verify_peer' => false,
  466.         'verify_peer_name' => false,
  467.         'allow_self_signed' => true,
  468.         // 'local_cert' => '',
  469.         // 'local_pk' => '',
  470.         // 'passphrase' => '',
  471.       )
  472.     );
  473.     $context = stream_context_create($options);
  474.     $socket = new SocketClient($address, $this->connectionTimeout, null, $context);
  475.  
  476.     if ($this->timeout) {
  477.       // Таймаут на чтение/запись для сокета
  478.       $socket->setTimeout($this->timeout);
  479.     }
  480.  
  481.     if ($isHttps) {
  482.       // Включаем шифрование трафика
  483.       $socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
  484.     }
  485.  
  486.     $uri = isset($url['path']) ? $url['path'] : '/';
  487.  
  488.     if (isset($url['query'])) {
  489.       $uri .= '?' . $url['query'];
  490.     }
  491.  
  492.     $headers = $this->headers;
  493.     // $headers = clone $this->headers;
  494.  
  495.     // if (isset($url['user'])) {
  496.     //   $username = $url['user'];
  497.     //   $password = isset($url['pass']) ? $url['pass'] : '';
  498.     //   $headers['Authorization'] = 'Basic ' . base64_encode($username . ':' . $password);
  499.     // }
  500.  
  501.     if (!is_null($this->username)) {
  502.       $headers['Authorization'] = 'Basic ' . base64_encode(sprintf('%s:%s', $this->username, $this->password));
  503.     }
  504.  
  505.     $content_length = strlen($data);
  506.  
  507.     if ($content_length) {
  508.       $headers['Content-Length'] = $content_length;
  509.     }
  510.  
  511.     $isDefaultPort = $isHttps ? $port == static::HTTPS_PORT : $port == static::HTTP_PORT;
  512.     $host = $isDefaultPort ? $hostname : sprintf('%s:%d', $hostname, $port);
  513.     $request = "{$this->method} $uri HTTP/1.1\r\nHost: $host\r\n";
  514.  
  515.     foreach ($headers as $k => $v) {
  516.       $request .= "$k: $v\r\n";
  517.     }
  518.  
  519.     $request .= "\r\n$data";
  520.     $socket->write($request);
  521.     return new Response($socket, $this->method, $this->url);
  522.   }
  523. }
  524.  
  525.  
  526. class RequestError extends Exception {}
  527.  
  528.  
  529. class Response {
  530.   const STATUS_RE = '/^HTTP\/(\d\.\d) ([1-5]\d\d) (.*)\r\n$/';
  531.   const CHUNK_RE = '/^[a-fA-F0-9]+(?=;|\r\n)/';
  532.   const NUM_RE = '/^(0|[1-9]\d*)$/';
  533.   const CHARSET_RE = '/;\s*charset\s*=\s*(?:"([^"]+)"|([^\s";]+))/i';
  534.   const STATUS_MAXLENGTH = 4096;
  535.   const HEADER_MAXLENGTH = 4096;
  536.   const BLOCK_SIZE = 4096;
  537.  
  538.   public $method;
  539.   public $url;
  540.   public $httpVersion;
  541.   public $statusCode;
  542.   public $reasonPhrase;
  543.   public $headers;
  544.   public $location = '';
  545.   public $contentType = '';
  546.   public $mimeType = '';
  547.   public $mainType = '';
  548.   public $subType = '';
  549.   public $charset = '';
  550.   public $content = '';
  551.  
  552.   protected $socket;
  553.   protected $redirectCodes = array(301, 302, 303, 307);
  554.  
  555.   public function __construct($socket, $method, $url) {
  556.     $this->socket = $socket;
  557.     $this->method = $method;
  558.     $this->url = $url;
  559.     $this->processResponse();
  560.   }
  561.  
  562.   public function isOk() {
  563.     return $this->statusCode == 200;
  564.   }
  565.  
  566.   public function isRedirect() {
  567.     return in_array($this->statusCode, $this->redirectCodes);
  568.   }
  569.  
  570.   protected function processResponse() {
  571.     $statusLine = $this->socket->readLine(static::STATUS_MAXLENGTH);
  572.  
  573.     if (!preg_match(static::STATUS_RE, $statusLine, $matches)) {
  574.       throw new ResponseError('Invalid status line');
  575.     }
  576.  
  577.     $this->httpVersion = $matches[1];
  578.     $this->statusCode = intval($matches[2]);
  579.     $this->reasonPhrase = $matches[3];
  580.     $headers = '';
  581.  
  582.     while (!$this->socket->eof()) {
  583.       $line = $this->socket->readLine(static::HEADER_MAXLENGTH);
  584.  
  585.       if ($line == '' || $line == "\r\n") {
  586.         break;
  587.       }
  588.  
  589.       $headers .= $line;
  590.     }
  591.  
  592.     // dump($headers);
  593.     $this->headers = Headers::parse($headers);
  594.  
  595.     if (isset($this->headers['location'])) {
  596.       $this->location = $this->headers['location'];
  597.     }
  598.  
  599.     if (isset($this->headers['content-type'])) {
  600.       $this->contentType = $this->headers['content-type'];
  601.       $ex = explode(';', $this->contentType, 1);
  602.       $this->mimeType = trim($ex[0]);
  603.       $ex = explode('/', $this->mimeType, 2);
  604.       $this->mainType = $ex[0];
  605.      
  606.       if (isset($ex[1])) {
  607.         $this->subType = $ex[1];
  608.       }
  609.  
  610.       // Кодировка
  611.       if (preg_match(static::CHARSET_RE, $this->contentType, $m)) {
  612.         $this->charset = isset($m[2]) ? $m[2] : $this->decodeSlashes($m[1]);
  613.       }
  614.     }
  615.  
  616.     // На запрос, отправленный методом HEAD, сервер не должен возвращать ответ
  617.     if ($this->method != 'HEAD') {
  618.       // Если установлен Transfer-Encoding Content-Length игнорируется.
  619.       // If a Content-Length header field (section 14.13) is present, its decimal
  620.       // value in OCTETs represents both the entity-length and the transfer-length.
  621.       // The Content-Length header field MUST NOT be sent if these two lengths are
  622.       // different (i.e., if a Transfer-Encoding header field is present). If a
  623.       // message is received with both a Transfer-Encoding header field and a
  624.       // Content-Length header field, the latter MUST be ignored.
  625.       if (isset($this->headers['transfer-encoding'])) {
  626.         $enc = strtolower($this->headers['transfer-encoding']);
  627.  
  628.         if ($enc != 'chunked') {
  629.           throw new ResponseError(sprintf('Unsupported transfer-encoding: %s', $enc));
  630.         }
  631.  
  632.         for (;;) {
  633.           $chunk = $this->socket->readLine();
  634.  
  635.           if (!preg_match(static::CHUNK_RE, $chunk, $matches)) {
  636.             throw new ResponseError('Invalid chunk');
  637.           }
  638.  
  639.           $size = hexdec($matches[0]);
  640.  
  641.           if (!$size) {
  642.             break;
  643.           }
  644.  
  645.           $this->content .= $this->socket->read($size);
  646.           // go to /dev/null
  647.           $trailerLine = $this->socket->readLine();
  648.         }
  649.       } else {
  650.         // unset($this->headers['content-length']);
  651.         if (isset($this->headers['content-length'])) {
  652.           if (!preg_match(static::NUM_RE, $this->headers['content-length'])) {
  653.             throw new Exception('Invalid content-length');
  654.           }
  655.  
  656.           // читаем количество байт указанных в заголовке
  657.           $length = intval($this->headers['content-length']);
  658.  
  659.           if ($length) {
  660.             $this->content = $this->socket->read($length);
  661.           }
  662.         } else {
  663.           // Читаем данные пока не кончатся?
  664.           while (!$this->socket->eof()) {
  665.             $data = $this->socket->read(static::BLOCK_SIZE);
  666.             $this->content .= $data;
  667.  
  668.             if (strlen($data) < static::BLOCK_SIZE) {
  669.               break;
  670.             }
  671.           }
  672.         }
  673.       }
  674.  
  675.       // Если были переданы Transfer-Encoding и Content-Encoding, то мы сначала
  676.       // собираем строку из чанков, а только потом декодируем
  677.       if (isset($this->headers['content-encoding'])) {
  678.         $enc = strtolower($this->headers['content-encoding']);
  679.         $decoded = $this->decodeContent($this->content, $enc);
  680.  
  681.         if ($decoded === false) {
  682.           throw new ResponseError(sprintf('Unable to decode content with content-encoding: %s', $enc));
  683.         }
  684.  
  685.         $this->content = $decoded;
  686.       }
  687.     }
  688.  
  689.     $this->socket->close();
  690.   }
  691.  
  692.   protected function decodeContent($content, $encoding) {
  693.     // $encoding = strtolower($encoding);
  694.  
  695.     if ($encoding == 'identity') {
  696.       return $content;
  697.     }
  698.  
  699.     if ($encoding == 'gzip') {
  700.       // http://php.net/manual/ru/function.gzinflate.php#77336
  701.       return @gzinflate(substr($content, 10, -8));
  702.     }
  703.  
  704.     if ($encoding == 'deflate') {
  705.       // https://github.com/zendframework/zend-http/blob/master/src/Response.php
  706.       // first byte is usually 0x78 but can also be 0x08,
  707.       // 0x18, 0x28, 0x38, 0x48, 0x58, or 0x68.  The first two bytes, when
  708.       // interpreted as an unsigned 16-bit number in big-endian byte order,
  709.       // contain a value that is a multiple of 31.
  710.       $zlibHeader = unpack('n', substr($content, 0, 2));
  711.  
  712.       if ($zlibHeader[1] % 31 == 0) {
  713.         return @gzuncompress($content);
  714.       }
  715.  
  716.       return @gzinflate($content);
  717.     }
  718.  
  719.     return false;
  720.   }
  721.  
  722.   protected function decodeSlashes($v) {
  723.     return $v;
  724.   }
  725. }
  726.  
  727.  
  728. class ResponseError extends Exception {}
  729.  
  730.  
  731. // $url = 'https://httpbin.org/gzip'; // ok
  732. // $url = 'https://httpbin.org/deflate'; // ok
  733. // $url = 'http://www.gbase.de/global/news/0/55322.html'; // fail
  734. $url = 'https://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx'; // ok
  735.  
  736. $request = new Request('GET', $url);
  737. $response = $request->send();
  738. header("Content-Type: {$response->contentType}");
  739. echo($response->content);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement