]> source.dussan.org Git - nextcloud-server.git/commitdiff
Allow for compression of the PROPFIND responses 20272/head
authorRoeland Jago Douma <roeland@famdouma.nl>
Thu, 2 Apr 2020 13:17:04 +0000 (15:17 +0200)
committerDaniel Kesselberg <mail@danielkesselberg.de>
Sat, 6 Jun 2020 13:55:04 +0000 (15:55 +0200)
For now it just gzip and only for propfind (the 99% case). Already saves
a bunch since the propfinds compress so nicely.

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
apps/dav/composer/composer/autoload_classmap.php
apps/dav/composer/composer/autoload_static.php
apps/dav/lib/Connector/Sabre/PropfindCompressionPlugin.php [new file with mode: 0644]
apps/dav/lib/Server.php
apps/dav/tests/unit/Connector/Sabre/PropfindCompressionPluginTest.php [new file with mode: 0644]

index 057f4dddfcb86d779f2c74353debda0f3f6d2153..71cf0c10876937c62291e06650adb5fe4cf159e7 100644 (file)
@@ -143,6 +143,7 @@ return array(
     'OCA\\DAV\\Connector\\Sabre\\Node' => $baseDir . '/../lib/Connector/Sabre/Node.php',
     'OCA\\DAV\\Connector\\Sabre\\ObjectTree' => $baseDir . '/../lib/Connector/Sabre/ObjectTree.php',
     'OCA\\DAV\\Connector\\Sabre\\Principal' => $baseDir . '/../lib/Connector/Sabre/Principal.php',
+    'OCA\\DAV\\Connector\\Sabre\\PropfindCompressionPlugin' => $baseDir . '/../lib/Connector/Sabre/PropfindCompressionPlugin.php',
     'OCA\\DAV\\Connector\\Sabre\\QuotaPlugin' => $baseDir . '/../lib/Connector/Sabre/QuotaPlugin.php',
     'OCA\\DAV\\Connector\\Sabre\\Server' => $baseDir . '/../lib/Connector/Sabre/Server.php',
     'OCA\\DAV\\Connector\\Sabre\\ServerFactory' => $baseDir . '/../lib/Connector/Sabre/ServerFactory.php',
index ec553beb2bb6d899d0ba411b2a8a5b33a71c8d2c..c0ed98a03ce60b484dd284a7e483fb84fc3b4f50 100644 (file)
@@ -158,6 +158,7 @@ class ComposerStaticInitDAV
         'OCA\\DAV\\Connector\\Sabre\\Node' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Node.php',
         'OCA\\DAV\\Connector\\Sabre\\ObjectTree' => __DIR__ . '/..' . '/../lib/Connector/Sabre/ObjectTree.php',
         'OCA\\DAV\\Connector\\Sabre\\Principal' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Principal.php',
+        'OCA\\DAV\\Connector\\Sabre\\PropfindCompressionPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/PropfindCompressionPlugin.php',
         'OCA\\DAV\\Connector\\Sabre\\QuotaPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/QuotaPlugin.php',
         'OCA\\DAV\\Connector\\Sabre\\Server' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Server.php',
         'OCA\\DAV\\Connector\\Sabre\\ServerFactory' => __DIR__ . '/..' . '/../lib/Connector/Sabre/ServerFactory.php',
diff --git a/apps/dav/lib/Connector/Sabre/PropfindCompressionPlugin.php b/apps/dav/lib/Connector/Sabre/PropfindCompressionPlugin.php
new file mode 100644 (file)
index 0000000..a152950
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\DAV\Connector\Sabre;
+
+use Sabre\DAV\ServerPlugin;
+use Sabre\HTTP\Request;
+use Sabre\HTTP\Response;
+
+class PropfindCompressionPlugin extends ServerPlugin {
+
+       /**
+        * Reference to main server object
+        *
+        * @var Server
+        */
+       private $server;
+
+       /**
+        * This initializes the plugin.
+        *
+        * This function is called by \Sabre\DAV\Server, after
+        * addPlugin is called.
+        *
+        * This method should set up the required event subscriptions.
+        *
+        * @param \Sabre\DAV\Server $server
+        * @return void
+        */
+       public function initialize(\Sabre\DAV\Server $server) {
+               $this->server = $server;
+               $this->server->on('afterMethod:PROPFIND', [$this, 'compressResponse'], 100);
+       }
+
+       public function compressResponse(Request $request, Response $response) {
+               $header = $request->getHeader('Accept-Encoding');
+
+               if ($header === null) {
+                       return $response;
+               }
+
+               if (strpos($header, 'gzip') !== false) {
+                       $body = $response->getBody();
+                       if (is_string($body)) {
+                               $response->setHeader('Content-Encoding', 'gzip');
+                               $response->setBody(gzencode($body));
+                       }
+               }
+
+               return $response;
+       }
+}
index 46cac5ef873217b0cfbb0f82d48b0772ac172435..e64abf43f75419d8395dfb032d72fa928a8cb816 100644 (file)
@@ -54,6 +54,7 @@ use OCA\DAV\Connector\Sabre\DummyGetResponsePlugin;
 use OCA\DAV\Connector\Sabre\FakeLockerPlugin;
 use OCA\DAV\Connector\Sabre\FilesPlugin;
 use OCA\DAV\Connector\Sabre\FilesReportPlugin;
+use OCA\DAV\Connector\Sabre\PropfindCompressionPlugin;
 use OCA\DAV\Connector\Sabre\QuotaPlugin;
 use OCA\DAV\Connector\Sabre\SharesPlugin;
 use OCA\DAV\Connector\Sabre\TagsPlugin;
@@ -314,6 +315,10 @@ class Server {
                                $root->addChild($appCollection);
                        }
                });
