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

Lines changed: 0 additions & 50 deletions
This file was deleted.

src/Symfony/Component/HttpClient/EventSourceHttpClient.php

Lines changed: 0 additions & 97 deletions
This file was deleted.
Lines changed: 110 additions & 0 deletions
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+
}
Lines changed: 15 additions & 0 deletions
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+
}
Lines changed: 70 additions & 0 deletions
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

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy