public function onRead()
{
if ($this->state === self::STATE_BODY) {
goto body;
}
if ($this->reqType === null) {
if ($this->requests->isEmpty()) {
$this->finish();
return;
}
$this->reqType = $this->requests->shift();
}
while (($line = $this->readLine()) !== null) {
if ($line !== '') {
if ($this->rawHeaders !== null) {
$this->rawHeaders[] = $line;
}
} else {
if (isset($this->headers['HTTP_CONTENT_LENGTH'])) {
$this->contentLength = (int) $this->headers['HTTP_CONTENT_LENGTH'];
} else {
$this->contentLength = -1;
}
if (isset($this->headers['HTTP_TRANSFER_ENCODING'])) {
$e = explode(', ', strtolower($this->headers['HTTP_TRANSFER_ENCODING']));
$this->chunked = in_array('chunked', $e, true);
} else {
$this->chunked = false;
}
if (isset($this->headers['HTTP_CONNECTION'])) {
$e = explode(', ', strtolower($this->headers['HTTP_CONNECTION']));
$this->keepalive = in_array('keep-alive', $e, true);
}
if (isset($this->headers['HTTP_CONTENT_TYPE'])) {
parse_str('type=' . strtr($this->headers['HTTP_CONTENT_TYPE'], [';' => '&', ' ' => '']), $p);
$this->contentType = $p['type'];
if (isset($p['charset'])) {
$this->charset = strtolower($p['charset']);
}
}
if ($this->contentLength === -1 && !$this->chunked && !$this->keepalive) {
$this->eofTerminated = true;
}
if ($this->reqType === 'HEAD') {
$this->requestFinished();
} else {
$this->state = self::STATE_BODY;
}
break;
}
if ($this->state === self::STATE_ROOT) {
$this->headers['STATUS'] = $line;
$e = explode(' ', $this->headers['STATUS']);
$this->responseCode = isset($e[1]) ? (int) $e[1] : 0;
$this->state = self::STATE_HEADERS;
} elseif ($this->state === self::STATE_HEADERS) {
$e = explode(': ', $line);
if (isset($e[1])) {
$k = 'HTTP_' . strtoupper(strtr($e[0], Generic::$htr));
if ($k === 'HTTP_SET_COOKIE') {
parse_str(strtr($e[1], [';' => '&', ' ' => '']), $p);
if (sizeof($p)) {
$this->cookie[$k = key($p)] =& $p;
$p['value'] = $p[$k];
unset($p[$k], $p);
}
}
if (isset($this->headers[$k])) {
if (is_array($this->headers[$k])) {
$this->headers[$k][] = $e[1];
} else {
$this->headers[$k] = [$this->headers[$k], $e[1]];
}
} else {
$this->headers[$k] = $e[1];
}
}
}
}
if ($this->state !== self::STATE_BODY) {
return;
// not enough data yet
}
body:
if ($this->eofTerminated) {
$body = $this->readUnlimited();
if ($this->chunkcb) {
$func = $this->chunkcb;
$func($body);
}
$this->body .= $body;
return;
}
if ($this->chunked) {
chunk:
if ($this->curChunkSize === null) {
// outside of chunk
$l = $this->readLine();
if ($l === '') {
// skip empty line
goto chunk;
}
if ($l === null) {
return;
// not enough data yet
}
if (!ctype_xdigit($l)) {
$this->protocolError = __LINE__;
$this->finish();
// protocol error
return;
}
$this->curChunkSize = hexdec($l);
}
if ($this->curChunkSize !== null) {
if ($this->curChunkSize === 0) {
if ($this->readLine() === '') {
$this->requestFinished();
return;
} else {
// protocol error
$this->protocolError = __LINE__;
$this->finish();
return;
}
}
$n = $this->curChunkSize - mb_orig_strlen($this->curChunk);
$this->curChunk .= $this->read($n);
if ($this->curChunkSize <= mb_orig_strlen($this->curChunk)) {
if ($this->chunkcb) {
$func = $this->chunkcb;
$func($this->curChunk);
}
$this->body .= $this->curChunk;
$this->curChunkSize = null;
$this->curChunk = '';
goto chunk;
}
}
} else {
$body = $this->read($this->contentLength - mb_orig_strlen($this->body));
if ($this->chunkcb) {
$func = $this->chunkcb;
$func($body);
}
$this->body .= $body;
if ($this->contentLength !== -1 && mb_orig_strlen($this->body) >= $this->contentLength) {
$this->requestFinished();
}
}
}