+
+               $this->server->addPlugin(
+                       new PropfindCompressionPlugin()
+               );
        }
 
        public function exec() {
diff --git a/apps/dav/tests/unit/Connector/Sabre/PropfindCompressionPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/PropfindCompressionPluginTest.php
new file mode 100644 (file)
index 0000000..a7cb858
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @author Roeland Jago Douma <roeland@famdouma.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\DAV\Tests\unit\Connector\Sabre;
+
+use OCA\DAV\Connector\Sabre\PropfindCompressionPlugin;
+use Sabre\HTTP\Request;
+use Sabre\HTTP\Response;
+use Test\TestCase;
+
+class PropfindCompressionPluginTest extends TestCase {
+       /** @var PropfindCompressionPlugin */
+       private $plugin;
+
+       protected function setUp(): void {
+               parent::setUp();
+
+               $this->plugin = new PropfindCompressionPlugin();
+       }
+
+       public function testNoHeader() {
+               $request = $this->createMock(Request::class);
+               $response = $this->createMock(Response::class);
+
+               $request->method('getHeader')
+                       ->with('Accept-Encoding')
+                       ->willReturn(null);
+
+               $response->expects($this->never())
+                       ->method($this->anything());
+
+               $result = $this->plugin->compressResponse($request, $response);
+               $this->assertSame($response, $result);
+       }
+
+       public function testHeaderButNoGzip() {
+               $request = $this->createMock(Request::class);
+               $response = $this->createMock(Response::class);
+
+               $request->method('getHeader')
+                       ->with('Accept-Encoding')
+                       ->willReturn('deflate');
+
+               $response->expects($this->never())
+                       ->method($this->anything());
+
+               $result = $this->plugin->compressResponse($request, $response);
+               $this->assertSame($response, $result);
+       }
+
+       public function testHeaderGzipButNoStringBody() {
+               $request = $this->createMock(Request::class);
+               $response = $this->createMock(Response::class);
+
+               $request->method('getHeader')
+                       ->with('Accept-Encoding')
+                       ->willReturn('deflate');
+
+               $response->method('getBody')
+                       ->willReturn(5);
+
+               $result = $this->plugin->compressResponse($request, $response);
+               $this->assertSame($response, $result);
+       }
+
+
+       public function testProperGzip() {
+               $request = $this->createMock(Request::class);
+               $response = $this->createMock(Response::class);
+
+               $request->method('getHeader')
+                       ->with('Accept-Encoding')
+                       ->willReturn('gzip, deflate');
+
+               $response->method('getBody')
+                       ->willReturn('my gzip test');
+
+               $response->expects($this->once())
+                       ->method('setHeader')
+                       ->with(
+                               $this->equalTo('Content-Encoding'),
+                               $this->equalTo('gzip')
+                       );
+               $response->expects($this->once())
+                       ->method('setBody')
+                       ->with($this->callback(function ($data) {
+                               $orig = gzdecode($data);
+                               return $orig === 'my gzip test';
+                       }));
+
+               $result = $this->plugin->compressResponse($request, $response);
+               $this->assertSame($response, $result);
+       }
+}