Page MenuHomec4science

PhutilHTTPResponseParser.php
No OneTemporary

File Metadata

Created
Thu, Feb 6, 05:08

PhutilHTTPResponseParser.php

<?php
final class PhutilHTTPResponseParser extends Phobject {
private $followLocationHeaders;
private $responses = array();
private $response;
private $buffer;
private $state = 'headers';
public function setFollowLocationHeaders($follow_location_headers) {
$this->followLocationHeaders = $follow_location_headers;
return $this;
}
public function getFollowLocationHeaders() {
return $this->followLocationHeaders;
}
public function readBytes($bytes) {
if ($this->state == 'discard') {
return $this;
}
$this->buffer .= $bytes;
while (true) {
if ($this->state == 'headers') {
$matches = null;
$ok = preg_match(
"/(\r?\n\r?\n)/",
$this->buffer,
$matches,
PREG_OFFSET_CAPTURE);
if (!$ok) {
break;
}
$headers_len = $matches[1][1];
$boundary_len = strlen($matches[1][0]);
$raw_headers = substr($this->buffer, 0, $headers_len);
$this->buffer = substr($this->buffer, $headers_len + $boundary_len);
$header_lines = phutil_split_lines($raw_headers);
$first_line = array_shift($header_lines);
$response_valid = true;
$matches = null;
$ok = preg_match(
'(^HTTP/\S+\s+(\d+)(?:\s+(.*))?$)i',
$first_line,
$matches);
if ($ok) {
$http_code = (int)$matches[1];
$http_status = phutil_utf8_strtolower($matches[2]);
} else {
$response_valid = false;
}
$header_list = array();
$saw_location = false;
foreach ($header_lines as $header_line) {
$pos = strpos($header_line, ':');
if ($pos === false) {
$response_valid = false;
break;
}
$name = substr($header_line, 0, $pos);
$value = ltrim(substr($header_line, $pos + 1), ' ');
if (phutil_utf8_strtolower($name) == 'location') {
$saw_location = true;
}
$header_list[] = array(
$name,
$value,
);
}
// If the response didn't start with a properly formatted "HTTP/..."
// line, or any of the header lines were not formatted correctly, add
// a malformed response to the response list and discard anything else
// we're given.
if (!$response_valid) {
$malformed = new HTTPFutureParseResponseStatus(
HTTPFutureParseResponseStatus::ERROR_MALFORMED_RESPONSE,
$raw_headers);
$this->newHTTPRepsonse()
->setStatus($malformed);
$this->buffer = '';
$this->state = 'discard';
break;
}
// Otherwise, we have a valid set of response headers.
$response_status = new HTTPFutureHTTPResponseStatus(
$http_code,
null,
$header_list);
$this->newHTTPResponse()
->setStatus($response_status)
->setHeaders($header_list);
$is_https_proxy =
($http_code === 200) &&
($http_status === 'connection established');
if ($http_code === 100) {
// If this is "HTTP/1.1 100 Continue", this is just the server
// telling us that everything is okay. This response won't have
// a body associated with it.
$more_headers = true;
} else if ($is_https_proxy) {
// If this is "HTTP/1.1 200 Connection Established", this is a
// response to a CONNECT request made automatically by cURL to
// an HTTPS proxy. This response won't have a body associated
// with it, and the real body will follow later.
$more_headers = true;
} else if ($saw_location && $this->followLocationHeaders) {
// If we're following location headers and this response had
// a location header, cURL will automatically follow it. This
// response shouldn't have a body.
$more_headers = true;
} else {
$more_headers = false;
}
// If we're expecting more headers, we're going to stay in the
// "headers" state and parse another set of headers. Otherwise,
// we transition to the "body" state and look for a body.
if (!$more_headers) {
$this->state = 'body';
}
continue;
}
if ($this->state == 'body') {
if (strlen($this->buffer)) {
$this->response->appendBody($this->buffer);
$this->buffer = '';
}
break;
}
}
return $this;
}
public function getResponses() {
if ($this->state !== 'body') {
throw new HTTPFutureParseResponseStatus(
HTTPFutureParseResponseStatus::ERROR_MALFORMED_RESPONSE,
$this->buffer);
}
return $this->responses;
}
private function newHTTPResponse() {
$response = new PhutilHTTPResponse();
$this->responses[] = $response;
$this->response = $response;
return $response;
}
}

Event Timeline