public function testVerifyRequestStackPushPopDuringHandle() { $request = new Request(); $stack = $this->getMock('Symfony\\Component\\HttpFoundation\\RequestStack', array('push', 'pop')); $stack->expects($this->at(0))->method('push')->with($this->equalTo($request)); $stack->expects($this->at(1))->method('pop'); $dispatcher = new EventDispatcher(); $kernel = new AsyncHttpKernel($dispatcher, $this->getResolver(), $stack); $kernel->handle($request, HttpKernelInterface::MASTER_REQUEST); }
public function onKernelController(FilterControllerEvent $event) { if (!$event->isMasterRequest()) { return; } $request = $event->getRequest(); if (!$request->attributes->has('_api')) { return; } /** @var Api $api */ $api = $request->attributes->get('_api'); $subRequests = $this->getSubRequests($request); $bulk = $api->isBulkable() && $subRequests; $stream = ($api->isStreamable() || $bulk) && $this->shouldStream($request); if (!$bulk && !$stream) { $request->attributes->set('stream', $this->noop); return; } // This listener will unwind/spread the calls, so don't trigger other Api listeners. $request->attributes->remove('_api'); if ($stream) { $headers = ['content-type' => 'application/json; boundary=NL', 'x-accel-buffering' => 'no']; } else { $headers = ['content-type' => 'application/json']; } if ($subRequests) { $event->setController(function () use($request, $headers, $subRequests, $stream) { return new StreamedResponse(function () use($request, $subRequests, $stream) { $promises = []; foreach ($subRequests as $i => $requestParams) { // Forward the query string without the 'payload', and put all the parameters in the body. $query = $request->query->all(); if (isset($query['payload'])) { unset($query['payload']); } $subRequest = $request->duplicate([], $requestParams); // Also force-make it a POST request, so it can contain a body. $subRequest->setMethod('POST'); $subRequest->attributes->set('stream', $stream ? $this->createStreamer($i) : $this->noop); /* @var PromiseInterface $promise */ $promises[] = $promise = $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true, false); if ($stream) { $streamer = $this->createStreamer($i); $promise->then(function (Response $response) use($streamer) { $streamer($response->getContent(), true); }); } } $responses = \GuzzleHttp\Promise\all($promises)->wait(); if (!$stream) { echo '['; for (reset($responses); $response = current($responses); next($responses)) { echo $response->getContent(); if (current($responses)) { echo ','; } } echo ']'; } }, 200, $headers); }); } else { $event->setController(function () use($request, $headers) { return new StreamedResponse(function () use($request) { $request->attributes->set('stream', $this->createStreamer()); // We duplicate the request because the profiler component's token keeps a reference to the parent request's token, // creating an infinite loop when attempting to display profiler info. $response = $this->httpKernel->handle($request->duplicate(), HttpKernelInterface::SUB_REQUEST, true, true); // The streamer outputs a new line as the ending delimiter. In single action calls, the ending line should be // the actual response without a new line at the end. That's why streaming single calls have a resulting // response, but bulk calls don't have one. echo $response->getContent(); }, 200, $headers); }); } }