|
<?php |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SimplePie_HTTP_Parser |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
public $http_version = 0.0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
public $status_code = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
public $reason = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
public $headers = array(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
public $body = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected $state = 'http_version'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected $data = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected $data_length = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected $position = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected $name = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected $value = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
public function __construct($data) |
|
{ |
|
$this->data = $data; |
|
$this->data_length = strlen($this->data); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
public function parse() |
|
{ |
|
while ($this->state && $this->state !== 'emit' && $this->has_data()) |
|
{ |
|
$state = $this->state; |
|
$this->$state(); |
|
} |
|
$this->data = ''; |
|
if ($this->state === 'emit' || $this->state === 'body') |
|
{ |
|
return true; |
|
} |
|
|
|
$this->http_version = ''; |
|
$this->status_code = ''; |
|
$this->reason = ''; |
|
$this->headers = array(); |
|
$this->body = ''; |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected function has_data() |
|
{ |
|
return (bool) ($this->position < $this->data_length); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected function is_linear_whitespace() |
|
{ |
|
return (bool) ($this->data[$this->position] === "\x09" |
|
|| $this->data[$this->position] === "\x20" |
|
|| ($this->data[$this->position] === "\x0A" |
|
&& isset($this->data[$this->position + 1]) |
|
&& ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); |
|
} |
|
|
|
|
|
|
|
|
|
protected function http_version() |
|
{ |
|
if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') |
|
{ |
|
$len = strspn($this->data, '0123456789.', 5); |
|
$this->http_version = substr($this->data, 5, $len); |
|
$this->position += 5 + $len; |
|
if (substr_count($this->http_version, '.') <= 1) |
|
{ |
|
$this->http_version = (float) $this->http_version; |
|
$this->position += strspn($this->data, "\x09\x20", $this->position); |
|
$this->state = 'status'; |
|
} |
|
else |
|
{ |
|
$this->state = false; |
|
} |
|
} |
|
else |
|
{ |
|
$this->state = false; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
protected function status() |
|
{ |
|
if ($len = strspn($this->data, '0123456789', $this->position)) |
|
{ |
|
$this->status_code = (int) substr($this->data, $this->position, $len); |
|
$this->position += $len; |
|
$this->state = 'reason'; |
|
} |
|
else |
|
{ |
|
$this->state = false; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
protected function reason() |
|
{ |
|
$len = strcspn($this->data, "\x0A", $this->position); |
|
$this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); |
|
$this->position += $len + 1; |
|
$this->state = 'new_line'; |
|
} |
|
|
|
|
|
|
|
|
|
protected function new_line() |
|
{ |
|
$this->value = trim($this->value, "\x0D\x20"); |
|
if ($this->name !== '' && $this->value !== '') |
|
{ |
|
$this->name = strtolower($this->name); |
|
|
|
if (isset($this->headers[$this->name]) && $this->name !== 'content-type') |
|
{ |
|
$this->headers[$this->name] .= ', ' . $this->value; |
|
} |
|
else |
|
{ |
|
$this->headers[$this->name] = $this->value; |
|
} |
|
} |
|
$this->name = ''; |
|
$this->value = ''; |
|
if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") |
|
{ |
|
$this->position += 2; |
|
$this->state = 'body'; |
|
} |
|
elseif ($this->data[$this->position] === "\x0A") |
|
{ |
|
$this->position++; |
|
$this->state = 'body'; |
|
} |
|
else |
|
{ |
|
$this->state = 'name'; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
protected function name() |
|
{ |
|
$len = strcspn($this->data, "\x0A:", $this->position); |
|
if (isset($this->data[$this->position + $len])) |
|
{ |
|
if ($this->data[$this->position + $len] === "\x0A") |
|
{ |
|
$this->position += $len; |
|
$this->state = 'new_line'; |
|
} |
|
else |
|
{ |
|
$this->name = substr($this->data, $this->position, $len); |
|
$this->position += $len + 1; |
|
$this->state = 'value'; |
|
} |
|
} |
|
else |
|
{ |
|
$this->state = false; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
protected function linear_whitespace() |
|
{ |
|
do |
|
{ |
|
if (substr($this->data, $this->position, 2) === "\x0D\x0A") |
|
{ |
|
$this->position += 2; |
|
} |
|
elseif ($this->data[$this->position] === "\x0A") |
|
{ |
|
$this->position++; |
|
} |
|
$this->position += strspn($this->data, "\x09\x20", $this->position); |
|
} while ($this->has_data() && $this->is_linear_whitespace()); |
|
$this->value .= "\x20"; |
|
} |
|
|
|
|
|
|
|
|
|
protected function value() |
|
{ |
|
if ($this->is_linear_whitespace()) |
|
{ |
|
$this->linear_whitespace(); |
|
} |
|
else |
|
{ |
|
switch ($this->data[$this->position]) |
|
{ |
|
case '"': |
|
|
|
|
|
if (strtolower($this->name) === 'etag') |
|
{ |
|
$this->value .= '"'; |
|
$this->position++; |
|
$this->state = 'value_char'; |
|
break; |
|
} |
|
$this->position++; |
|
$this->state = 'quote'; |
|
break; |
|
|
|
case "\x0A": |
|
$this->position++; |
|
$this->state = 'new_line'; |
|
break; |
|
|
|
default: |
|
$this->state = 'value_char'; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
protected function value_char() |
|
{ |
|
$len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); |
|
$this->value .= substr($this->data, $this->position, $len); |
|
$this->position += $len; |
|
$this->state = 'value'; |
|
} |
|
|
|
|
|
|
|
|
|
protected function quote() |
|
{ |
|
if ($this->is_linear_whitespace()) |
|
{ |
|
$this->linear_whitespace(); |
|
} |
|
else |
|
{ |
|
switch ($this->data[$this->position]) |
|
{ |
|
case '"': |
|
$this->position++; |
|
$this->state = 'value'; |
|
break; |
|
|
|
case "\x0A": |
|
$this->position++; |
|
$this->state = 'new_line'; |
|
break; |
|
|
|
case '\\': |
|
$this->position++; |
|
$this->state = 'quote_escaped'; |
|
break; |
|
|
|
default: |
|
$this->state = 'quote_char'; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
protected function quote_char() |
|
{ |
|
$len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); |
|
$this->value .= substr($this->data, $this->position, $len); |
|
$this->position += $len; |
|
$this->state = 'value'; |
|
} |
|
|
|
|
|
|
|
|
|
protected function quote_escaped() |
|
{ |
|
$this->value .= $this->data[$this->position]; |
|
$this->position++; |
|
$this->state = 'quote'; |
|
} |
|
|
|
|
|
|
|
|
|
protected function body() |
|
{ |
|
$this->body = substr($this->data, $this->position); |
|
if (!empty($this->headers['transfer-encoding'])) |
|
{ |
|
unset($this->headers['transfer-encoding']); |
|
$this->state = 'chunked'; |
|
} |
|
else |
|
{ |
|
$this->state = 'emit'; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
protected function chunked() |
|
{ |
|
if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body))) |
|
{ |
|
$this->state = 'emit'; |
|
return; |
|
} |
|
|
|
$decoded = ''; |
|
$encoded = $this->body; |
|
|
|
while (true) |
|
{ |
|
$is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches ); |
|
if (!$is_chunked) |
|
{ |
|
|
|
$this->state = 'emit'; |
|
return; |
|
} |
|
|
|
$length = hexdec(trim($matches[1])); |
|
if ($length === 0) |
|
{ |
|
|
|
$this->state = 'emit'; |
|
$this->body = $decoded; |
|
return; |
|
} |
|
|
|
$chunk_length = strlen($matches[0]); |
|
$decoded .= $part = substr($encoded, $chunk_length, $length); |
|
$encoded = substr($encoded, $chunk_length + $length + 2); |
|
|
|
if (trim($encoded) === '0' || empty($encoded)) |
|
{ |
|
$this->state = 'emit'; |
|
$this->body = $decoded; |
|
return; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static public function prepareHeaders($headers, $count = 1) |
|
{ |
|
$data = explode("\r\n\r\n", $headers, $count); |
|
$data = array_pop($data); |
|
if (false !== stripos($data, "HTTP/1.0 200 Connection established\r\n")) { |
|
$exploded = explode("\r\n\r\n", $data, 2); |
|
$data = end($exploded); |
|
} |
|
if (false !== stripos($data, "HTTP/1.1 200 Connection established\r\n")) { |
|
$exploded = explode("\r\n\r\n", $data, 2); |
|
$data = end($exploded); |
|
} |
|
return $data; |
|
} |
|
} |
|
|