Changeset View
Changeset View
Standalone View
Standalone View
src/applications/celerity/controller/CelerityResourceController.php
Show All 18 Lines | abstract class CelerityResourceController extends PhabricatorController { | ||||
} | } | ||||
public function shouldAllowLegallyNonCompliantUsers() { | public function shouldAllowLegallyNonCompliantUsers() { | ||||
return true; | return true; | ||||
} | } | ||||
abstract public function getCelerityResourceMap(); | abstract public function getCelerityResourceMap(); | ||||
protected function serveResource($path, $package_hash = null) { | protected function serveResource(array $spec) { | ||||
$path = $spec['path']; | |||||
$hash = idx($spec, 'hash'); | |||||
// Sanity checking to keep this from exposing anything sensitive, since it | // Sanity checking to keep this from exposing anything sensitive, since it | ||||
// ultimately boils down to disk reads. | // ultimately boils down to disk reads. | ||||
if (preg_match('@(//|\.\.)@', $path)) { | if (preg_match('@(//|\.\.)@', $path)) { | ||||
return new Aphront400Response(); | return new Aphront400Response(); | ||||
} | } | ||||
$type = CelerityResourceTransformer::getResourceType($path); | $type = CelerityResourceTransformer::getResourceType($path); | ||||
$type_map = self::getSupportedResourceTypes(); | $type_map = self::getSupportedResourceTypes(); | ||||
if (empty($type_map[$type])) { | if (empty($type_map[$type])) { | ||||
throw new Exception(pht('Only static resources may be served.')); | throw new Exception(pht('Only static resources may be served.')); | ||||
} | } | ||||
$dev_mode = PhabricatorEnv::getEnvConfig('phabricator.developer-mode'); | $dev_mode = PhabricatorEnv::getEnvConfig('phabricator.developer-mode'); | ||||
if (AphrontRequest::getHTTPHeader('If-Modified-Since') && !$dev_mode) { | $map = $this->getCelerityResourceMap(); | ||||
$expect_hash = $map->getHashForName($path); | |||||
// Test if the URI hash is correct for our current resource map. If it | |||||
// is not, refuse to cache this resource. This avoids poisoning caches | |||||
// and CDNs if we're getting a request for a new resource to an old node | |||||
// shortly after a push. | |||||
$is_cacheable = ($hash === $expect_hash) && | |||||
$this->isCacheableResourceType($type); | |||||
if (AphrontRequest::getHTTPHeader('If-Modified-Since') && $is_cacheable) { | |||||
// Return a "304 Not Modified". We don't care about the value of this | // Return a "304 Not Modified". We don't care about the value of this | ||||
// field since we never change what resource is served by a given URI. | // field since we never change what resource is served by a given URI. | ||||
return $this->makeResponseCacheable(new Aphront304Response()); | return $this->makeResponseCacheable(new Aphront304Response()); | ||||
} | } | ||||
$is_cacheable = (!$dev_mode) && | |||||
$this->isCacheableResourceType($type); | |||||
$cache = null; | $cache = null; | ||||
$data = null; | $data = null; | ||||
if ($is_cacheable) { | if ($is_cacheable && !$dev_mode) { | ||||
$cache = PhabricatorCaches::getImmutableCache(); | $cache = PhabricatorCaches::getImmutableCache(); | ||||
$request_path = $this->getRequest()->getPath(); | $request_path = $this->getRequest()->getPath(); | ||||
$cache_key = $this->getCacheKey($request_path); | $cache_key = $this->getCacheKey($request_path); | ||||
$data = $cache->getKey($cache_key); | $data = $cache->getKey($cache_key); | ||||
} | } | ||||
if ($data === null) { | if ($data === null) { | ||||
$map = $this->getCelerityResourceMap(); | |||||
if ($map->isPackageResource($path)) { | if ($map->isPackageResource($path)) { | ||||
$resource_names = $map->getResourceNamesForPackageName($path); | $resource_names = $map->getResourceNamesForPackageName($path); | ||||
if (!$resource_names) { | if (!$resource_names) { | ||||
return new Aphront404Response(); | return new Aphront404Response(); | ||||
} | } | ||||
try { | try { | ||||
$data = array(); | $data = array(); | ||||
Show All 38 Lines | protected function serveResource(array $spec) { | ||||
if (isset($cross_origin_types[$type])) { | if (isset($cross_origin_types[$type])) { | ||||
// We could be more tailored here, but it's not currently trivial to | // We could be more tailored here, but it's not currently trivial to | ||||
// generate a comprehensive list of valid origins (an install may have | // generate a comprehensive list of valid origins (an install may have | ||||
// arbitrarily many Phame blogs, for example), and we lose nothing by | // arbitrarily many Phame blogs, for example), and we lose nothing by | ||||
// allowing access from anywhere. | // allowing access from anywhere. | ||||
$response->addAllowOrigin('*'); | $response->addAllowOrigin('*'); | ||||
} | } | ||||
return $this->makeResponseCacheable($response); | if ($is_cacheable) { | ||||
$response = $this->makeResponseCacheable($response); | |||||
} | |||||
return $response; | |||||
} | } | ||||
public static function getSupportedResourceTypes() { | public static function getSupportedResourceTypes() { | ||||
return array( | return array( | ||||
'css' => 'text/css; charset=utf-8', | 'css' => 'text/css; charset=utf-8', | ||||
'js' => 'text/javascript; charset=utf-8', | 'js' => 'text/javascript; charset=utf-8', | ||||
'png' => 'image/png', | 'png' => 'image/png', | ||||
'svg' => 'image/svg+xml', | 'svg' => 'image/svg+xml', | ||||
▲ Show 20 Lines • Show All 45 Lines • Show Last 20 Lines |