From 0ea6d131e07250564bec2d471f5bc75bb3544cd5 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 14 Apr 2020 16:45:35 -0700 Subject: [PATCH] In Conduit responses, assert that Phabricator supports a "gzip" capability Summary: Ref T13507. If we believe the server can accept "Content-Encoding: gzip" requests, make the claim in an "X-Conduit-Capabilities" header in responses. Clients can use request compression on subsequent requests. Test Plan: See D21119 for the client piece. Maniphest Tasks: T13507 Differential Revision: https://secure.phabricator.com/D21120 --- .../requeststream/AphrontRequestStream.php | 3 ++- src/aphront/response/AphrontJSONResponse.php | 8 ++++---- src/aphront/response/AphrontResponse.php | 11 ++++++++++- .../PhabricatorConduitAPIController.php | 19 ++++++++++++++++++- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/aphront/requeststream/AphrontRequestStream.php b/src/aphront/requeststream/AphrontRequestStream.php index 6bd27712ed..009451c3ad 100644 --- a/src/aphront/requeststream/AphrontRequestStream.php +++ b/src/aphront/requeststream/AphrontRequestStream.php @@ -101,8 +101,9 @@ final class AphrontRequestStream extends Phobject { $filters = stream_get_filters(); foreach ($filters as $filter) { - if (preg_match('/^zlib\\./', $filter)) { + if (!strncasecmp($filter, 'zlib.', strlen('zlib.'))) { $has_zlib = true; + break; } } diff --git a/src/aphront/response/AphrontJSONResponse.php b/src/aphront/response/AphrontJSONResponse.php index 3d1c429d41..228a1a1721 100644 --- a/src/aphront/response/AphrontJSONResponse.php +++ b/src/aphront/response/AphrontJSONResponse.php @@ -31,10 +31,10 @@ final class AphrontJSONResponse extends AphrontResponse { } public function getHeaders() { - $headers = array( - array('Content-Type', 'application/json'), - ); - $headers = array_merge(parent::getHeaders(), $headers); + $headers = parent::getHeaders(); + + $headers[] = array('Content-Type', 'application/json'); + return $headers; } diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index 8a94adf38d..5dae168c73 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -10,7 +10,7 @@ abstract class AphrontResponse extends Phobject { private $contentSecurityPolicyURIs; private $disableContentSecurityPolicy; protected $frameable; - + private $headers = array(); public function setRequest($request) { $this->request = $request; @@ -49,6 +49,11 @@ abstract class AphrontResponse extends Phobject { return $this; } + final public function addHeader($key, $value) { + $this->headers[] = array($key, $value); + return $this; + } + /* -( Content )------------------------------------------------------------ */ @@ -105,6 +110,10 @@ abstract class AphrontResponse extends Phobject { $headers[] = array('Referrer-Policy', 'no-referrer'); + foreach ($this->headers as $header) { + $headers[] = $header; + } + return $headers; } diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index d34189125a..3b3e6ee423 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -134,9 +134,17 @@ final class PhabricatorConduitAPIController $method_implementation); case 'json': default: - return id(new AphrontJSONResponse()) + $response = id(new AphrontJSONResponse()) ->setAddJSONShield(false) ->setContent($response->toDictionary()); + + $capabilities = $this->getConduitCapabilities(); + if ($capabilities) { + $capabilities = implode(' ', $capabilities); + $response->addHeader('X-Conduit-Capabilities', $capabilities); + } + + return $response; } } @@ -716,5 +724,14 @@ final class PhabricatorConduitAPIController return false; } + private function getConduitCapabilities() { + $capabilities = array(); + + if (AphrontRequestStream::supportsGzip()) { + $capabilities[] = 'gzip'; + } + + return $capabilities; + } }