Skip to content

Commit

Permalink
fix: return an error page if the response is too large for lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
carlalexander committed Feb 10, 2022
1 parent 9e24f34 commit 2010f68
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/Lambda/Response/ForbiddenHttpResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

/*
* This file is part of Ymir PHP Runtime.
*
* (c) Carl Alexander <support@ymirapp.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Ymir\Runtime\Lambda\Response;

/**
* A Lambda response for a 403 HTTP response.
*/
class ForbiddenHttpResponse extends AbstractErrorHttpResponse
{
/**
* Constructor.
*/
public function __construct(string $message = 'Forbidden', string $templatesDirectory = '')
{
parent::__construct($message, 403, $templatesDirectory);
}
}
11 changes: 10 additions & 1 deletion src/Lambda/RuntimeApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use Ymir\Runtime\Lambda\InvocationEvent\InvocationEventFactory;
use Ymir\Runtime\Lambda\InvocationEvent\InvocationEventInterface;
use Ymir\Runtime\Lambda\Response\ForbiddenHttpResponse;
use Ymir\Runtime\Lambda\Response\ResponseInterface;
use Ymir\Runtime\Logger;

Expand Down Expand Up @@ -102,7 +103,15 @@ public function sendInitializationError(\Throwable $error)
*/
public function sendResponse(InvocationEventInterface $event, ResponseInterface $response)
{
$this->sendData($response->getResponseData(), "invocation/{$event->getId()}/response");
$data = $response->getResponseData();

// Lambda has a 6MB response payload limit. Send an error if we hit this limit instead of getting an
// error from the API gateway.
if (!empty($data['body']) && mb_strlen((string) $data['body']) >= 6000000) {
$data = (new ForbiddenHttpResponse('Response Too Large'))->getResponseData();
}

$this->sendData($data, "invocation/{$event->getId()}/response");
}

