Skip to content

Commit 1b05044

Browse files
authored
Merge pull request #2 from clue-labs/content-type
Ignore additional parameters and case for Content-Type response header
2 parents 957836e + d77cdbc commit 1b05044

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

src/EventSource.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ private function send()
8282
return;
8383
}
8484

85-
if ($response->getHeaderLine('Content-Type') !== 'text/event-stream') {
85+
// match `Content-Type: text/event-stream` (case insensitve and ignore additional parameters)
86+
if (!preg_match('/^text\/event-stream(?:$|;)/i', $response->getHeaderLine('Content-Type'))) {
8687
$this->readyState = self::CLOSED;
8788
$this->emit('error', array(new \UnexpectedValueException('Unexpected Content-Type')));
8889
$this->close();

tests/EventSourceTest.php

+62
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,68 @@ public function testConstructorWillReportOpenWhenGetResponseResolvesWithValidRes
420420
$this->assertEquals(EventSource::OPEN, $readyState);
421421
}
422422

423+
public function testConstructorWillReportOpenWhenGetResponseResolvesWithValidResponseWithCaseInsensitiveContentTypeAfterTimer()
424+
{
425+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
426+
$timer = null;
427+
$loop->expects($this->at(0))->method('addTimer')->with(0, $this->callback(function ($cb) use (&$timer) {
428+
$timer = $cb;
429+
return true;
430+
}));
431+
432+
$stream = new ThroughStream();
433+
$response = new Response(200, array('CONTENT-type' => 'TEXT/Event-Stream'), new ReadableBodyStream($stream));
434+
$browser = $this->getMockBuilder('Clue\React\Buzz\Browser')->disableOriginalConstructor()->getMock();
435+
$browser->expects($this->once())->method('get')->with('http://example.com')->willReturn(\React\Promise\resolve($response));
436+
437+
$es = new EventSource('http://example.com', $loop);
438+
439+
$ref = new ReflectionProperty($es, 'browser');
440+
$ref->setAccessible(true);
441+
$ref->setValue($es, $browser);
442+
443+
$readyState = null;
444+
$es->on('open', function () use ($es, &$readyState) {
445+
$readyState = $es->readyState;
446+
});
447+
448+
$this->assertNotNull($timer);
449+
$timer();
450+
451+
$this->assertEquals(EventSource::OPEN, $readyState);
452+
}
453+
454+
public function testConstructorWillReportOpenWhenGetResponseResolvesWithValidResponseAndSuperfluousParametersAfterTimer()
455+
{
456+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
457+
$timer = null;
458+
$loop->expects($this->at(0))->method('addTimer')->with(0, $this->callback(function ($cb) use (&$timer) {
459+
$timer = $cb;
460+
return true;
461+
}));
462+
463+
$stream = new ThroughStream();
464+
$response = new Response(200, array('Content-Type' => 'text/event-stream;charset=utf-8;foo=bar'), new ReadableBodyStream($stream));
465+
$browser = $this->getMockBuilder('Clue\React\Buzz\Browser')->disableOriginalConstructor()->getMock();
466+
$browser->expects($this->once())->method('get')->with('http://example.com')->willReturn(\React\Promise\resolve($response));
467+
468+
$es = new EventSource('http://example.com', $loop);
469+
470+
$ref = new ReflectionProperty($es, 'browser');
471+
$ref->setAccessible(true);
472+
$ref->setValue($es, $browser);
473+
474+
$readyState = null;
475+
$es->on('open', function () use ($es, &$readyState) {
476+
$readyState = $es->readyState;
477+
});
478+
479+
$this->assertNotNull($timer);
480+
$timer();
481+
482+
$this->assertEquals(EventSource::OPEN, $readyState);
483+
}
484+
423485
public function testCloseResponseStreamWillReturnToStartTimerToReconnectWithoutErrorEvent()
424486
{
425487
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();

0 commit comments

Comments
 (0)