Skip to content

Commit

Permalink
Passes both first request and first response cookies to internal request
Browse files Browse the repository at this point in the history
This should handle any encrypted request as well.

Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
tonysm and taylorotwell committed Nov 11, 2021
1 parent 13a40c9 commit 23127fc
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 4 deletions.
34 changes: 30 additions & 4 deletions src/Http/Middleware/TurboMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpFoundation\Cookie;
use Tonysm\TurboLaravel\Facades\Turbo as TurboFacade;
use Tonysm\TurboLaravel\Turbo;

Expand All @@ -20,6 +21,13 @@ class TurboMiddleware
/** @var \Tonysm\TurboLaravel\Http\Middleware\RouteRedirectGuesser */
private $redirectGuesser;

/**
* Encrypted cookies to be added to the internal requests following redirects.
*
* @var array
*/
private array $encryptedCookies;

public function __construct(RouteRedirectGuesser $redirectGuesser)
{
$this->redirectGuesser = $redirectGuesser;
Expand All @@ -32,6 +40,8 @@ public function __construct(RouteRedirectGuesser $redirectGuesser)
*/
public function handle($request, Closure $next)
{
$this->encryptedCookies = $request->cookies->all();

if ($this->turboNativeVisit($request)) {
TurboFacade::setVisitingFromTurboNative();
}
Expand Down Expand Up @@ -63,14 +73,24 @@ private function turboResponse($response, Request $request)
return $response;
}

// We get the response's encrypted cookies and merge them with the
// encrypted cookies of the first request to make sure that are
// sub-sequent request will use the most up-to-date values.

$responseCookies = collect($response->headers->getCookies())
->mapWithKeys(fn (Cookie $cookie) => [$cookie->getName() => $cookie->getValue()])
->all();

$this->encryptedCookies = array_replace_recursive($this->encryptedCookies, $responseCookies);

// When throwing a ValidationException and the app uses named routes convention, we can guess
// the form route for the current endpoint, make an internal request there, and return the
// response body with the form over a 422 status code, which is better for Turbo Native.

if ($response->exception instanceof ValidationException && ($formRedirectUrl = $this->getRedirectUrl($request, $response))) {
$response->setTargetUrl($formRedirectUrl);

return tap($this->handleRedirectInternally($this->kernel(), $request, $response), function () use ($request) {
return tap($this->handleRedirectInternally($request, $response), function () use ($request) {
App::instance('request', $request);
Facade::clearResolvedInstance('request');
});
Expand All @@ -94,28 +114,34 @@ private function kernel(): Kernel
}

/**
* @param Kernel $kernel
* @param Request $request
* @param Response $response
*
* @return Response
*/
private function handleRedirectInternally($kernel, $request, $response)
private function handleRedirectInternally($request, $response)
{
$kernel = $this->kernel();

do {
$response = $kernel->handle(
$request = $this->createRequestFrom($response->headers->get('Location'), $request)
);
} while ($response->isRedirect());

return $response->setStatusCode(422);
if ($response->isOk()) {
$response->setStatusCode(422);
}

return $response;
}

private function createRequestFrom(string $url, Request $baseRequest)
{
$request = Request::create($url, 'GET');

$request->headers->replace($baseRequest->headers->all());
$request->cookies->replace($this->encryptedCookies);

return $request;
}
Expand Down
36 changes: 36 additions & 0 deletions tests/Http/Middleware/TurboMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,40 @@ public function only_guess_route_on_resource_routes()
->assertRedirect(route('app.login'))
->assertStatus(303);
}

public function usesRoutesWhichExceptCookies()
{
Route::get('posts/create', function () {
$firstRequestCookie = request()->cookie('my-cookie', 'no-cookie');

$responseCookie = request()->cookie('response-cookie', 'no-cookie');

return response(sprintf('Request Cookie: %s; Response Cookie: %s', $firstRequestCookie, $responseCookie));
})->name('posts.create');

Route::post('posts', function () {
$exception = ValidationException::withMessages([
'title' => ['Title cannot be blank.'],
]);

$exception->response = redirect()->to('/')->withCookie('response-cookie', 'response-cookie-value');

throw $exception;
})->name('posts.store')->middleware(TurboMiddleware::class);
}

/**
* @test
* @define-route usesRoutesWhichExceptCookies
*/
public function passes_the_request_cookies_to_the_internal_request()
{
$this->withHeaders([
'Accept' => sprintf('%s, text/html, application/xhtml+xml', Turbo::TURBO_STREAM_FORMAT),
])
->withUnencryptedCookie('my-cookie', 'test-value')
->post(route('posts.store'))
->assertSee('Request Cookie: test-value; Response Cookie: response-cookie-value')
->assertStatus(422);
}
}

0 comments on commit 23127fc

Please sign in to comment.