Skip to content

Commit fd30abe

Browse files
committed
Merge branch '7.1' into 7.2
* 7.1: (31 commits) [DependencyInjection] Fix phpdoc for $calls [Security] check token in payload instead just request fix tests add missing method fix merge fix test fix merge fix test change test to use a real ObjectManager [Mailer] Document the usage of custom headers in Infobip bridge [SecurityBundle] Add `provider` XML attribute to the authenticators it’s missing from [DoctrineBridge] Test reset with a true manager Sync php-cs-fixer config file with 7.2 [HttpClient] Fix parsing SSE [Notifier] Fix thread key in GoogleChat bridge [HttpKernel][Security] Fix accessing session for stateless request [Serializer] Fix `ObjectNormalizer` with property path test handling of special "value" constraint option [PhpUnitBridge] Add missing import [FrameworkBundle] Fix setting default context for certain normalizers ...
2 parents cda3a15 + a8a3352 commit fd30abe

File tree

2 files changed

+55
-60
lines changed

2 files changed

+55
-60
lines changed

EventSourceHttpClient.php

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\HttpClient;
1313

14+
use Symfony\Component\HttpClient\Chunk\DataChunk;
1415
use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
1516
use Symfony\Component\HttpClient\Exception\EventSourceException;
1617
use Symfony\Component\HttpClient\Response\AsyncContext;
@@ -120,17 +121,30 @@ public function request(string $method, string $url, array $options = []): Respo
120121
return;
121122
}
122123

123-
$rx = '/((?:\r\n){2,}|\r{2,}|\n{2,})/';
124-
$content = $state->buffer.$chunk->getContent();
125-
126124
if ($chunk->isLast()) {
127-
$rx = substr_replace($rx, '|$', -2, 0);
125+
if ('' !== $content = $state->buffer) {
126+
$state->buffer = '';
127+
yield new DataChunk(-1, $content);
128+
}
129+
130+
yield $chunk;
131+
132+
return;
128133
}
129-
$events = preg_split($rx, $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
134+
135+
$content = $state->buffer.$chunk->getContent();
136+
$events = preg_split('/((?:\r\n){2,}|\r{2,}|\n{2,})/', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
130137
$state->buffer = array_pop($events);
131138

132139
for ($i = 0; isset($events[$i]); $i += 2) {
133-
$event = new ServerSentEvent($events[$i].$events[1 + $i]);
140+
$content = $events[$i].$events[1 + $i];
141+
if (!preg_match('/(?:^|\r\n|[\r\n])[^:\r\n]/', $content)) {
142+
yield new DataChunk(-1, $content);
143+
144+
continue;
145+
}
146+
147+
$event = new ServerSentEvent($content);
134148

135149
if ('' !== $event->getId()) {
136150
$context->setInfo('last_event_id', $state->lastEventId = $event->getId());
@@ -142,17 +156,6 @@ public function request(string $method, string $url, array $options = []): Respo
142156

143157
yield $event;
144158
}
145-
146-
if (preg_match('/^(?::[^\r\n]*+(?:\r\n|[\r\n]))+$/m', $state->buffer)) {
147-
$content = $state->buffer;
148-
$state->buffer = '';
149-
150-
yield $context->createChunk($content);
151-
}
152-
153-
if ($chunk->isLast()) {
154-
yield $chunk;
155-
}
156159
});
157160
}
158161
}

Tests/EventSourceHttpClientTest.php

