Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- function dump($v) {
- echo('<pre>' . var_export($v, true) . '</pre>');
- }
- // header("http/1.0 404 \t\t\tNot Found\t");
- header("Content-Type:\t \t text/plain;charset=windows-1251 =пизда%00;charset \t\t\t\t= \"utf-8\" foo, bar");
- header('cache-control: no-cache');
- header('Cache-control: no-store'); // cache-control: no-store
- echo("Тест\n");
- preg_match('~;\s*charset\s*=\s*(?:"([^"]+)"|([^\s;]+))~i', "text/html; charset\t= utf-8\t\"q;", $matches);
- // var_dump($matches);
- // echo('charset: ' . (count($matches) ? (isset($matches[2]) ? $matches[2] : $matches[1]) : ''));
- // echo('\\\t\'\0 ');
- preg_match_all('/[\x00-\xff]/', 'тест', $m);
- // var_dump($m);
- $headers = "HTTP/1.1 200\r\n" .
- "Content-Type:" .
- "\r\n\ttext/html;" .
- "\r\n\t charset=utf-8";
- // echo("Headers:\n");
- // echo(preg_replace('/\r\n[ \t]+/', ' ', $headers));
- // Bad chars: \x00-\x08\x0b\x0c\x0e-\x1f\x7f
- error_reporting(E_ALL);
- // set_time_limit(5);
- echo(str_repeat('-', 80) . PHP_EOL);
- // list($a, $b) = explode(':', '');
- // die;
- if (!function_exists('http_build_url')) {
- function http_build_url($parts) {
- if (!is_array($parts)) {
- return false;
- }
- return (isset($parts['scheme']) ? $parts['scheme'] . '://' : '') .
- (isset($parts['user']) ?
- $parts['user'] .
- (isset($parts['pass']) ? ':' . $parts['pass'] : '') . '@'
- : '') .
- (isset($parts['host']) ? $parts['host']: '') .
- (isset($parts['port']) ? ':' . $parts['port']: '') .
- (isset($parts['path']) ?
- (substr($parts['path'], 0, 1) != '/' ? '/' : '') . $parts['path']
- : '') .
- (isset($parts['query']) ? '?' . $parts['query']: '') .
- (isset($parts['fragment']) ? '#' . $parts['fragment']: '');
- }
- }
- echo(http_build_url([
- 'user' => 'username',
- 'host' => 'example.com',
- 'path' => 'path/to/handler',
- 'query' => 'query',
- 'fragment' => 'hash'
- ]) . PHP_EOL);
- echo(http_build_url([
- 'scheme' => 'https',
- 'user' => 'username',
- 'pass' => 'password',
- 'host' => 'example.com',
- 'path' => 'path/to/handler',
- 'query' => 'query',
- 'fragment' => 'hash'
- ]) . PHP_EOL);
- $u = 'google.com:8080/path/to/handler?#';
- echo('Исходный URL: ' . $u . PHP_EOL);
- $p = parse_url($u);
- echo("Результат парсинга: ");
- print_r($p);
- echo('Результат http_build_url(): ' . http_build_url($p) . PHP_EOL);
- class HTTPConnection {
- const CONNECTION_TIMEOUT = 10;
- const HOSTNAME = 'localhost';
- const READ_SIZE = 4096;
- const TIMEOUT = 10;
- const TRANSPORT = 'tcp';
- const PORT = 80;
- protected $connectionTimeout;
- protected $persistent;
- protected $port;
- protected $socket;
- protected $timeout;
- protected $hostname;
- public function __construct($hostname = null, $port = null, $connection_timeout = null, $timeout = null,
- $persistent = false) {
- $this->hostname = is_null($hostname) ? static::HOSTNAME : $hostname;
- $this->port = is_null($port) ? static::PORT : $port;
- $this->connectionTimeout = is_null($connection_timeout) ? static::CONNECTION_TIMEOUT : $connection_timeout;
- $this->timeout = is_null($timeout) ? static::TIMEOUT : $timeout;
- $this->persistent = $persistent;
- $this->open();
- }
- public function getHostname() {
- return $this->hostname;
- }
- public function getPort() {
- return $this->port;
- }
- public function getConnectionTimeout() {
- return $this->connectionTimeout;
- }
- public function getTimeout() {
- return $this->timeout;
- }
- public function request($path = '/', $method = 'GET', $headers = array(),
- $data = '') {
- $message = "$method $path HTTP/1.1\r\n";
- $message .= 'Host: ' . $this->hostname . ($this->port != static::PORT ? ':' . $this->port : '') . "\r\n";
- foreach ($headers as $name => $value) {
- $message .= "$name: $value\r\n";
- }
- $message .= "\r\n";
- $message .= $data;
- // echo("Request:\n");
- // echo($message . "\n");
- $this->write($message);
- }
- public function write($data) {
- return fwrite($this->socket, $data);
- }
- public function read($size = null) {
- $data = fread($this->socket, is_null($size) ? static::READ_SIZE : $size);
- $this->throwIfTimedOut();
- return $data;
- }
- public function readLine($size = null) {
- $data = fgets($this->socket, is_null($size) ? static::READ_SIZE : $size);
- $this->throwIfTimedOut();
- return $data;
- }
- public function eof() {
- return feof($this->socket);
- }
- public function getStreamMetaData() {
- return stream_get_meta_data($this->socket);
- }
- public function close() {
- fclose($this->socket);
- }
- protected function open() {
- $funcion = ($this->persistent ? 'p' : '') . 'fsockopen';
- $address = static::TRANSPORT . '://' . $this->hostname;
- // echo($address . "\n");
- $this->socket = @call_user_func($funcion, $address, $this->port, $errno, $errstr, $this->connectionTimeout);
- if (!$this->socket) {
- // Функции типа socket_last_error отключены по-умолчанию
- throw new SocketError("Socket error $errno: $errstr");
- }
- // http://forum.sources.ru/index.php?showtopic=88374
- // stream_set_blocking($this->socket, true);
- // timeout на чтение/запись в сокет
- stream_set_timeout($this->socket, $this->timeout);
- }
- protected function throwIfTimedOut() {
- $info = $this->getStreamMetaData();
- if ($info['timed_out']) {
- throw new SocketError('Socket timed out');
- }
- }
- }
- class HTTPSConnection extends HTTPConnection {
- const PORT = 443;
- const TRANSPORT = 'ssl';
- }
- class SocketError extends Exception {}
- class Header {
- protected $name;
- protected $value;
- public function __construct($name, $value) {
- $this->setName($name);
- $this->setValue($value);
- }
- public function setName($name) {
- $this->name = $name;
- }
- public function setValue($value) {
- $this->value = $value;
- }
- public function getName() {
- return $this->name;
- }
- public function getValue() {
- return $this->value;
- }
- public function __toString() {
- return sprintf('%s: %s', $this->name, $this->value);
- }
- }
- /**
- * Класс для работы с http-заголовками.
- *
- * Имена полей регистронезависимы. Значения полей можно получить, обращаясь к
- * объекту как к массиву. К объекту применимы вызовы функций count, isset и
- * unset, а так же возможен по нему обход с помощью цикла foreach.
- */
- class Headers implements ArrayAccess, Countable, IteratorAggregate {
- protected $headers = array();
- public function __construct($headers = array()) {
- $this->update($headers);
- }
- /**
- * Добавляет новый заголовок.
- */
- public function add($name, $value) {
- $this->headers[strtolower($name)] = new Header($name, $value);
- }
- /**
- * Добавляет новый заголовок либо заменяет значение существующего, при этом
- * оригинальное имя сохраняется.
- */
- public function set($name, $value) {
- $lower_name = strtolower($name);
- if (isset($this->headers[$lower_name])) {
- $this->headers[$lower_name]->setValue($value);
- } else {
- $this->headers[$lower_name] = new Header($name, $value);
- }
- // возвращаем установленное значение
- return $value;
- }
- public function update($data) {
- foreach ($data as $name => $value) {
- $this->set($name, $value);
- }
- }
- public function get($name, $default = null) {
- $name = strtolower($name);
- return isset($this->headers[$name]) ? $this->headers[$name]->getValue() : $default;
- }
- /**
- * Возвращает оригинальное имя заголовка.
- */
- public function getOriginalName($name) {
- $name = strtolower($name);
- if (isset($this->headers[$name])) {
- return $this->headers[$name]->getName();
- }
- }
- public function has($name) {
- return isset($this->headers[strtolower($name)]);
- }
- public function remove($name) {
- unset($this->headers[strtolower($name)]);
- }
- public function toArray() {
- $headers = array();
- foreach ($this->headers as $header) {
- $headers[$header->getName()] = $header->getValue();
- }
- return $headers;
- }
- public function __toString() {
- $out = '';
- foreach ($this->headers as $header) {
- $out .= $header->__toString() . "\r\n";
- }
- return $out;
- }
- public function __clone() {
- foreach ($this->headers as &$h) {
- $h = clone $h;
- }
- }
- public static function parse($str) {
- // new self будет создавать экземпляр класса, где был объявлен метод
- $headers = new static;
- if (strlen($str)) {
- // Значения заголовков могут располагаться на нескольких строках, если
- // перед значением следуют CRLF и хотя бы один пробел или таб (LWS):
- //
- // Header: value,
- // value2
- //
- // RFC советует заменять LWS на одиночный пробел:
- //
- // Header: value, value2
- //
- $str = preg_replace('/\r\n[ \t]+/', ' ', $str);
- // всегда возвращает хотя бы один элемент
- $lines = explode("\r\n", $str);
- $limit = count($lines);
- // Пустая строка в конце массива
- if (!strlen($lines[$limit - 1])) {
- --$limit;
- }
- for ($i = 0; $i < $limit; ++$i) {
- $parts = explode(':', $lines[$i], 2);
- if (count($parts) < 2) {
- throw new HeaderParseError('Header without colon');
- }
- list($name, $value) = $parts;
- $value = trim($value);
- if (isset($headers[$name])) {
- // по rfc заголовки с одинаковыми либо различающимися лишь регистром
- // именами могут быть объеденены в один. Их значения должны быть
- // добавлены к значениям первого заголовка и разделены запятыми.
- //
- // Пример:
- //
- // Cache-Control: no-cache
- // cache-control: no-store
- //
- // После нормализации:
- //
- // Cache-Control: no-cache, no-store
- $headers[$name] .= ', ' . $value;
- } else {
- $headers[$name] = $value;
- }
- }
- }
- return $headers;
- }
- public function offsetSet($offset, $value) {
- return $this->set($offset, $value);
- }
- public function offsetExists($offset) {
- return $this->has($offset);
- }
- public function offsetUnset($offset) {
- $this->remove($offset);
- }
- public function offsetGet($offset) {
- return $this->get($offset);
- }
- public function count() {
- return count($this->headers);
- }
- public function getIterator() {
- return new ArrayIterator($this->toArray());
- }
- }
- class HeaderParseError extends Exception {}
- // за основу взят этот код:
- // https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/MediaType.java
- // там неправильная регулярка для токенов
- // http://rfc2.ru/2068.rfc/6#p2.2
- class MediaType {
- const TOKEN = '([!#$%&\'*+-.^`|~\w]+)';
- // quoted-string = ( <"> *(qdtext) <"> )
- // qdtext = <любой TEXT не включающий <">>
- const QUOTED_STRING = '"([^"]*)"';
- protected $mediaType;
- protected $type;
- protected $subtype;
- protected $parameters;
- public function __construct($str) {
- $this->mediaType = $str;
- $this->parse();
- }
- public function __toString() {
- return $this->mediaType;
- }
- public function getMediaType() {
- return $this->mediaType;
- }
- public function getType() {
- return $this->type;
- }
- public function getSubtype() {
- return $this->subtype;
- }
- public function getFullType() {
- return $this->type . '/' . $this->subtype;
- }
- public function getParameter($name, $default = null) {
- $name = strtolower($name);
- if (isset($this->parameters[$name])) {
- return $this->parameters[$name];
- }
- return $default;
- }
- public function getParameters() {
- return $this->parameters;
- }
- protected function parse() {
- // media-type = type "/" subtype *( ";" parameter )
- // type = token
- // subtype = token
- $media_type_re = '/^\s*' . static::TOKEN . '\/' . static::TOKEN . '\s*(?=;|$)/';
- // echo($media_type_re . PHP_EOL);
- // parameter = attribute "=" value
- // attribute = token
- // value = token | quoted-string
- $parameter_re = '/^;\s*' . static::TOKEN . '\s*=\s*(?:' . static::TOKEN . '|' . static::QUOTED_STRING . ')\s*/';
- // echo($parameter_re . PHP_EOL);
- if (!preg_match($media_type_re, $this->mediaType, $matches)) {
- throw new MediaTypeError('Invalid media type');
- }
- $this->type = strtolower($matches[1]);
- $this->subtype = strtolower($matches[2]);
- $offset = strlen($matches[0]);
- $str = $this->mediaType;
- while ($offset < strlen($str)) {
- $str = substr($str, $offset);
- if (!preg_match($parameter_re, $str, $matches)) {
- throw new MediaTypeError('Invalid parameter');
- }
- // print_r($matches);
- $offset = strlen($matches[0]);
- $name = strtolower($matches[1]);
- $value = isset($matches[3]) ? $this->unquote($matches[3]) : $matches[2];
- $this->parameters[$name] = $value;
- }
- }
- protected function unquote($v) {
- // TODO: сделать замену escape-последовательностей
- return $v;
- }
- }
- class MediaTypeError extends Exception {}
- print_r(new MediaType(' TeXt/HtMl ; charset = "UTF-8" ; foo=bar '));
- $headers = new Headers(
- array(
- 'User-Agent' => 'Mozilla/5.0',
- 'Accept-Encoding' => 'gzip, deflate',
- 'Accept' => '*/*',
- )
- );
- //
- // $url = 'https://httpbin.org/gzip';
- // $url = 'https://httpbin.org/deflate'
- $url = 'https://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx';
- $method = 'GET';
- $data = '';
- // scheme, user, pass, host, port, path, query, fragment
- $parsed_url = parse_url($url);
- print_r($parsed_url);
- if ($parsed_url === false || !isset($parsed_url['scheme'])
- || !isset($parsed_url['host'])) {
- throw new Exception('Bad URL');
- }
- $scheme = $parsed_url['scheme'];
- if (!preg_match('/^https?$/i', $scheme)) {
- throw new Exception("Unsupported scheme: $scheme");
- }
- $hostname = $parsed_url['host'];
- $port = isset($parsed_url['port']) ? $parsed_url['port'] : null;
- // классы все равно регистронезависимы
- $class = $scheme . 'connection';
- $connection = new $class($hostname, $port);
- $request_uri = isset($parsed_url['path']) ? $parsed_url['path'] : '/';
- if (isset($parsed_url['query'])) {
- $request_uri .= '?' . $parsed_url['query'];
- }
- $headers = clone $headers;
- // $headers['Connection'] = 'close';
- // if (isset($parsed_url['user'])) {
- // $username = $parsed_url['user'];
- // $password = isset($parsed_url['pass']) ? $parsed_url['pass'] : '';
- // $headers['Authorization'] = 'Basic ' .
- // base64_encode($username . ':' . $password);
- // }
- $content_length = strlen($data);
- if ($content_length) {
- $headers['Content-Length'] = $content_length;
- }
- echo("Request headers:\n");
- print_r($headers->toArray());
- $connection->request($request_uri, $method, $headers, $data);
- $status_line = $connection->readLine();
- // HTTP-version is case-sensitive.
- $re = '/^HTTP\/(\d\.\d) ([1-5]\d\d) (.*)/';
- if (!preg_match($re, $status_line, $matches)) {
- throw new Exception('Invalid status line');
- }
- list( , $http_version, $status_code, $reason_phrase) = $matches;
- $header = '';
- while (!$connection->eof()) {
- $line = $connection->readLine();
- if ($line == "\r\n") {
- break;
- }
- $header .= $line;
- }
- $headers = Headers::parse($header);
- echo("Response headers:\n");
- print_r($headers->toArray());
- // unset($headers['content-length']);
- // $headers['content-length'] = -1;
- $content = '';
- function decode($data, $encoding) {
- if ($encoding == 'gzip') {
- // http://php.net/manual/ru/function.gzinflate.php#77336
- // Про флаги тут:
- // http://www.forensicswiki.org/wiki/Gzip
- if (substr($data, 0, 3) == "\x1f\x8b\x08") {
- $offset = 10;
- $flag = ord(substr($data, 3, 1));
- if ($flag) {
- if ($flag & 2) {
- $offset += 2;
- }
- if ($flag & 4) {
- list($xlen) = unpack('v',substr($data, $offset, 2));
- $offset += 2 + $xlen;
- }
- if ($flag & 8) {
- $offset = strpos($data, "\0", $offset) + 1;
- }
- if ($flag & 16) {
- $offset = strpos($data, "\0", $offset) + 1;
- }
- }
- return @gzinflate(substr($data, $offset, -8));
- }
- } else if ($encoding == 'deflate') {
- // https://github.com/zendframework/zend-http/blob/master/src/Response.php
- $h = unpack('n', substr($data, 0, 2));
- if ($h[1] % 31 == 0) {
- return @gzuncompress($data);
- }
- return @gzinflate($data);
- }
- return false;
- }
- // Если установлен Transfer-Encoding Content-Length игнорируется.
- // If a Content-Length header field (section 14.13) is present, its decimal
- // value in OCTETs represents both the entity-length and the transfer-length.
- // The Content-Length header field MUST NOT be sent if these two lengths are
- // different (i.e., if a Transfer-Encoding header field is present). If a
- // message is received with both a Transfer-Encoding header field and a
- // Content-Length header field, the latter MUST be ignored.
- if (isset($headers['transfer-encoding'])) {
- $encoding = strtolower($headers['transfer-encoding']);
- if ($encoding != 'chunked') {
- throw new Exception("Unsupported transfer-encoding: $encoding");
- }
- while (!$connection->eof()) {
- $chunk = $connection->readLine();
- if (!preg_match('/^[a-fA-F0-9]+(?=;|\r\n)/', $chunk, $matches)) {
- throw new Exception('Invalid chunk');
- }
- $size = hexdec($matches[0]);
- if (!$size) {
- $connection->readLine();
- break;
- }
- $content .= $connection->read($size);
- $connection->readLine();
- }
- } else {
- if (isset($headers['content-length'])) {
- if (!preg_match('/^(0|[1-9]\d*)$/', $headers['content-length'])) {
- throw new Exception('Invalid content-length');
- }
- // читаем количество байт указанных в заголовке
- $length = intval($headers['content-length']);
- if ($length) {
- $content = $connection->read($length);
- }
- } else {
- // Если заголовок Content-Length не установлен, клиент должен читать из
- // сокета, пока не будет разорвано соединение
- while (!$connection->eof()) {
- $content .= $connection->read();
- }
- }
- if (isset($headers['content-encoding'])) {
- $encoding = strtolower($headers['content-encoding']);
- $decoded = decode($content, $encoding);
- if ($decoded === false) {
- throw new Exception("Unable to decode content with content-encoding: $encoding");
- }
- $content = $decoded;
- }
- }
- print_r($connection->getStreamMetaData());
- $connection->close();
- file_put_contents('chunked.jpeg', $content);
- // echo($content);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement