Вопрос по php, http – Как легко декодировать кодированную HTTP-строку при выполнении необработанного HTTP-запроса?

3

Я хочу сделать HTTP-запрос без зависимости от cURL иallow_url_fopen = 1 открыв сокет-соединение и отправив необработанный HTTP-запрос:

/**
 * Make HTTP GET request
 *
 * @param   string   the URL
 * @param   int      will be filled with HTTP response status code
 * @param   string   will be filled with HTTP response header
 * @return  string   HTTP response body
 */
function http_get_request($url, &$http_code = '', &$res_head = '') 
{
  $scheme = $host = $user = $pass = $query = $fragment = '';
  $path = '/';
  $port = substr($url, 0, 5) == 'https' ? 443 : 80;

  extract(parse_url($url)); 

  $path .= ($query ? "?$query" : '').($fragment ? "#$fragment" : '');

  $head = "GET $path HTTP/1.1\r\n"
        . "Host: $host\r\n"
        . "Authorization: Basic ".base64_encode("$user:$pass")."\r\n"
        . "Connection: close\r\n\r\n";

  $fp = fsockopen($scheme == 'https' ? "ssl://$host" : $host, $port) or 
    die('Cannot connect!');

  fputs($fp, $head);
  while(!feof($fp)) {
    $res .= fgets($fp, 4096);
  }
  fclose($fp);

  list($res_head, $res_body) = explode("\r\n\r\n", $res, 2);
  list(, $http_code, ) = explode(' ', $res_head, 3);

  return $res_body;
}

Функция работает нормально, но, поскольку я использую HTTP / 1.1, тело ответа обычно возвращается вБлочная-закодирован строка. Например (из Википедии):

25
This is the data in the first chunk

1C
and this is the second one

3
con
8
sequence
0

Я не хочу использоватьhttp_chunked_decode() так как он имеет зависимость PECL, и я хочу очень переносимый код.

Как легко декодировать закодированную строку HTTP, чтобы моя функция могла вернуть исходный HTML? Я также должен убедиться, что длина декодированной строки совпадает сContent-Length: заголовок.

Любая помощь будет оценена. Благодарю.

Код в этом ответе не такой большой, он просто хорошо прокомментирован :) Ja͢ck
This question немного похоже на мой вопрос. Но выбранный ответtoo bloated, Я работаю над более простым решением, используя там код. Я надеюсь, что этот вопрос не будет закрыт :) flowfree
возможный дубликатHow to handle chunked encoding request properly? Ja͢ck

Ваш Ответ

3   ответа
0

Эту функцию используют в Wordpress.

function decode_chunked($data) {
    if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
        return $data;
    }



    $decoded = '';
    $encoded = $data;

    while (true) {
        $is_chunked = (bool) preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', $encoded, $matches);
        if (!$is_chunked) {
            // Looks like it's not chunked after all
            return $data;
        }

        $length = hexdec(trim($matches[1]));
        if ($length === 0) {
            // Ignore trailer headers
            return $decoded;
        }

        $chunk_length = strlen($matches[0]);
        $decoded .= substr($encoded, $chunk_length, $length);
        $encoded = substr($encoded, $chunk_length + $length + 2);

        if (trim($encoded) === '0' || empty($encoded)) {
            return $decoded;
        }
    }

    // We'll never actually get down here
    // @codeCoverageIgnoreStart
}
9

Поскольку функция возвращает заголовок ответа HTTP, вам следует проверить,'Transfer-Encoding' является'chunked' затем декодируйте кодированную строку. В псевдокоде:

CALL parse_http_header
IF 'Transfer-Encoding' IS 'chunked'
  CALL decode_chunked

Parsing HTTP response header:

Ниже приведена функция для анализа заголовка HTTP-ответа в ассоциативном массиве.

function parse_http_header($str) 
{
  $lines = explode("\r\n", $str);
  $head  = array(array_shift($lines));
  foreach ($lines as $line) {
    list($key, $val) = explode(':', $line, 2);
    if ($key == 'Set-Cookie') {
      $head['Set-Cookie'][] = trim($val);
    } else {
      $head[$key] = trim($val);
    }
  }
  return $head;
}

Функция вернет массив следующим образом:

Array
(
    [0] => HTTP/1.1 200 OK
    [Expires] => Tue, 31 Mar 1981 05:00:00 GMT
    [Content-Type] => text/html; charset=utf-8
    [Transfer-Encoding] => chunked
    [Set-Cookie] => Array
        (
            [0] => k=10.34; path=/; expires=Sat, 09-Jun-12 01:58:23 GMT; domain=.example.com
            [1] => guest_id=v1%3A13; domain=.example.com; path=/; expires=Mon, 02-Jun-2014 13:58:23 GMT
        )
    [Content-Length] => 43560
)

Обратите внимание, какSet-Cookie заголовки разбираются в массив. Вам необходимо проанализировать файлы cookie позже, чтобы связать URL с файлами cookie, которые необходимо отправить.

Decode the chunked-encoded string

Приведенная ниже функция принимает в качестве аргумента строку с закодированным фрагментом и возвращает расшифрованная строка.

function decode_chunked($str) {
  for ($res = ''; !empty($str); $str = trim($str)) {
    $pos = strpos($str, "\r\n");
    $len = hexdec(substr($str, 0, $pos));
    $res.= substr($str, $pos + 2, $len);
    $str = substr($str, $pos + 2 + $len);
  }
  return $res;
}

// Given the string in the question, the function above will returns:
//
// This is the data in the first chunk
// and this is the second one
// consequence
удалить всю мою строку (моя строка JSON)
Спасибо - это было быстро :)
@rdlowrey Я отредактировал свой ответ. Спасибо за ваше исправление. flowfree
2

Я не знаю, оптимально ли это для вас, что вам нужно делать, но, если вы укажетеHTTP/1.0 вместоHTTP/1.1, вы не получите частичный ответ.

Да. Но в HTTP / 1.1 есть несколько интересных функций, которые я хочу реализовать в своей функции. flowfree

Похожие вопросы