eventsource.php 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. <?php
  2. /**
  3. * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. /**
  9. * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events)
  10. * includes a fallback for older browsers and IE
  11. *
  12. * use server side events with caution, to many open requests can hang the server
  13. */
  14. class OC_EventSource implements \OCP\IEventSource {
  15. /**
  16. * @var bool
  17. */
  18. private $fallback;
  19. /**
  20. * @var int
  21. */
  22. private $fallBackId = 0;
  23. /**
  24. * @var bool
  25. */
  26. private $started = false;
  27. protected function init() {
  28. if ($this->started) {
  29. return;
  30. }
  31. $this->started = true;
  32. // prevent php output buffering, caching and nginx buffering
  33. OC_Util::obEnd();
  34. header('Cache-Control: no-cache');
  35. header('X-Accel-Buffering: no');
  36. $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true';
  37. if ($this->fallback) {
  38. $this->fallBackId = (int)$_GET['fallback_id'];
  39. header("Content-Type: text/html");
  40. echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy
  41. } else {
  42. header("Content-Type: text/event-stream");
  43. }
  44. if (!OC_Util::isCallRegistered()) {
  45. $this->send('error', 'Possible CSRF attack. Connection will be closed.');
  46. $this->close();
  47. exit();
  48. }
  49. flush();
  50. }
  51. /**
  52. * send a message to the client
  53. *
  54. * @param string $type
  55. * @param mixed $data
  56. *
  57. * @throws \BadMethodCallException
  58. * if only one parameter is given, a typeless message will be send with that parameter as data
  59. */
  60. public function send($type, $data = null) {
  61. if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) {
  62. throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')');
  63. }
  64. $this->init();
  65. if (is_null($data)) {
  66. $data = $type;
  67. $type = null;
  68. }
  69. if ($this->fallback) {
  70. $response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
  71. . $this->fallBackId . ',"' . $type . '",' . OCP\JSON::encode($data) . ')</script>' . PHP_EOL;
  72. echo $response;
  73. } else {
  74. if ($type) {
  75. echo 'event: ' . $type . PHP_EOL;
  76. }
  77. echo 'data: ' . OCP\JSON::encode($data) . PHP_EOL;
  78. }
  79. echo PHP_EOL;
  80. flush();
  81. }
  82. /**
  83. * close the connection of the event source
  84. */
  85. public function close() {
  86. $this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it
  87. }
  88. }