Commit a8d858b6 authored by e0ipso's avatar e0ipso Committed by Mateu Aguiló Bosch

feat(Front Controller): Allow blueprint via GET request (#2864903 by e0ipso)

In order to improve performance allow requesting a blueprint via a GET request so the whole response
can be cached.

Closes 2864903
parent a60635ac
......@@ -3,6 +3,8 @@
namespace Drupal\subrequests\Blueprint;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Cache\CacheableResponseInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\SerializerInterface;
......@@ -42,8 +44,15 @@ class Parser {
* - Request mime-type.
*/
public function parseRequest(Request $request) {
$data = '';
if ($request->getMethod() === Request::METHOD_POST) {
$data = $request->getContent();
}
else if ($request->getMethod() === Request::METHOD_GET) {
$data = $request->query->get('query', '');
}
$tree = $this->serializer->deserialize(
$request->getContent(),
$data,
RequestTree::class,
$request->getRequestFormat(),
['master_request' => $request]
......@@ -72,8 +81,18 @@ class Parser {
$headers = ['Content-Type' => $content_type];
$context = ['delimiter' => $delimiter];
// Set the content.
$content = $this->serializer->normalize($responses, 'multipart-related', $context);
return Response::create($content, 207, $headers);
$response = CacheableResponse::create($content, 207, $headers);
// Set the cacheability metadata.
$cacheable_responses = array_filter($responses, function ($response) {
return $response instanceof CacheableResponseInterface;
});
array_walk($cacheable_responses, function (CacheableResponseInterface $partial_response) use ($response) {
$response->addCacheableDependency($partial_response->getCacheableMetadata());
});
return $response;
}
/**
......
......@@ -158,8 +158,13 @@ class RequestTree {
$uri = $request->getUri();
$changes += static::replaceAllOccurrences($responses, $uri);
foreach ($request->query as $key => $value) {
$changes += static::replaceAllOccurrences($responses, $value);
$request->query->set($key, $value);
$new_key = $key;
$query_changes = static::replaceAllOccurrences($responses, $new_key);
$query_changes += static::replaceAllOccurrences($responses, $value);
if ($query_changes) {
$request->query->remove($key);
$request->query->set($new_key, $value);
}
}
// If there is anything to update.
......@@ -211,6 +216,21 @@ class RequestTree {
* The number of replacements made.
*/
public static function replaceAllOccurrences(array $responses, &$input) {
if (is_array($input)) {
$changes = 0;
// Apply the replacement recursively on the array keys and values.
foreach ($input as $key => $value) {
$new_key = $key;
$local_changes = static::replaceAllOccurrences($responses, $new_key);
$local_changes += static::replaceAllOccurrences($responses, $value);
$changes += $local_changes;
if ($local_changes) {
unset($input[$key]);
$input[$new_key] = $value;
}
}
return $changes;
}
// Detect {{/foo#/bar}}
$pattern = '/\{\{\/([^\{\}]+)@(\/[^\{\}]+)\}\}/';
$matches = [];
......
......@@ -8,6 +8,7 @@ use Drupal\subrequests\Blueprint\Parser;
use Drupal\subrequests\Blueprint\RequestTree;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class FrontController extends ControllerBase {
......
......@@ -27,7 +27,7 @@ class JsonSubrequestDenormalizer implements DenormalizerInterface {
throw new \RuntimeException('The provided blueprint contains an invalid subrequest.');
}
$data['path'] = parse_url($data['uri'], PHP_URL_PATH);
$data['query'] = parse_url($data['uri'], PHP_URL_QUERY);
$data['query'] = parse_url($data['uri'], PHP_URL_QUERY) ?: [];
if (isset($data['query']) && !is_array($data['query'])) {
$query = [];
parse_str($data['query'], $query);
......@@ -35,7 +35,6 @@ class JsonSubrequestDenormalizer implements DenormalizerInterface {
}
$data = NestedArray::mergeDeep([
'body' => '',
'query' => [],
'headers' => [],
], $data, parse_url($data['path']));
......@@ -48,13 +47,15 @@ class JsonSubrequestDenormalizer implements DenormalizerInterface {
empty($data['body']) ? $data['query'] : Json::decode($data['body']),
$master_request->cookies ? (array) $master_request->cookies->getIterator() : [],
$master_request->files ? (array) $master_request->files->getIterator() : [],
$master_request->server ? (array) $master_request->server->getIterator() : [],
[],
empty($data['body']) ? '' : $data['body']
);
// Maintain the same session as in the master request.
$request->setSession($master_request->getSession());
// Replace the headers by the ones in the subrequest.
$request->headers = new HeaderBag($data['headers']);
foreach ($data['headers'] as $name => $value) {
$request->headers->set($name, $value);
}
$this::fixBasicAuth($request);
// Add the content ID to the sub-request.
......
......@@ -3,9 +3,8 @@ subrequests.front-controller:
defaults:
_title: 'Returns the trace recorded by test proxy session handlers as JSON'
_controller: '\Drupal\subrequests\Controller\FrontController::handle'
methods: [POST]
methods: [GET, POST]
options:
no_cache: TRUE
_auth: ['basic_auth', 'cookie', 'oauth2']
requirements:
_permission: 'issue subrequests'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment