Skip to content

Commit

Permalink
Merge branch '7.0' into 7.1
Browse files Browse the repository at this point in the history
* 7.0: (29 commits)
  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
  [57251] Missing translations for Romanian (ro)
  [ErrorHandler] Fix rendered exception code highlighting on PHP 8.3
  ...
  • Loading branch information
xabbuh committed Jun 25, 2024
2 parents 1ec24a5 + bf1d08a commit a8a3352
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 60 deletions.
37 changes: 20 additions & 17 deletions EventSourceHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\HttpClient;

use Symfony\Component\HttpClient\Chunk\DataChunk;
use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
use Symfony\Component\HttpClient\Exception\EventSourceException;
use Symfony\Component\HttpClient\Response\AsyncContext;
Expand Down Expand Up @@ -121,17 +122,30 @@ public function request(string $method, string $url, array $options = []): Respo
return;
}

$rx = '/((?:\r\n){2,}|\r{2,}|\n{2,})/';
$content = $state->buffer.$chunk->getContent();

if ($chunk->isLast()) {
$rx = substr_replace($rx, '|$', -2, 0);
if ('' !== $content = $state->buffer) {
$state->buffer = '';
yield new DataChunk(-1, $content);
}

yield $chunk;

return;
}
$events = preg_split($rx, $content, -1, \PREG_SPLIT_DELIM_CAPTURE);

$content = $state->buffer.$chunk->getContent();
$events = preg_split('/((?:\r\n){2,}|\r{2,}|\n{2,})/', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
$state->buffer = array_pop($events);

for ($i = 0; isset($events[$i]); $i += 2) {
$event = new ServerSentEvent($events[$i].$events[1 + $i]);
$content = $events[$i].$events[1 + $i];
if (!preg_match('/(?:^|\r\n|[\r\n])[^:\r\n]/', $content)) {
yield new DataChunk(-1, $content);

continue;
}

$event = new ServerSentEvent($content);

if ('' !== $event->getId()) {
$context->setInfo('last_event_id', $state->lastEventId = $event->getId());
Expand All @@ -143,17 +157,6 @@ public function request(string $method, string $url, array $options = []): Respo

yield $event;
}

if (preg_match('/^(?::[^\r\n]*+(?:\r\n|[\r\n]))+$/m', $state->buffer)) {
$content = $state->buffer;
$state->buffer = '';

yield $context->createChunk($content);
}

if ($chunk->isLast()) {
yield $chunk;
}
});
}
}
78 changes: 35 additions & 43 deletions Tests/EventSourceHttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
use Symfony\Component\HttpClient\Chunk\DataChunk;
use Symfony\Component\HttpClient\Chunk\ErrorChunk;
use Symfony\Component\HttpClient\Chunk\FirstChunk;
use Symfony\Component\HttpClient\Chunk\LastChunk;
use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
use Symfony\Component\HttpClient\EventSourceHttpClient;
use Symfony\Component\HttpClient\Exception\EventSourceException;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Component\HttpClient\Response\ResponseStream;
use Symfony\Contracts\HttpClient\HttpClientInterface;
Expand All @@ -34,7 +36,11 @@ class EventSourceHttpClientTest extends TestCase
*/
public function testGetServerSentEvents(string $sep)
{
$rawData = <<<TXT
$es = new EventSourceHttpClient(new MockHttpClient(function (string $method, string $url, array $options) use ($sep): MockResponse {
$this->assertSame(['Accept: text/event-stream', 'Cache-Control: no-cache'], $options['headers']);

return new MockResponse([
str_replace("\n", $sep, <<<TXT
event: builderror
id: 46
data: {"foo": "bar"}
Expand All @@ -43,7 +49,18 @@ public function testGetServerSentEvents(string $sep)
id: 47
data: {}
: this is a oneline comment
: this is a
: multiline comment
: comments are ignored
event: reload
TXT
),
str_replace("\n", $sep, <<<TXT
: anywhere
id: 48
data: {}
Expand All @@ -62,58 +79,33 @@ public function testGetServerSentEvents(string $sep)
id: 60
data
TXT;
$data = str_replace("\n", $sep, $rawData);

$chunk = new DataChunk(0, $data);
$response = new MockResponse('', ['canceled' => false, 'http_method' => 'GET', 'url' => 'http://localhost:8080/events', 'response_headers' => ['content-type: text/event-stream']]);
$responseStream = new ResponseStream((function () use ($response, $chunk) {
yield $response => new FirstChunk();
yield $response => $chunk;
yield $response => new ErrorChunk(0, 'timeout');
})());

$hasCorrectHeaders = function ($options) {
$this->assertSame(['Accept: text/event-stream', 'Cache-Control: no-cache'], $options['headers']);

return true;
};

$httpClient = $this->createMock(HttpClientInterface::class);
$httpClient->method('request')->with('GET', 'http://localhost:8080/events', $this->callback($hasCorrectHeaders))->willReturn($response);

$httpClient->method('stream')->willReturn($responseStream);

$es = new EventSourceHttpClient($httpClient);
TXT
),
], [
'canceled' => false,
'http_method' => 'GET',
'url' => 'http://localhost:8080/events',
'response_headers' => ['content-type: text/event-stream'],
]);
}));
$res = $es->connect('http://localhost:8080/events');

$expected = [
new FirstChunk(),
new ServerSentEvent(str_replace("\n", $sep, "event: builderror\nid: 46\ndata: {\"foo\": \"bar\"}\n\n")),
new ServerSentEvent(str_replace("\n", $sep, "event: reload\nid: 47\ndata: {}\n\n")),
new ServerSentEvent(str_replace("\n", $sep, "event: reload\nid: 48\ndata: {}\n\n")),
new DataChunk(-1, str_replace("\n", $sep, ": this is a oneline comment\n\n")),
new DataChunk(-1, str_replace("\n", $sep, ": this is a\n: multiline comment\n\n")),
new ServerSentEvent(str_replace("\n", $sep, ": comments are ignored\nevent: reload\n: anywhere\nid: 48\ndata: {}\n\n")),
new ServerSentEvent(str_replace("\n", $sep, "data: test\ndata:test\nid: 49\nevent: testEvent\n\n\n")),
new ServerSentEvent(str_replace("\n", $sep, "id: 50\ndata: <tag>\ndata\ndata: <foo />\ndata\ndata: </tag>\n\n")),
new DataChunk(-1, str_replace("\n", $sep, "id: 60\ndata")),
new LastChunk("\r\n" === $sep ? 355 : 322),
];
$i = 0;

$this->expectExceptionMessage('Response has been canceled');
while ($res) {
if ($i > 0) {
$res->cancel();
}
foreach ($es->stream($res) as $chunk) {
if ($chunk->isTimeout()) {
continue;
}

if ($chunk->isLast()) {
continue;
}

$this->assertEquals($expected[$i++], $chunk);
}
foreach ($es->stream($res) as $chunk) {
$this->assertEquals(array_shift($expected), $chunk);
}
$this->assertSame([], $expected);
}

public function testPostServerSentEvents()
Expand Down

0 comments on commit a8a3352

Please # to comment.