Skip to content

Commit addda43

Browse files
committed
Improve Eventsource interface + dunglas comments
1 parent b88c4ca commit addda43

File tree

8 files changed

+261
-206
lines changed

8 files changed

+261
-206
lines changed

src/Symfony/Component/HttpClient/Chunk/MessageChunk.php

-50
This file was deleted.

src/Symfony/Component/HttpClient/EventSourceHttpClient.php

-97
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
namespace Symfony\Component\HttpClient\ServerSentEvents;
4+
5+
use Symfony\Component\HttpClient\HttpClient;
6+
use Symfony\Contracts\HttpClient\HttpClientInterface;
7+
8+
final class EventSource implements EventSourceInterface
9+
{
10+
private $url;
11+
private $client;
12+
private $reconnectionTime = 5;
13+
private $lastEventId = null;
14+
private $response = null;
15+
16+
const CONNECTING = 0;
17+
const OPEN = 1;
18+
const CLOSED = 2;
19+
20+
public $readyState = self::CLOSED;
21+
22+
public function __construct(string $url, array $defaultOptions = [], HttpClientInterface $client = null)
23+
{
24+
$this->url = $url;
25+
$this->client = $client ?: HttpClient::create();
26+
$this->connect();
27+
}
28+
29+
private function connect()
30+
{
31+
$this->readyState = self::CONNECTING;
32+
$response = $this->client->request('GET', 'http://localhost:8080/events', ['headers' => [
33+
'Accept' => 'text/event-stream',
34+
'Cache-Control' => 'no-store',
35+
]]);
36+
37+
if (200 !== $response->getStatusCode()) {
38+
throw new \Exception('Could not request ');
39+
}
40+
41+
$this->readyState = self::OPEN;
42+
43+
$this->response = $response;
44+
}
45+
46+
private function loop()
47+
{
48+
return $this->client->stream($this->response, $this->reconnectionTime);
49+
}
50+
51+
public function getHttpClient(): HttpClientInterface
52+
{
53+
return $this->client;
54+
}
55+
56+
/**
57+
* {@inheritdoc}
58+
*/
59+
public function getMessages(): \Iterator
60+
{
61+
$buffer = '';
62+
foreach ($this->loop() as $chunk) {
63+
if ($chunk->isTimeout()) {
64+
var_dump('todo');
65+
$this->connect();
66+
// $response = $this->doRequest();
67+
// if (false === $this->shouldDisconnect) {
68+
// @TODO handle reconnection?
69+
// $response = $this->request()
70+
// }
71+
72+
continue;
73+
}
74+
75+
// We have to gather our own Buffer that may be longer then a chunk
76+
$buffer .= $chunk->getContent();
77+
78+
if (!$buffer) {
79+
var_dump('closed');
80+
// connection closed, should we throw ?
81+
return null;
82+
}
83+
84+
// If the line starts with a U+003A COLON character (':') ignore the line
85+
if (':' === $buffer[0]) {
86+
$buffer = '';
87+
continue;
88+
}
89+
90+
// Process if we got new line ending
91+
$end = substr($buffer, -1);
92+
if ("\n" !== $end && "\r" !== $end) {
93+
continue;
94+
}
95+
96+
$message = MessageEvent::parse($buffer);
97+
if (null !== $message->getId()) {
98+
$this->requestArgs[2]['headers']['Last-Event-ID'] = $message->getId();
99+
}
100+
101+
$buffer = '';
102+
103+
if (null !== $message->getRetry()) {
104+
$this->reconnectionTime = $message->retry;
105+
}
106+
107+
yield $message;
108+
}
109+
}
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Symfony\Component\HttpClient\ServerSentEvents;
4+
5+
use Symfony\Contracts\HttpClient\HttpClientInterface;
6+
7+
interface EventSourceInterface
8+
{
9+
public function getHttpClient(): HttpClientInterface;
10+
11+
/**
12+
* @return \Iterator|MessageEvent[]
13+
*/
14+
public function getMessages(): \Iterator;
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Symfony\Component\HttpClient\ServerSentEvents;
4+
5+
final class MessageEvent
6+
{
7+
private $data;
8+
private $id;
9+
private $type;
10+
private $retry;
11+
12+
public function __construct(string $data = '', string $id = null, string $type = 'message', int $retry = null)
13+
{
14+
$this->data = $data;
15+
$this->id = $id;
16+
$this->type = $type;
17+
$this->retry = $retry;
18+
}
19+
20+
public static function parse(string $rawData): self
21+
{
22+
preg_match_all('/^([a-z]*)\:? ?(.*)/m', $rawData, $matches, PREG_SET_ORDER);
23+
$data = '';
24+
$retry = $id = null;
25+
$type = 'message';
26+
27+
foreach ($matches as $match) {
28+
switch ($match[1]) {
29+
case 'id':
30+
$id = $match[2];
31+
break;
32+
case 'event':
33+
$type = $match[2];
34+
break;
35+
case 'data':
36+
$data .= $match[2]."\n";
37+
break;
38+
case 'retry':
39+
$retry = ctype_digit($match[2]) ? (int) $match[2] : null;
40+
break;
41+
}
42+
}
43+
44+
if ("\n" === substr($data, -1)) {
45+
$data = substr($data, 0, -1);
46+
}
47+
48+
return new self($data, $id, $type, $retry);
49+
}
50+
51+
public function getId(): ?int
52+
{
53+
return $this->id;
54+
}
55+
56+
public function getType(): ?string
57+
{
58+
return $this->type;
59+
}
60+
61+
public function getData(): string
62+
{
63+
return $this->data;
64+
}
65+
66+
public function getRetry(): ?int
67+
{
68+
return $this->retry;
69+
}
70+
}

src/Symfony/Component/HttpClient/Tests/Chunk/MessageChunkTest.php

-48
This file was deleted.

0 commit comments

Comments
 (0)