diff --git a/demo/http.php b/demo/http.php index 5baae8b64..7836e377e 100644 --- a/demo/http.php +++ b/demo/http.php @@ -6,4 +6,23 @@ sleep(10); } +if (isset($_GET['img'])) { + $fp = fopen('https://bref.sh/img/logo-small.png', 'rb'); + header('Content-Type: image/png'); + fpassthru($fp); + exit(0); +} + +if (isset($_GET['json'])) { + header('Content-Type: application/json'); + echo json_encode(['Hello' => '🌍']); + exit(0); +} + +if (isset($_GET['weird'])) { + header('Content-Type: foo/bar'); + echo 'Hello 🌍'; + exit(0); +} + echo 'Hello world!'; diff --git a/docs/runtimes/http.md b/docs/runtimes/http.md index a599dc799..d1a67feed 100644 --- a/docs/runtimes/http.md +++ b/docs/runtimes/http.md @@ -112,6 +112,37 @@ Lambda and API Gateway are only used for executing code. Serving assets via PHP Deploying a website and serving assets (e.g. CSS, JavaScript, images) is covered in [the "Websites" documentation](/docs/websites.md). +In some cases however, you will need to serve images (or other assets) via PHP. One example would be if you served generated images via PHP. In those cases, you need to read the [Binary response](#binary-responses) section below. + +## Binary responses + +By default API Gateway **does not support binary HTTP responses** like images, PDF, binary files… To achieve this, you need to enable the option for binary responses in `serverless.yml`: + +```yaml +provider: + # ... + apiGateway: + binaryMediaTypes: + - '*/*' +``` + +This will make API Gateway support binary responses for all responses. Your application can now return binary responses as usual. + +**However, you must define a `Content-Type` header on binary responses.** If you don't, you may get the following error: *Failed encoding Lambda JSON response: Malformed UTF-8 characters, possibly incorrectly encoded*. [Symfony's helpers](https://symfony.com/doc/current/components/http_foundation.html#serving-files) or [Laravel's helpers](https://laravel.com/docs/5.8/responses#file-downloads) will take care of that for you. If you don't use them, here are some examples: + +```php +// Vanilla PHP example with a JPEG image response: +header('Content-Type: image/jpeg'); +header('Content-Length: ' . filesize($filename)); +fpassthru(fopen($filename, 'rb')); + +// PSR-7 example: +return $response + ->withHeader('Content-Type', 'image/jpeg') + ->withHeader('Content-Length', (string) filesize($filename)) + ->withBody(new Stream($filename)); +``` + ## Cold starts AWS Lambda automatically destroys Lambda containers that have been unused for 10 to 60 minutes. Warming up a new container can take some time, especially if your package is large or if your Lambda is connected to a VPC. This delay is called [cold start](https://mikhail.io/serverless/coldstarts/aws/). diff --git a/serverless.yml b/serverless.yml index a9ffcc256..c80bf5570 100644 --- a/serverless.yml +++ b/serverless.yml @@ -4,6 +4,9 @@ provider: name: aws runtime: provided region: us-east-2 + apiGateway: + binaryMediaTypes: + - '*/*' plugins: - ./index.js diff --git a/src/Runtime/LambdaRuntime.php b/src/Runtime/LambdaRuntime.php old mode 100644 new mode 100755 index 2fbcbd4c8..e97400e07 --- a/src/Runtime/LambdaRuntime.php +++ b/src/Runtime/LambdaRuntime.php @@ -243,6 +243,19 @@ public function failInitialization(string $message, ?\Throwable $error = null): */ private function postJson(string $url, $data): void { + $contentType = $data['multiValueHeaders']['content-type'][0] ?? null; + /** + * API Gateway does not support binary content in HTTP responses. + * What we do is: + * - if we are certain the response is not binary (either JSON or the content type starts with `text/*`) we don't encode + * - else we encode all other responses to base64 + * API Gateway checks `isBase64Encoded` and decodes what we returned if necessary. + */ + if ($contentType && $contentType !== 'application/json' && strpos($contentType, 'text/') !== 0) { + $data['body'] = base64_encode($data['body']); + $data['isBase64Encoded'] = true; + } + $jsonData = json_encode($data); if ($jsonData === false) { throw new \Exception('Failed encoding Lambda JSON response: ' . json_last_error_msg());