public function parse($data = null)
{
if ($data !== null) {
$this->buffer .= $data;
}
if ($this->buffer == '') {
goto more_data_needed;
}
switch ($this->state) {
case self::AWAITING_HEADERS:
goto awaiting_headers;
case self::BODY_IDENTITY:
goto body_identity;
case self::BODY_IDENTITY_EOF:
goto body_identity_eof;
case self::BODY_CHUNKS:
goto body_chunks;
case self::TRAILERS_START:
goto trailers_start;
case self::TRAILERS:
goto trailers;
}
awaiting_headers:
if (!($startLineAndHeaders = $this->shiftHeadersFromMessageBuffer())) {
goto more_data_needed;
} else {
goto start_line;
}
start_line:
$startLineEndPos = strpos($startLineAndHeaders, "\n");
$startLine = substr($startLineAndHeaders, 0, $startLineEndPos);
$rawHeaders = substr($startLineAndHeaders, $startLineEndPos + 1);
$this->traceBuffer = $startLineAndHeaders;
if ($this->mode === self::MODE_REQUEST) {
goto request_line_and_headers;
} else {
goto status_line_and_headers;
}
request_line_and_headers:
$parts = explode(' ', trim($startLine));
if (isset($parts[0]) && ($method = trim($parts[0]))) {
$this->requestMethod = $method;
} else {
throw new ParseException($this->getParsedMessageArray(), $msg = 'Invalid request line', $code = 400, $previousException = null);
}
if (isset($parts[1]) && ($uri = trim($parts[1]))) {
$this->requestUri = $uri;
} else {
throw new ParseException($this->getParsedMessageArray(), $msg = 'Invalid request line', $code = 400, $previousException = null);
}
if (isset($parts[2]) && ($protocol = str_ireplace('HTTP/', '', trim($parts[2])))) {
$this->protocol = $protocol;
} else {
throw new ParseException($this->getParsedMessageArray(), $msg = 'Invalid request line', $code = 400, $previousException = null);
}
if (!($protocol === '1.0' || '1.1' === $protocol)) {
throw new ParseException($this->getParsedMessageArray(), $msg = 'Protocol not supported: {$protocol}', $code = 505, $previousException = null);
}
if ($rawHeaders) {
$this->headers = $this->parseHeadersFromRaw($rawHeaders);
}
goto transition_from_request_headers_to_body;
status_line_and_headers:
if (preg_match(self::STATUS_LINE_PATTERN, $startLine, $m)) {
$this->protocol = $m['protocol'];
$this->responseCode = $m['status'];
$this->responseReason = trim($m['reason']);
} else {
throw new ParseException($this->getParsedMessageArray(), $msg = 'Invalid status line', $code = 400, $previousException = null);
}
if ($rawHeaders) {
$this->headers = $this->parseHeadersFromRaw($rawHeaders);
}
goto transition_from_response_headers_to_body;
transition_from_request_headers_to_body:
if ($this->requestMethod == 'HEAD' || $this->requestMethod == 'TRACE' || $this->requestMethod == 'OPTIONS') {
goto complete;
} elseif ($this->parseFlowHeaders['TRANSFER-ENCODING']) {
$this->state = self::BODY_CHUNKS;
goto before_body;
} elseif ($this->parseFlowHeaders['CONTENT-LENGTH']) {
$this->remainingBodyBytes = $this->parseFlowHeaders['CONTENT-LENGTH'];
$this->state = self::BODY_IDENTITY;
goto before_body;
} else {
goto complete;
}
transition_from_response_headers_to_body:
$requestMethod = array_shift($this->responseMethodMatch);
if ($this->responseCode == 204 || $this->responseCode == 304 || $this->responseCode < 200 || $requestMethod === 'HEAD' || $requestMethod === 'CONNECT') {
goto complete;
} elseif ($this->parseFlowHeaders['TRANSFER-ENCODING']) {
$this->state = self::BODY_CHUNKS;
goto before_body;
} elseif ($this->parseFlowHeaders['CONTENT-LENGTH'] === null) {
$this->state = self::BODY_IDENTITY_EOF;
goto before_body;
} elseif ($this->parseFlowHeaders['CONTENT-LENGTH'] > 0) {
$this->remainingBodyBytes = $this->parseFlowHeaders['CONTENT-LENGTH'];
$this->state = self::BODY_IDENTITY;
goto before_body;
} else {
goto complete;
}
before_body:
if ($this->remainingBodyBytes === 0) {
goto complete;
}
$uri = 'php://temp/maxmemory:' . $this->bodySwapSize;
$this->body = fopen($uri, 'r+');
if ($this->returnBeforeEntity) {
$parsedMsgArr = $this->getParsedMessageArray();
$parsedMsgArr['headersOnly'] = true;
return $parsedMsgArr;
}
switch ($this->state) {
case self::BODY_IDENTITY:
goto body_identity;
case self::BODY_IDENTITY_EOF:
goto body_identity_eof;
case self::BODY_CHUNKS:
goto body_chunks;
default:
throw new \RuntimeException('Unexpected parse state encountered');
}
body_identity:
$bufferDataSize = strlen($this->buffer);
if ($bufferDataSize < $this->remainingBodyBytes) {
$this->addToBody($this->buffer);
$this->buffer = null;
$this->remainingBodyBytes -= $bufferDataSize;
goto more_data_needed;
} elseif ($bufferDataSize == $this->remainingBodyBytes) {
$this->addToBody($this->buffer);
$this->buffer = null;
$this->remainingBodyBytes = 0;
goto complete;
} else {
$bodyData = substr($this->buffer, 0, $this->remainingBodyBytes);
$this->addToBody($bodyData);
$this->buffer = substr($this->buffer, $this->remainingBodyBytes);
$this->remainingBodyBytes = 0;
goto complete;
}
body_identity_eof:
$this->addToBody($this->buffer);
$this->buffer = '';
goto more_data_needed;
body_chunks:
if ($this->dechunk()) {
$this->state = self::TRAILERS_START;
goto trailers_start;
} else {
goto more_data_needed;
}
trailers_start:
$firstTwoBytes = substr($this->buffer, 0, 2);
if ($firstTwoBytes == "" || $firstTwoBytes === "\r") {
goto more_data_needed;
} elseif ($firstTwoBytes === "\r\n") {
$this->buffer = substr($this->buffer, 2);
goto complete;
} else {
$this->state = self::TRAILERS;
goto trailers;
}
trailers:
if ($trailers = $this->shiftHeadersFromMessageBuffer()) {
$this->parseTrailers($trailers);
goto complete;
} else {
goto more_data_needed;
}
complete:
$parsedMsgArr = $this->getParsedMessageArray();
$parsedMsgArr['headersOnly'] = false;
$this->state = self::AWAITING_HEADERS;
$this->traceBuffer = null;
$this->headers = [];
$this->body = null;
$this->bodyBytesConsumed = 0;
$this->remainingBodyBytes = null;
$this->chunkLenRemaining = null;
$this->protocol = null;
$this->requestUri = null;
$this->requestMethod = null;
$this->responseCode = null;
$this->responseReason = null;
$this->parseFlowHeaders = ['TRANSFER-ENCODING' => null, 'CONTENT-LENGTH' => null];
return $parsedMsgArr;
more_data_needed:
return null;
}