-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from xp-forge/feature/streaming
Implement HTTP response streaming
- Loading branch information
Showing
12 changed files
with
617 additions
and
344 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/main/php/com/amazon/aws/lambda/HttpIntegration.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php namespace com\amazon\aws\lambda; | ||
|
||
use web\{Routing, Environment as WebEnv}; | ||
|
||
/** | ||
* Base class for HTTP APIs with the following implementations: | ||
* | ||
* - `HttpStreaming` for Lambda function URLs with streaming support | ||
* - `HttpApi` for API Gateway and function URLs with buffering | ||
* | ||
* @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html | ||
*/ | ||
abstract class HttpIntegration extends Handler { | ||
protected $tracing; | ||
|
||
/** Creates a new handler with a given lambda environment */ | ||
public function __construct(Environment $environment) { | ||
parent::__construct($environment); | ||
$this->tracing= new Tracing($environment); | ||
} | ||
|
||
/** | ||
* Returns routes. Overwrite this in subclasses! | ||
* | ||
* @param web.Environment $environment | ||
* @return web.Application|web.Routing|[:var] | ||
*/ | ||
public abstract function routes($environment); | ||
|
||
/** @return web.Routing */ | ||
protected final function routing() { | ||
return Routing::cast($this->routes(new WebEnv( | ||
$this->environment->variable('PROFILE') ?? 'prod', | ||
$this->environment->root, | ||
$this->environment->path('static'), | ||
[$this->environment->properties], | ||
[], | ||
$this->tracing | ||
))); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
src/main/php/com/amazon/aws/lambda/HttpStreaming.class.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php namespace com\amazon\aws\lambda; | ||
|
||
use Throwable; | ||
use web\{Error, InternalServerError, Request, Response}; | ||
|
||
/** | ||
* AWS Lambda with AWS function URLs. Uses streaming as this has lower | ||
* TTFB and memory consumption. | ||
* | ||
* @test com.amazon.aws.lambda.unittest.HttpIntegrationTest | ||
* @see https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-features.html#gettingstarted-features-urls | ||
*/ | ||
abstract class HttpStreaming extends HttpIntegration { | ||
|
||
/** @return callable|com.amazon.aws.lambda.Lambda|com.amazon.aws.lambda.Streaming */ | ||
public function target() { | ||
$routing= $this->routing(); | ||
|
||
// Return event handler | ||
return function($event, $stream, $context) use($routing) { | ||
$in= new FromApiGateway($event); | ||
$req= new Request($in); | ||
$res= new Response(new StreamingTo($stream)); | ||
|
||
try { | ||
foreach ($routing->service($req->pass('context', $context)->pass('request', $in->context()), $res) ?? [] as $_) { } | ||
$this->tracing->log($req, $res); | ||
|
||
$res->end(); | ||
} catch (Throwable $t) { | ||
$e= $t instanceof Error ? $t : new InternalServerError($t); | ||
$this->tracing->log($req, $res, $e); | ||
|
||
$res->answer($e->status(), $e->getMessage()); | ||
$res->header('x-amzn-ErrorType', nameof($e)); | ||
$res->send($e->compoundMessage(), 'text/plain'); | ||
} | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php namespace com\amazon\aws\lambda; | ||
|
||
use text\json\{Json, StreamOutput}; | ||
use web\io\Output; | ||
|
||
/** | ||
* Response streaming with HTTP integration | ||
* | ||
* @test com.amazon.aws.lambda.unittest.HttpApiTest | ||
* @see https://github.com/xp-forge/lambda/pull/23#issuecomment-1595720377 | ||
*/ | ||
class StreamingTo extends Output { | ||
const DELIMITER= "\0\0\0\0\0\0\0\0"; | ||
const MIME_TYPE= 'application/vnd.awslambda.http-integration-response'; | ||
const USESTREAM= 'Must use RESPONSE_STREAM'; | ||
|
||
private $stream; | ||
|
||
/** Creates a new streaming response */ | ||
public function __construct(Stream $stream) { | ||
$this->stream= $stream; | ||
$this->stream->use(self::MIME_TYPE); | ||
} | ||
|
||
/** @return web.io.Output */ | ||
public function stream() { return $this; } | ||
|
||
/** | ||
* Begins a request | ||
* | ||
* @param int $status | ||
* @param string $message | ||
* @param [:string[]] $headers | ||
*/ | ||
public function begin($status, $message, $headers) { | ||
$meta= [ | ||
'statusCode' => $status, | ||
'statusDescription' => $message, | ||
'headers' => [], | ||
'body' => self::USESTREAM, | ||
]; | ||
foreach ($headers as $name => $values) { | ||
if ('Set-Cookie' === $name) { | ||
$meta['cookies']= $values; | ||
} else { | ||
$meta['headers'][$name]= current($values); | ||
} | ||
} | ||
|
||
$this->stream->write(json_encode($meta)); | ||
$this->stream->write(self::DELIMITER); | ||
} | ||
|
||
/** | ||
* Writes the bytes | ||
* | ||
* @param string $bytes | ||
* @return void | ||
*/ | ||
public function write($bytes) { | ||
$this->stream->write($bytes); | ||
} | ||
|
||
/** @return void */ | ||
public function finish() { | ||
$this->stream->end(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php namespace com\amazon\aws\lambda; | ||
|
||
use web\Logging; | ||
use web\logging\ToFunction; | ||
|
||
class Tracing extends Logging { | ||
|
||
public function __construct(Environment $environment) { | ||
parent::__construct(new ToFunction(function($request, $response, $error= null) use($environment) { | ||
$query= $request->uri()->query(); | ||
$environment->trace(sprintf( | ||
'TRACE [%s] %d %s %s %s', | ||
$request->value('context')->traceId, | ||
$response->status(), | ||
$request->method(), | ||
$request->uri()->path().($query ? '?'.$query : ''), | ||
$error ? $error->toString() : '' | ||
)); | ||
})); | ||
} | ||
} |
Oops, something went wrong.