/**
Expand Down
47 changes: 47 additions & 0 deletions tests/Unit/Lambda/Response/ForbiddenHttpResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

/*
* This file is part of Ymir PHP Runtime.
*
* (c) Carl Alexander <support@ymirapp.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Ymir\Runtime\Tests\Unit\Lambda\Response;

use PHPUnit\Framework\TestCase;
use Ymir\Runtime\Lambda\Response\ForbiddenHttpResponse;

/**
* @covers \Ymir\Runtime\Lambda\Response\ForbiddenHttpResponse
*/
class ForbiddenHttpResponseTest extends TestCase
{
public function testGetDataWhenTemplateFound()
{
$this->assertSame([
'isBase64Encoded' => true,
'statusCode' => 403,
'body' => 'PCEtLQoKX18gIF9fICAgICAgICAgIF8KXCBcLyAvX19fIF9fXyAgKF8pX19fXwogXCAgLyBfXyBgX18gXC8gLyBfX18vCiAvIC8gLyAvIC8gLyAvIC8gLwovXy9fLyAvXy8gL18vXy9fLwoKLS0+Cgo8IURPQ1RZUEUgaHRtbD4KPGh0bWwgbGFuZz0iZW4iPgogICAgPGhlYWQ+CiAgICAgICAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgogICAgICAgIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MSI+CgogICAgICAgIDx0aXRsZT40MDMgfCBmb288L3RpdGxlPgoKICAgICAgICA8IS0tIEZvbnRzIC0tPgogICAgICAgIDxsaW5rIHJlbD0iZG5zLXByZWZldGNoIiBocmVmPSJodHRwczovL3JzbXMubWUvIj4KICAgICAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vcnNtcy5tZS9pbnRlci9pbnRlci5jc3MiIC8+CgogICAgICAgIDwhLS0gU3R5bGVzIC0tPgogICAgICAgIDxzdHlsZT4KICAgICAgICAgICAgaHRtbCwgYm9keSB7CiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMTkxOTFlOwogICAgICAgICAgICAgICAgY29sb3I6ICM4MmMzMzk7CiAgICAgICAgICAgICAgICBmb250LWZhbWlseTogJ0ludGVyIHZhcicsIHVpLXNhbnMtc2VyaWYsIHN5c3RlbS11aSwgLWFwcGxlLXN5c3RlbTsKICAgICAgICAgICAgICAgIGZvbnQtd2VpZ2h0OiAzMDA7CiAgICAgICAgICAgICAgICBoZWlnaHQ6IDEwMHZoOwogICAgICAgICAgICAgICAgbWFyZ2luOiAwOwogICAgICAgICAgICB9CgogICAgICAgICAgICAuZnVsbC1oZWlnaHQgewogICAgICAgICAgICAgICAgaGVpZ2h0OiAxMDB2aDsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLmZsZXgtY2VudGVyIHsKICAgICAgICAgICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgICAgICAgICAgICBkaXNwbGF5OiBmbGV4OwogICAgICAgICAgICAgICAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC5mbGV4LWNlbnRlci1jb2x1bW4gewogICAgICAgICAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgICAgICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjsKICAgICAgICAgICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC5wb3NpdGlvbi1yZWYgewogICAgICAgICAgICAgICAgcG9zaXRpb246IHJlbGF0aXZlOwogICAgICAgICAgICB9CgogICAgICAgICAgICAuY29kZSB7CiAgICAgICAgICAgICAgICBib3JkZXItcmlnaHQ6IDJweCBzb2xpZDsKICAgICAgICAgICAgICAgIGZvbnQtc2l6ZTogMjZweDsKICAgICAgICAgICAgICAgIHBhZGRpbmc6IDAgMTVweCAwIDE1cHg7CiAgICAgICAgICAgICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC5tZXNzYWdlIHsKICAgICAgICAgICAgICAgIGZvbnQtc2l6ZTogMThweDsKICAgICAgICAgICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgICAgICAgfQogICAgICAgIDwvc3R5bGU+CiAgICA8L2hlYWQ+CiAgICA8Ym9keT4KICAgICAgICA8ZGl2IGNsYXNzPSJmbGV4LWNlbnRlci1jb2x1bW4gcG9zaXRpb24tcmVmIGZ1bGwtaGVpZ2h0Ij4KICAgICAgICAgICAgPGRpdiBzdHlsZT0icGFkZGluZy1ib3R0b206IDIwcHg7Ij4KICAgICAgICAgICAgICAgIDxhIGhyZWY9Imh0dHA6Ly95bWlyYXBwLmNvbSI+CiAgICAgICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz0iaC02IHctYXV0byIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIiB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIgaWQ9InN2ZzIzNzA1NiIgdmlld0JveD0iMCAwIDIwMCA1MC4yMDE1NjEiIGhlaWdodD0iNTAuMjAxNTYxIiB3aWR0aD0iMjAwIiB2ZXJzaW9uPSIxLjEiIGlua3NjYXBlOnZlcnNpb249IjEuMC4xIChjNDk3YjAzYywgMjAyMC0wOS0xMCkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c29kaXBvZGk6bmFtZWR2aWV3IHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgYm9yZGVyY29sb3I9IiM2NjY2NjYiIGJvcmRlcm9wYWNpdHk9IjEiIG9iamVjdHRvbGVyYW5jZT0iMTAiIGdyaWR0b2xlcmFuY2U9IjEwIiBndWlkZXRvbGVyYW5jZT0iMTAiIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIiBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE0NDAiIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg1NSIgaWQ9Im5hbWVkdmlldzExOCIgc2hvd2dyaWQ9ImZhbHNlIiBpbmtzY2FwZTp6b29tPSIwLjg0MjQ0NzkyIiBpbmtzY2FwZTpjeD0iMjcwLjM1MDE2IiBpbmtzY2FwZTpjeT0iNjcuODU5OTk3IiBpbmtzY2FwZTp3aW5kb3cteD0iMCIgaW5rc2NhcGU6d2luZG93LXk9IjIzIiBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIwIiBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmcyMzcwNTYiPjwvc29kaXBvZGk6bmFtZWR2aWV3PgogICAgICAgICAgICAgICAgICAgICAgICA8bWV0YWRhdGEgaWQ9Im1ldGFkYXRhMjM3MDYyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxyZGY6cmRmPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjYzp3b3JrIHJkZjphYm91dD0iIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkYzp0eXBlIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiPjwvZGM6dHlwZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2NjOndvcms+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3JkZjpyZGY+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cmRmOnJkZj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2M6d29yayByZGY6YWJvdXQ9IiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIj48L2RjOnR5cGU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY2M6d29yaz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvcmRmOnJkZj4KICAgICAgICAgICAgICAgICAgICAgICAgPC9tZXRhZGF0YT4KICAgICAgICAgICAgICAgICAgICAgICAgPGRlZnMgaWQ9ImRlZnMyMzcwNjAiPjwvZGVmcz4KICAgICAgICAgICAgICAgICAgICAgICAgPGcgaWQ9ImxvZ28tZ3JvdXAiIHRyYW5zZm9ybT0ibWF0cml4KDAuMzY5ODkwNjYsMCwwLDAuMzY5ODkwNjYsLTg5LjM4NDAxOSwtMTE2LjkzNzIzKSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZyBpZD0idGl0bGUiIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDo3MDA7Zm9udC1zaXplOjcycHg7bGluZS1oZWlnaHQ6MTtmb250LWZhbWlseTonTWVlZG9yaSBTYW5zJztmb250LXZhcmlhbnQtbGlnYXR1cmVzOm5vbmU7dGV4dC1hbGlnbjpjZW50ZXI7dGV4dC1hbmNob3I6bWlkZGxlIiBhcmlhLWxhYmVsPSJZTUlSIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBpZD0icGF0aDIzNzA2NCIgc3R5bGU9ImZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtd2VpZ2h0OjcwMDtmb250LXNpemU6NzJweDtsaW5lLWhlaWdodDoxO2ZvbnQtZmFtaWx5OidNZWVkb3JpIFNhbnMnO2ZvbnQtdmFyaWFudC1saWdhdHVyZXM6bm9uZTt0ZXh0LWFsaWduOmNlbnRlcjt0ZXh0LWFuY2hvcjptaWRkbGU7ZmlsbDojYjdkOWEzIiBkPSJNIDQ0Mi4zMDk2Miw5NS4wMTYgViAxMjAgaCAtOSBWIDk1LjAxNiBMIDQxMS4yNzc2Miw2NiBoIDExLjAxNjAxIEwgNDM4LjM0OTYyLDg2LjAxNiA0NTMuMzI1NjIsNjYgaCAxMi4wMjQgeiIgdHJhbnNmb3JtPSJtYXRyaXgoMi41LDAsMCwyLjUsLTc4Ni41NDQyMSwxNTEuNjgpIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggaWQ9InBhdGgyMzcwNjYiIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDo3MDA7Zm9udC1zaXplOjcycHg7bGluZS1oZWlnaHQ6MTtmb250LWZhbWlseTonTWVlZG9yaSBTYW5zJztmb250LXZhcmlhbnQtbGlnYXR1cmVzOm5vbmU7dGV4dC1hbGlnbjpjZW50ZXI7dGV4dC1hbmNob3I6bWlkZGxlO2ZpbGw6IzlhY2Q2YSIgZD0ibSA1MjguNjM1MzcsNjUuNzg0IHYgNTQuMjg4IGwgLTksLTcuMDU2IFYgODQuODY0IGwgLTE4LjE0NCwxNC4xMTIgLTE4LjA3MiwtMTQuMTEyIHYgMjguMTUyIGwgLTkuMDcyLDcuMDU2IFYgNjUuNzg0IGwgMjcuMTQ0LDIxLjE2OCB6IiB0cmFuc2Zvcm09Im1hdHJpeCgyLjUsMCwwLDIuNSwtNzcxLjU0NDIxLDE1MS42OCkiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBpZD0icGF0aDIzNzA2OCIgc3R5bGU9ImZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtd2VpZ2h0OjcwMDtmb250LXNpemU6NzJweDtsaW5lLWhlaWdodDoxO2ZvbnQtZmFtaWx5OidNZWVkb3JpIFNhbnMnO2ZvbnQtdmFyaWFudC1saWdhdHVyZXM6bm9uZTt0ZXh0LWFsaWduOmNlbnRlcjt0ZXh0LWFuY2hvcjptaWRkbGU7ZmlsbDojODJjMzM5IiBkPSJtIDU0My42NzU1LDY2IGggOC45MjggdiA0Ny4wMTYgbCAtOC45MjgsNi45ODQgeiIgdHJhbnNmb3JtPSJtYXRyaXgoMi41LDAsMCwyLjUsLTc1Ni41NDQyMSwxNTEuNjgpIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggaWQ9InBhdGgyMzcwNzAiIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDo3MDA7Zm9udC1zaXplOjcycHg7bGluZS1oZWlnaHQ6MTtmb250LWZhbWlseTonTWVlZG9yaSBTYW5zJztmb250LXZhcmlhbnQtbGlnYXR1cmVzOm5vbmU7dGV4dC1hbGlnbjpjZW50ZXI7dGV4dC1hbmNob3I6bWlkZGxlO2ZpbGw6IzY1YjcwMCIgZD0ibSA2MDkuNTU3NzUsMTIwIGggLTEwLjAwOCBsIC0xMS45NTIsLTIyLjAzMiBoIC0yMC4wMTYgViA4OS4wNCBoIDI1Ljk5MiBxIDEuNjU2LC0wLjA3MiAyLjk1MiwtMC44NjQgMS4xNTIsLTAuNjQ4IDIuMDg4LC0yLjA4OCAwLjkzNiwtMS40NCAwLjkzNiwtNC4xMDQgMCwtMi41OTIgLTAuOTM2LC00LjAzMiAtMC45MzYsLTEuNDQgLTIuMDg4LC0yLjA4OCAtMS4yOTYsLTAuNzkyIC0yLjk1MiwtMC44NjQgaCAtMjUuOTkyIHYgLTkgaCAyNS45OTIgcSA0LjE3NiwwLjIxNiA3LjQ4OCwyLjAxNiAxLjM2OCwwLjc5MiAyLjczNiwxLjk0NCAxLjM2OCwxLjE1MiAyLjM3NiwyLjgwOCAxLjA4LDEuNjU2IDEuNzI4LDMuOTYgMC42NDgsMi4yMzIgMC42NDgsNS4yNTYgMCw0Ljg5NiAtMS41ODQsOC4wNjQgLTEuNTg0LDMuMDk2IC0zLjYsNC44OTYgLTEuOTQ0LDEuNzI4IC0zLjc0NCwyLjM3NiAtMS43MjgsMC42NDggLTIuMDg4LDAuNjQ4IHoiIHRyYW5zZm9ybT0ibWF0cml4KDIuNSwwLDAsMi41LC03NDEuNTQ0MjEsMTUxLjY4KSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPGcgaWQ9InRhZ2xpbmUiIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDo1MDA7Zm9udC1zaXplOjMycHg7bGluZS1oZWlnaHQ6MTtmb250LWZhbWlseTpNb250c2VycmF0O2ZvbnQtdmFyaWFudC1saWdhdHVyZXM6bm9uZTt0ZXh0LWFsaWduOmNlbnRlcjt0ZXh0LWFuY2hvcjptaWRkbGUiPjwvZz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgICAgIDwvc3ZnPgogICAgICAgICAgICAgICAgPC9hPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZsZXgtY2VudGVyIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgIDQwMyAgICAgICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJtZXNzYWdlIiBzdHlsZT0icGFkZGluZzogMTBweDsiPgogICAgICAgICAgICAgICAgICAgIGZvbyAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICA8L2JvZHk+CjwvaHRtbD4KCg==',
'multiValueHeaders' => [
'Content-Type' => ['text/html'],
],
], (new ForbiddenHttpResponse('foo', __DIR__.'/../../../../templates'))->getResponseData());
}

public function testGetResponseDataWhenTemplateNotFound()
{
$this->assertSame([
'isBase64Encoded' => true,
'statusCode' => 403,
'body' => '',
'multiValueHeaders' => [
'Content-Type' => ['text/html'],
],
], (new ForbiddenHttpResponse('foo'))->getResponseData());
}
}

0 comments on commit 2010f68

Please sign in to comment.