<?php
namespace Can\RestBundle\EventListener;
use Can\RestBundle\CanRestBundle;
use Can\RestBundle\Http\Exception\UnsupportedMediaTypeHttpException;
use Can\RestBundle\Http\HttpConfiguration;
use Can\RestBundle\Http\Method;
use Can\RestBundle\Http\MethodSupport;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Exception;
/**
* Some of the HTTP methods do not define request bodies. Servers MUST examine
* all requests for a body, even when a body was not expected. In cases where
* a request body is present but would be ignored by a server, the server MUST
* reject the request with 415 (Unsupported Media Type).
*
* This informs the client (which may have been attempting to use an extension)
* that the body could not be processed as the client intended.
*
* @group core
*
* @package can/rest-bundle
* @author lechecacharro <lechecacharro@gmail.com>
*/
class RequestBodyListener
{
/**
* @var HttpConfiguration
*/
protected $configuration;
/**
* RequiredBodyListener constructor.
*
* @param HttpConfiguration $configuration
*/
public function __construct(HttpConfiguration $configuration)
{
$this->configuration = $configuration;
}
/**
* @param RequestEvent $event
*/
public function onRequest(RequestEvent $event): void
{
// Skip any check if the configuration states that we should
// allow unexpected entities in the body of requests which have
// no semantic defined for such entity
if ($this->configuration->isAllowUnexpectedEntities()) {
return;
}
if (! $event->isMasterRequest()) {
return;
}
$request = $event->getRequest();
if (! $request->attributes->get(CanRestBundle::ATTR_SERVICE_ZONE)) {
return;
}
try {
$method = Method::getMethod($request->getMethod());
} catch (Exception $e) {
// This should not ever happen, as the RequestMethodListener
// should handle this case
throw new MethodNotAllowedHttpException(MethodSupport::getHttpMethods(0));
}
$hasContent = $this->hasContent($request);
$request->attributes->set(CanRestBundle::ATTR_REQUEST_CONTENT, $hasContent);
if ($hasContent && ! $method->mayHaveRequestBody()) {
throw new UnsupportedMediaTypeHttpException(sprintf(
'No semantic defined for the request body entity of a %s request',
$method->getName()
));
}
}
/**
* @param Request $request
*
* @return bool
*/
private function hasContent(Request $request): bool
{
// DO NOT use $request->getContent(), as in case of POST or PUT requests,
// it would read the php://input fully using file_get_contents(), which
// can be expensive if the request body is too large
$contentLength = $request->headers->get('Content-Length', 0);
if (is_array($contentLength)) {
$contentLength = (int) reset($contentLength);
}
return $contentLength > 0;
}
}