Skip to content

Commit

Permalink
Full text() functionality using React
Browse files Browse the repository at this point in the history
  • Loading branch information
g105b committed Nov 2, 2018
1 parent f5781f8 commit e8d5ccd
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 87 deletions.
76 changes: 61 additions & 15 deletions src/BodyResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,38 @@
namespace Gt\Fetch;

use Gt\Curl\JsonDecodeException;
use Gt\Http\Header\ResponseHeaders;
use Gt\Http\Response;
use Gt\Http\StatusCode;
use Psr\Http\Message\UriInterface;
use React\Promise\Deferred;
use React\Promise\Promise;
use stdClass;

/**
* @property-read ResponseHeaders $headers
* @property-read bool $ok
* @property-read bool $redirected
* @property-read int $status
* @property-read string $statusText
* @property-read string $type
* @property-read UriInterface $uri
* @property-read UriInterface $url
*/
class BodyResponse extends Response {
public function arrayBuffer():Promise {
$deferred = new Deferred();
/** @var Deferred */
protected $deferred;

return $deferred->promise();
public function arrayBuffer():TODO {
}

public function blob():Promise {
$deferred = new Deferred();
return $deferred->promise();
public function blob():TODO {
}

public function formData():Promise {
$deferred = new Deferred();
return $deferred->promise();
public function formData():TODO {
}

public function json(int $depth = 512, int $options = 0):Promise {
public function json(int $depth = 512, int $options = 0):StdClass {
$deferred = new Deferred();

$json = json_decode(
Expand All @@ -43,10 +53,46 @@ public function json(int $depth = 512, int $options = 0):Promise {
}

public function text():Promise {
$value = $this->getBody();
var_dump($value->getContents());die();
$deferred = new Deferred();
$deferred->resolve($value);
return $deferred->promise();
$this->deferred = new Deferred();
$promise = $this->deferred->promise();
return $promise;
}

public function completeResponse():void {
$position = $this->stream->tell();
$this->stream->rewind();
$contents = $this->stream->getContents();
$this->stream->seek($position);
$this->deferred->resolve($contents);
}

public function __get(string $name) {
switch($name) {
case "headers":
return $this->getHeaders();
break;

case "ok":
return ($this->statusCode >= 200
&& $this->statusCode < 300);

case "redirected":
break;

case "status":
return $this->getStatusCode();

case "statusText":
return StatusCode::REASON_PHRASE[$this->status];

case "type":
// TODO: What exactly is this property for?
break;

case "uri":
case "url":
// TODO: How do we get the URI from the request?
break;
}
}
}
1 change: 0 additions & 1 deletion src/GlobalFetch.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?php

namespace Gt\Fetch;

use React\Promise\PromiseInterface;
Expand Down
16 changes: 7 additions & 9 deletions src/Http.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@
use Gt\Http\Uri;
use Http\Client\HttpClient;
use Http\Client\HttpAsyncClient;
use Http\Promise\Promise;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
use React\EventLoop\StreamSelectLoop;
use React\EventLoop\Factory;
use React\EventLoop\LoopInterface;
use React\Promise\Deferred;
use React\EventLoop\Factory as EventLoopFactory;
use React\Promise\PromiseInterface;

class Http extends GlobalFetchHelper implements HttpClient, HttpAsyncClient {
const REFERRER = "PhpGt/Fetch";

/** @var float */
protected $interval;
/** @var StreamSelectLoop */
/** @var LoopInterface */
protected $loop;
/** @var RequestResolver */
protected $requestResolver;
Expand All @@ -37,7 +36,7 @@ public function __construct(
$this->options = $options + $this->options;
$this->interval = $interval;

$this->loop = EventLoopFactory::create();
$this->loop = Factory::create();
$this->requestResolver = new RequestResolver($this->loop);
}

Expand Down Expand Up @@ -80,6 +79,7 @@ public function fetch($input, array $init = []):PromiseInterface {
$init,
$deferred
);

return $promise;
}

Expand All @@ -103,13 +103,12 @@ public function ensureUriInterface($uri):UriInterface {
* Executes all promises in parallel, returning only when all promises
* have been fulfilled.
*/
public function wait() {
$this->timer = $this->loop->addPeriodicTimer(
public function wait():void {
$timer = $this->loop->addPeriodicTimer(
$this->interval,
[$this->requestResolver, "tick"]
);
$this->loop->run();
// $this->requestResolver->temporaryThing();
}

/**
Expand All @@ -120,7 +119,6 @@ public function all():PromiseInterface {
$deferred = new Deferred();
$promise = $deferred->promise();
$this->wait();

$deferred->resolve(true);
return $promise;
}
Expand Down
151 changes: 89 additions & 62 deletions src/RequestResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,32 @@
use Gt\Curl\CurlMulti;
use Gt\Curl\CurlMultiInterface;
use Gt\Http\Header\Parser;
use Gt\Http\Response;
use Psr\Http\Message\UriInterface;
use React\EventLoop\LoopInterface;
use React\Promise\Deferred;
use React\Promise\Promise;

class RequestResolver {
/** @var LoopInterface */
protected $loop;
/** @var CurlMultiInterface */
protected $curlMulti;

/** @var CurlMultiInterface[] */
protected $curlMultiList;
/** @var CurlInterface[] */
protected $curlList;
/** @var Deferred[] */
/** @var Promise[] */
protected $deferredList;
/** @var Response[] */
/** @var BodyResponse[] */
protected $responseList;
/** @var string[] */
protected $headersList;
protected $headerList;

public function __construct(
LoopInterface $loop,
string $curlMultiClass = CurlMulti::class
) {
public function __construct(LoopInterface $loop) {
$this->loop = $loop;
$this->curlList = [];
$this->curlMultiList = [];
$this->deferredList = [];
$this->headersList = [];
$this->curlMulti = new $curlMultiClass();
$this->headerList = [];
}

public function add(
Expand All @@ -49,78 +46,108 @@ public function add(
}

$curl->setOpt(CURLOPT_RETURNTRANSFER, false);
$curl->setOpt(CURLOPT_HEADER, true);
$curl->setOpt(CURLOPT_HEADER, false);
$curl->setOpt(CURLOPT_HEADERFUNCTION, [$this, "writeHeader"]);
$curl->setOpt(CURLOPT_WRITEFUNCTION, [$this, "write"]);

$this->curlMulti->add($curl);
$curlMulti = new CurlMulti();
$curlMulti->add($curl);

$this->curlList []= $curl;
$this->curlMultiList []= $curlMulti;
$this->deferredList []= $deferred;
$this->headersList []= "";
$this->responseList []= new Response();
$this->responseList []= new BodyResponse();
$this->headerList []= "";
}

public function write($chIncoming, $content):int {
$match = false;
foreach($this->curlList as $i => $curl) {
if($chIncoming === $curl->getHandle()) {
$match = true;
break;
}
}
public function write($ch, $content):int {
$i = $this->getIndex($ch);

if(!$match) {
// TODO: Throw exception.
die("NO CURL HANDLE!!!!");
$body = $this->responseList[$i]->getBody();
$body->write($content);

if($this->deferredList[$i]) {
$this->deferredList[$i]->resolve($this->responseList[$i]);
$this->deferredList[$i] = null;
}

if(!strstr($this->headersList[$i], "\r\n\r\n")) {
echo "1";
if(strstr($content, "\r\n\r\n")) {
$parser = new Parser($this->headersList[$i]);
$this->responseList[$i] = $this->responseList[$i]->withProtocolVersion(
$parser->getProtocolVersion()
);
$this->responseList[$i] = $this->responseList[$i]->withStatus(
$parser->getStatusCode()
);
foreach($parser->getKeyValues() as $key => $value) {
$this->responseList[$i] = $this->responseList[$i]->withAddedHeader(
$key,
$value
);
return strlen($content);
}

public function writeHeader($ch, string $rawHeader) {
$i = $this->getIndex($ch);

$headerLine = trim($rawHeader);

if($headerLine === "") {
$parser = new Parser($this->headerList[$i]);
$this->responseList[$i] = $this->responseList[$i]->withProtocolVersion(
$parser->getProtocolVersion()
);
$this->responseList[$i] = $this->responseList[$i]->withStatus(
$parser->getStatusCode()
);
foreach($parser->getKeyValues() as $key => $value) {
if(empty($key)) {
continue;
}
$this->responseList[$i] = $this->responseList[$i]->withAddedHeader(
$key,
$value
);
}

$this->headersList[$i] .= $content;
}
else {
echo "2";
$body = $this->responseList[$i]->getBody();
$body->write($content);
$this->deferredList[$i]->resolve($body);
}

echo ".";

return strlen($content);
$this->headerList[$i] .= $rawHeader;
return strlen($rawHeader);
}

public function tick():void {
$active = 0;
$totalActive = 0;

foreach($this->curlMultiList as $i => $curlMulti) {
$active = 0;

do {
$status = $curlMulti->exec($active);
}
while($status === CURLM_CALL_MULTI_PERFORM);

if($status !== CURLM_OK) {
// TODO: Throw exception.
die("ERROR!");
}

$totalActive += $active;

if($active === 0) {
// TODO: Resolve body's complete promise.
$this->responseList[$i]->completeResponse();
}

do {
$status = $this->curlMulti->exec($active);
}
while($status === CURLM_CALL_MULTI_PERFORM);

if($status !== CURLM_OK) {
// TODO: Throw exception.
die("ERROR!");
}

if($active === 0) {
if($totalActive === 0) {
$this->loop->stop();
return;
}
}

protected function getIndex($chIncoming):int {
$match = false;
foreach($this->curlList as $i => $curl) {
if($chIncoming === $curl->getHandle()) {
$match = true;
break;
}
}

if(!$match) {
// TODO: Throw exception.
die("NO CURL HANDLE!!!!");
}

return $i;
}
}

0 comments on commit e8d5ccd

Please sign in to comment.