<?php
namespace Can\RestBundle\EventListener;
use Can\RestBundle\CanRestBundle;
use Can\RestBundle\Negotiation\NegotiationConfiguration;
use Can\RestBundle\Negotiation\NegotiationHeader;
use Can\RestBundle\Negotiation\StopNegotiationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
/**
* Performs the instance digest negotiation.
*
* This is supposed to be performed after actual content negotiation takes
* place, so if an error occurs, the error response may be generated in the
* appropriate format, if necessary.
*
* @group negotiation
*
* @package can/rest-bundle
* @author lechecacharro <lechecacharro@gmail.com>
*/
class WantDigestListener extends NegotiationListener
{
/**
* Performs the instance digest negotiation.
*
* @param RequestEvent $event
*
* @throws NotAcceptableHttpException
* @throws UnprocessableEntityHttpException
*/
public function onRequest(RequestEvent $event): void
{
if (! $this->configuration->isAcceptInstanceDigests()) {
return;
}
if (! $event->isMasterRequest()) {
return;
}
$request = $event->getRequest();
if (! $request->attributes->get(CanRestBundle::ATTR_SERVICE_ZONE)) {
return;
}
$wantDigest = $this->getWantDigest($request);
if (! $wantDigest) {
return;
}
$digest = null;
try {
$digest = $this->negotiate($this->provider->getDigestNegotiator(), $wantDigest);
} catch (StopNegotiationException $e) {
// Failthrough
}
if (null === $digest) {
if ($this->configuration->isStrictDigestPolicy()) {
$event->setResponse($this->createDigestClientErrorResponse());
} else {
// Just choose an arbitrary ingest algorithm and continue --
// currently we choose the one that requires less effort to
// the server
$digest = $this->selectArbitraryDigest();
}
}
$request->attributes->set(CanRestBundle::ATTR_NEGOTIATION_DIGEST, $digest);
}
/**
* @param Request $request
*
* @return string | null
*/
private function getWantDigest(Request $request): ?string
{
return $this->getNegotiationHeader($request, NegotiationHeader::WANT_DIGEST);
}
/**
* @return Response
*/
private function createDigestClientErrorResponse(): Response
{
$response = new Response('', $this->configuration->getDigestClientErrorStatus());
$response->headers->set(NegotiationHeader::WANT_DIGEST, implode(', ', $this->configuration->getDigests()));
return $response;
}
/**
* Note that it is safe to assume that the list of digests algorithm names
* is not empty, as the {@link NegotiationConfiguration::isAcceptInstanceDigests()}
* method has already been called.
*
* @return string
*/
private function selectArbitraryDigest(): string
{
// FIXME
// Use a priority list; do not choose at random
return $this->configuration->getDigests()[0];
}
}