public function process(DispatchRequest $dispatch) { if (!$dispatch->isMaster()) { return $dispatch->proceed(); } $request = $dispatch->getHttpRequest(); $path = $request->getPathInfo(); $m = NULL; if (!preg_match("'^_res/+(.+)\$'i", $path, $m)) { return $dispatch->proceed(); } $path = $m[1]; if ('app/' === substr($path, 0, 4)) { $resource = 'k2://app/' . substr($path, 4); } else { $parts = explode('/', $path, 2); if (count($parts) !== 2) { return new HttpResponse(Http::CODE_NOT_FOUND); } $resource = 'k2://' . $parts[0] . '/' . $parts[1]; } if (!is_file($resource)) { return new HttpResponse(Http::CODE_NOT_FOUND); } if (!$this->publisher->isPublic($resource)) { return new HttpResponse(Http::CODE_FORBIDDEN); } $response = new HttpResponse(); // Conditional caching: $etag = sprintf('"%x-%x"', filemtime($resource), filesize($resource)); $response->setHeader('Access-Control-Allow-Origin', '*'); $response->setHeader('Cache-Control', 'public, max-age=7200'); $response->setHeader('ETag', $etag); $response->setHeader(new ExpiresHeader(new \DateTimeImmutable('@' . (time() + 7200)))); if ($etag === $request->getHeader('If-None-Match', '')) { $response->setStatus(Http::CODE_NOT_MODIFIED); return $response; } $mediaType = new MediaType(Filesystem::guessMimeTypeFromFilename($resource)); $response->setHeader('X-Content-Type-Options', 'nosniff'); if ($mediaType->isType('text')) { $response->setHeader('Content-Type', $mediaType . '; charset="utf-8"'); } else { $response->setHeader('Content-Type', (string) $mediaType); } $response->setEntity(new FileEntity(new \SplFileInfo($resource))); return $response; }