From 54c918fe48eeae35adae64381f1e1c41ac4a660b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 29 Aug 2014 17:18:11 +0200 Subject: Initialize an event source when we start using it, not in the constructor --- lib/private/eventsource.php | 88 +++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/lib/private/eventsource.php b/lib/private/eventsource.php index 192c1446b5a..5a870ae3f3a 100644 --- a/lib/private/eventsource.php +++ b/lib/private/eventsource.php @@ -1,25 +1,10 @@ . -* -*/ + * Copyright (c) 2014 Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ /** * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) @@ -27,51 +12,70 @@ * * use server side events with caution, to many open requests can hang the server */ -class OC_EventSource{ +class OC_EventSource { + /** + * @var bool + */ private $fallback; - private $fallBackId=0; - public function __construct() { + /** + * @var int + */ + private $fallBackId = 0; + + /** + * @var bool + */ + private $started = false; + + protected function init() { + if ($this->started) { + return; + } + $this->started = true; + + // prevent php output buffering, caching and nginx buffering OC_Util::obEnd(); header('Cache-Control: no-cache'); header('X-Accel-Buffering: no'); - $this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true'; - if($this->fallback) { - $this->fallBackId=$_GET['fallback_id']; + $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true'; + if ($this->fallback) { + $this->fallBackId = $_GET['fallback_id']; header("Content-Type: text/html"); - echo str_repeat(''.PHP_EOL, 10); //dummy data to keep IE happy - }else{ + echo str_repeat('' . PHP_EOL, 10); //dummy data to keep IE happy + } else { header("Content-Type: text/event-stream"); } - if( !OC_Util::isCallRegistered()) { + if (!OC_Util::isCallRegistered()) { $this->send('error', 'Possible CSRF attack. Connection will be closed.'); $this->close(); exit(); } flush(); - } /** * send a message to the client + * * @param string $type * @param mixed $data * * if only one parameter is given, a typeless message will be send with that parameter as data */ - public function send($type, $data=null) { - if(is_null($data)) { - $data=$type; - $type=null; + public function send($type, $data = null) { + $this->init(); + if (is_null($data)) { + $data = $type; + $type = null; } - if($this->fallback) { + if ($this->fallback) { $fallBackId = OC_Util::sanitizeHTML($this->fallBackId); - $response='' . PHP_EOL; + $response = '' . PHP_EOL; echo $response; - }else{ - if($type) { - echo 'event: ' . $type.PHP_EOL; + } else { + if ($type) { + echo 'event: ' . $type . PHP_EOL; } echo 'data: ' . OCP\JSON::encode($data) . PHP_EOL; } @@ -80,9 +84,9 @@ class OC_EventSource{ } /** - * close the connection of the even source + * close the connection of the event source */ public function close() { - $this->send('__internal__', 'close');//server side closing can be an issue, let the client do it + $this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it } } -- cgit v1.2.3 From dad53180bc6c554336c3a30abf64e45c242b2262 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 29 Aug 2014 17:19:38 +0200 Subject: Add event source to the public api --- lib/private/eventsource.php | 2 +- lib/private/server.php | 9 +++++++++ lib/public/ieventsource.php | 32 ++++++++++++++++++++++++++++++++ lib/public/iservercontainer.php | 7 +++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 lib/public/ieventsource.php diff --git a/lib/private/eventsource.php b/lib/private/eventsource.php index 5a870ae3f3a..22782d677e4 100644 --- a/lib/private/eventsource.php +++ b/lib/private/eventsource.php @@ -12,7 +12,7 @@ * * use server side events with caution, to many open requests can hang the server */ -class OC_EventSource { +class OC_EventSource implements \OCP\IEventSource { /** * @var bool */ diff --git a/lib/private/server.php b/lib/private/server.php index 5d40f1327f6..1c3b1b03ce9 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -492,4 +492,13 @@ class Server extends SimpleContainer implements IServerContainer { } return new CertificateManager($user); } + + /** + * Returns a search instance + * + * @return \OCP\IEventSource + */ + function getEventSource() { + return new \OC_EventSource(); + } } diff --git a/lib/public/ieventsource.php b/lib/public/ieventsource.php new file mode 100644 index 00000000000..ea4bfc73d42 --- /dev/null +++ b/lib/public/ieventsource.php @@ -0,0 +1,32 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCP; + +/** + * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) + * includes a fallback for older browsers and IE + * + * use server side events with caution, to many open requests can hang the server + */ +interface IEventSource { + /** + * send a message to the client + * + * @param string $type + * @param mixed $data + * + * if only one parameter is given, a typeless message will be send with that parameter as data + */ + public function send($type, $data = null); + + /** + * close the connection of the event source + */ + public function close(); +} diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 60b0b497c54..24d8894d9fe 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -235,4 +235,11 @@ interface IServerContainer { * @return \OCP\ICertificateManager */ function getCertificateManager($user = null); + + /** + * Returns a search instance + * + * @return \OCP\IEventSource + */ + function getEventSource(); } -- cgit v1.2.3 From 65608d7c9253d03ba5b56615f850f19f1fd90a49 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 29 Aug 2014 17:21:52 +0200 Subject: Use the public api to get event sources --- apps/files/ajax/newfile.php | 2 +- apps/files/ajax/scan.php | 6 +++--- core/ajax/update.php | 2 +- lib/private/appframework/core/api.php | 7 +++---- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 606576760ec..1070246fca9 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -16,7 +16,7 @@ $content = isset( $_REQUEST['content'] ) ? $_REQUEST['content'] : ''; $source = isset( $_REQUEST['source'] ) ? trim($_REQUEST['source'], '/\\') : ''; if($source) { - $eventSource=new OC_EventSource(); + $eventSource = \OC::$server->getEventSource(); } else { OC_JSON::callCheck(); } diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index 3ec7f9394b1..eed620113ac 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -15,7 +15,7 @@ if (isset($_GET['users'])) { $users = array(OC_User::getUser()); } -$eventSource = new OC_EventSource(); +$eventSource = \OC::$server->getEventSource(); $listener = new ScanListener($eventSource); foreach ($users as $user) { @@ -39,12 +39,12 @@ class ScanListener { private $lastCount = 0; /** - * @var \OC_EventSource event source to pass events to + * @var \OCP\IEventSource event source to pass events to */ private $eventSource; /** - * @param \OC_EventSource $eventSource + * @param \OCP\IEventSource $eventSource */ public function __construct($eventSource) { $this->eventSource = $eventSource; diff --git a/core/ajax/update.php b/core/ajax/update.php index 627ada080c8..89ce2798378 100644 --- a/core/ajax/update.php +++ b/core/ajax/update.php @@ -4,7 +4,7 @@ require_once '../../lib/base.php'; if (OC::checkUpgrade(false)) { $l = new \OC_L10N('core'); - $eventSource = new OC_EventSource(); + $eventSource = \OC::$server->getEventSource(); $updater = new \OC\Updater(\OC_Log::$object); $updater->listen('\OC\Updater', 'maintenanceStart', function () use ($eventSource, $l) { $eventSource->send('success', (string)$l->t('Turned on maintenance mode')); diff --git a/lib/private/appframework/core/api.php b/lib/private/appframework/core/api.php index ba6b9f95cb2..0dd3a583868 100644 --- a/lib/private/appframework/core/api.php +++ b/lib/private/appframework/core/api.php @@ -110,12 +110,11 @@ class API implements IApi{ /** - * used to return and open a new eventsource - * @return \OC_EventSource a new open EventSource class + * used to return and open a new event source + * @return \OCP\IEventSource a new open EventSource class */ public function openEventSource(){ - # TODO: use public api - return new \OC_EventSource(); + return \OC::$server->getEventSource(); } /** -- cgit v1.2.3 From 8605e2e6a5ac637ed552d9469f3dc31717b4ea3e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 29 Aug 2014 17:33:10 +0200 Subject: Explicitly cast id and validate type --- lib/private/eventsource.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/private/eventsource.php b/lib/private/eventsource.php index 22782d677e4..53947f3a2f2 100644 --- a/lib/private/eventsource.php +++ b/lib/private/eventsource.php @@ -40,7 +40,7 @@ class OC_EventSource implements \OCP\IEventSource { header('X-Accel-Buffering: no'); $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true'; if ($this->fallback) { - $this->fallBackId = $_GET['fallback_id']; + $this->fallBackId = (int)$_GET['fallback_id']; header("Content-Type: text/html"); echo str_repeat('' . PHP_EOL, 10); //dummy data to keep IE happy } else { @@ -60,18 +60,21 @@ class OC_EventSource implements \OCP\IEventSource { * @param string $type * @param mixed $data * + * @throws \BadMethodCallException * if only one parameter is given, a typeless message will be send with that parameter as data */ public function send($type, $data = null) { + if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) { + throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')'); + } $this->init(); if (is_null($data)) { $data = $type; $type = null; } if ($this->fallback) { - $fallBackId = OC_Util::sanitizeHTML($this->fallBackId); $response = '' . PHP_EOL; + . $this->fallBackId . ',"' . $type . '",' . OCP\JSON::encode($data) . ')' . PHP_EOL; echo $response; } else { if ($type) { -- cgit v1.2.3 From fa3393674c854e884abf10d3b82dab03aca83c61 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 4 Sep 2014 01:10:02 +0200 Subject: Better phpdoc and method naming --- apps/files/ajax/newfile.php | 2 +- apps/files/ajax/scan.php | 2 +- core/ajax/update.php | 2 +- lib/private/appframework/core/api.php | 2 +- lib/private/server.php | 4 ++-- lib/public/ieventsource.php | 2 ++ lib/public/iservercontainer.php | 4 ++-- 7 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 1070246fca9..46629e1b602 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -16,7 +16,7 @@ $content = isset( $_REQUEST['content'] ) ? $_REQUEST['content'] : ''; $source = isset( $_REQUEST['source'] ) ? trim($_REQUEST['source'], '/\\') : ''; if($source) { - $eventSource = \OC::$server->getEventSource(); + $eventSource = \OC::$server->createEventSource(); } else { OC_JSON::callCheck(); } diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index eed620113ac..da5a2ce6f26 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -15,7 +15,7 @@ if (isset($_GET['users'])) { $users = array(OC_User::getUser()); } -$eventSource = \OC::$server->getEventSource(); +$eventSource = \OC::$server->createEventSource(); $listener = new ScanListener($eventSource); foreach ($users as $user) { diff --git a/core/ajax/update.php b/core/ajax/update.php index 89ce2798378..2a586535e70 100644 --- a/core/ajax/update.php +++ b/core/ajax/update.php @@ -4,7 +4,7 @@ require_once '../../lib/base.php'; if (OC::checkUpgrade(false)) { $l = new \OC_L10N('core'); - $eventSource = \OC::$server->getEventSource(); + $eventSource = \OC::$server->createEventSource(); $updater = new \OC\Updater(\OC_Log::$object); $updater->listen('\OC\Updater', 'maintenanceStart', function () use ($eventSource, $l) { $eventSource->send('success', (string)$l->t('Turned on maintenance mode')); diff --git a/lib/private/appframework/core/api.php b/lib/private/appframework/core/api.php index 0dd3a583868..279f4bf97f7 100644 --- a/lib/private/appframework/core/api.php +++ b/lib/private/appframework/core/api.php @@ -114,7 +114,7 @@ class API implements IApi{ * @return \OCP\IEventSource a new open EventSource class */ public function openEventSource(){ - return \OC::$server->getEventSource(); + return \OC::$server->createEventSource(); } /** diff --git a/lib/private/server.php b/lib/private/server.php index 1c3b1b03ce9..0c663e8553a 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -494,11 +494,11 @@ class Server extends SimpleContainer implements IServerContainer { } /** - * Returns a search instance + * Create a new event source * * @return \OCP\IEventSource */ - function getEventSource() { + function createEventSource() { return new \OC_EventSource(); } } diff --git a/lib/public/ieventsource.php b/lib/public/ieventsource.php index ea4bfc73d42..eb7853c5e90 100644 --- a/lib/public/ieventsource.php +++ b/lib/public/ieventsource.php @@ -13,6 +13,8 @@ namespace OCP; * includes a fallback for older browsers and IE * * use server side events with caution, to many open requests can hang the server + * + * The event source will initialize the connection to the client when the first data is sent */ interface IEventSource { /** diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 24d8894d9fe..1abf0d9938d 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -237,9 +237,9 @@ interface IServerContainer { function getCertificateManager($user = null); /** - * Returns a search instance + * Create a new event source * * @return \OCP\IEventSource */ - function getEventSource(); + function createEventSource(); } -- cgit v1.2.3