Lines changed: 35 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
use Symfony\Component\HttpClient\Chunk\DataChunk;
1616
use Symfony\Component\HttpClient\Chunk\ErrorChunk;
1717
use Symfony\Component\HttpClient\Chunk\FirstChunk;
18+
use Symfony\Component\HttpClient\Chunk\LastChunk;
1819
use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
1920
use Symfony\Component\HttpClient\EventSourceHttpClient;
2021
use Symfony\Component\HttpClient\Exception\EventSourceException;
22+
use Symfony\Component\HttpClient\MockHttpClient;
2123
use Symfony\Component\HttpClient\Response\MockResponse;
2224
use Symfony\Component\HttpClient\Response\ResponseStream;
2325
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -34,7 +36,11 @@ class EventSourceHttpClientTest extends TestCase
3436
*/
3537
public function testGetServerSentEvents(string $sep)
3638
{
37-
$rawData = <<<TXT
39+
$es = new EventSourceHttpClient(new MockHttpClient(function (string $method, string $url, array $options) use ($sep): MockResponse {
40+
$this->assertSame(['Accept: text/event-stream', 'Cache-Control: no-cache'], $options['headers']);
41+
42+
return new MockResponse([
43+
str_replace("\n", $sep, <<<TXT
3844
event: builderror
3945
id: 46
4046
data: {"foo": "bar"}
@@ -43,7 +49,18 @@ public function testGetServerSentEvents(string $sep)
4349
id: 47
4450
data: {}
4551
52+
: this is a oneline comment
53+
54+
: this is a
55+
: multiline comment
56+
57+
: comments are ignored
4658
event: reload
59+
60+
TXT
61+
),
62+
str_replace("\n", $sep, <<<TXT
63+
: anywhere
4764
id: 48
4865
data: {}
4966
@@ -62,58 +79,33 @@ public function testGetServerSentEvents(string $sep)
6279
6380
id: 60
6481
data
65-
TXT;
66-
$data = str_replace("\n", $sep, $rawData);
67-
68-
$chunk = new DataChunk(0, $data);
69-
$response = new MockResponse('', ['canceled' => false, 'http_method' => 'GET', 'url' => 'http://localhost:8080/events', 'response_headers' => ['content-type: text/event-stream']]);
70-
$responseStream = new ResponseStream((function () use ($response, $chunk) {
71-
yield $response => new FirstChunk();
72-
yield $response => $chunk;
73-
yield $response => new ErrorChunk(0, 'timeout');
74-
})());
75-
76-
$hasCorrectHeaders = function ($options) {
77-
$this->assertSame(['Accept: text/event-stream', 'Cache-Control: no-cache'], $options['headers']);
78-
79-
return true;
80-
};
81-
82-
$httpClient = $this->createMock(HttpClientInterface::class);
83-
$httpClient->method('request')->with('GET', 'http://localhost:8080/events', $this->callback($hasCorrectHeaders))->willReturn($response);
84-
85-
$httpClient->method('stream')->willReturn($responseStream);
86-
87-
$es = new EventSourceHttpClient($httpClient);
82+
TXT
83+
),
84+
], [
85+
'canceled' => false,
86+
'http_method' => 'GET',
87+
'url' => 'http://localhost:8080/events',
88+
'response_headers' => ['content-type: text/event-stream'],
89+
]);
90+
}));
8891
$res = $es->connect('http://localhost:8080/events');
8992

9093
$expected = [
9194
new FirstChunk(),
9295
new ServerSentEvent(str_replace("\n", $sep, "event: builderror\nid: 46\ndata: {\"foo\": \"bar\"}\n\n")),
9396
new ServerSentEvent(str_replace("\n", $sep, "event: reload\nid: 47\ndata: {}\n\n")),
94-
new ServerSentEvent(str_replace("\n", $sep, "event: reload\nid: 48\ndata: {}\n\n")),
97+
new DataChunk(-1, str_replace("\n", $sep, ": this is a oneline comment\n\n")),
98+
new DataChunk(-1, str_replace("\n", $sep, ": this is a\n: multiline comment\n\n")),
99+
new ServerSentEvent(str_replace("\n", $sep, ": comments are ignored\nevent: reload\n: anywhere\nid: 48\ndata: {}\n\n")),
95100
new ServerSentEvent(str_replace("\n", $sep, "data: test\ndata:test\nid: 49\nevent: testEvent\n\n\n")),
96101
new ServerSentEvent(str_replace("\n", $sep, "id: 50\ndata: <tag>\ndata\ndata: <foo />\ndata\ndata: </tag>\n\n")),
102+
new DataChunk(-1, str_replace("\n", $sep, "id: 60\ndata")),
103+
new LastChunk("\r\n" === $sep ? 355 : 322),
97104
];
98-
$i = 0;
99-
100-
$this->expectExceptionMessage('Response has been canceled');
101-
while ($res) {
102-
if ($i > 0) {
103-
$res->cancel();
104-
}
105-
foreach ($es->stream($res) as $chunk) {
106-
if ($chunk->isTimeout()) {
107-
continue;
108-
}
109-
110-
if ($chunk->isLast()) {
111-
continue;
112-
}
113-
114-
$this->assertEquals($expected[$i++], $chunk);
115-
}
105+
foreach ($es->stream($res) as $chunk) {
106+
$this->assertEquals(array_shift($expected), $chunk);
116107
}
108+
$this->assertSame([], $expected);
117109
}
118110

119111
public function testPostServerSentEvents()

0 commit comments

Comments
 (0)