Skip to content

Commit 442ee58

Browse files
committed
feat: add request and trace id to http requests and responses
1 parent 3e0b602 commit 442ee58

8 files changed

Lines changed: 615 additions & 11 deletions

File tree

phpstan-baseline.neon

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ parameters:
3737
path: src/Lambda/Response/Http/AbstractErrorHttpResponse.php
3838

3939
-
40-
message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#'
41-
identifier: function.alreadyNarrowedType
40+
message: '#^Method Ymir\\Runtime\\Lambda\\Response\\Http\\HttpResponse\:\:withHeader\(\) has parameter \$value with no type specified\.$#'
41+
identifier: missingType.parameter
4242
count: 1
43-
path: src/Lambda/Response/Http/StaticFileResponse.php
43+
path: src/Lambda/Response/Http/HttpResponse.php
4444

4545
-
4646
message: '#^Method Ymir\\Runtime\\Logger\:\:__construct\(\) has parameter \$level with no type specified\.$#'

src/FastCgi/FastCgiRequest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,32 @@ public function __construct(string $content = '', array $parameters = [])
5050
public static function createFromInvocationEvent(HttpRequestEvent $event, string $scriptFilename): self
5151
{
5252
$content = $event->getBody();
53+
$context = $event->getContext();
5354
$documentRoot = getcwd() ?: '';
5455
$headers = $event->getHeaders();
5556
$method = strtoupper($event->getMethod());
5657
$path = $event->getPath();
5758
$pathInfo = '';
5859
$port = $headers['x-forwarded-port'][0] ?? 80;
5960
$queryString = $event->getQueryString();
61+
$requestId = $context->getRequestId();
6062
$scriptName = str_replace($documentRoot, '', $scriptFilename);
6163
$self = $scriptName.$path;
64+
$traceId = $context->getTraceId();
6265

6366
// Parse path information using same regex for nginx with "fastcgi_split_path_info"
6467
if (1 === preg_match('%^(.+\.php)(/.+)$%i', $path, $matches)) {
6568
$pathInfo = $matches[2];
6669
$self = $matches[0];
6770
}
6871

72+
$headers['x-request-id'] = $headers['x-request-id'] ?? [$requestId];
73+
$headers['x-trace-id'] = $headers['x-trace-id'] ?? [$traceId];
74+
$headers['x-amzn-request-id'] = $headers['x-amzn-request-id'] ?? [$requestId];
75+
$headers['x-amzn-trace-id'] = $headers['x-amzn-trace-id'] ?? [$traceId];
76+
6977
$parameters = [
78+
'AWS_REQUEST_ID' => $requestId,
7079
'DOCUMENT_ROOT' => $documentRoot,
7180
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
7281
'PATH_INFO' => $pathInfo,
@@ -84,6 +93,7 @@ public static function createFromInvocationEvent(HttpRequestEvent $event, string
8493
'SERVER_PORT' => $port,
8594
'SERVER_PROTOCOL' => $event->getProtocol(),
8695
'SERVER_SOFTWARE' => 'ymir',
96+
'_X_AMZN_TRACE_ID' => $traceId,
8797
];
8898

8999
$parameters['REQUEST_URI'] = empty($queryString) ? $path : $path.'?'.$queryString;

src/Lambda/Response/Http/HttpResponse.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public function getResponseData(): array
9696

9797
// Compress the response body if we hit the 6MB Lambda payload limit and the response supports it.
9898
if ($this->shouldCompressResponse($body, $headers)) {
99+
// Would be better if we could fallback if gzencode returns false. Right now, this would return a blank page
99100
$body = (string) gzencode($body, 9);
100101
$headers['Content-Encoding'] = ['gzip'];
101102
$headers['Content-Length'] = [strlen($body)];
@@ -129,6 +130,16 @@ public function isCompressible(): bool
129130
return $this->compressible;
130131
}
131132

133+
/**
134+
* Return an instance with the given value replacing the given header.
135+
*/
136+
public function withHeader(string $name, $value): self
137+
{
138+
$this->headers[$name] = (array) $value;
139+
140+
return $this;
141+
}
142+
132143
/**
133144
* Get the HTTP response headers properly formatted for a Lambda response.
134145
*/

src/RuntimeApiClient.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Ymir\Runtime\Lambda\InvocationEvent\InvocationEventFactory;
1919
use Ymir\Runtime\Lambda\InvocationEvent\InvocationEventInterface;
2020
use Ymir\Runtime\Lambda\Response\Http\ForbiddenHttpResponse;
21+
use Ymir\Runtime\Lambda\Response\Http\HttpResponse;
2122
use Ymir\Runtime\Lambda\Response\ResponseInterface;
2223

2324
/**
@@ -104,12 +105,23 @@ public function sendInitializationError(\Throwable $error): void
104105
*/
105106
public function sendResponse(InvocationEventInterface $event, ResponseInterface $response): void
106107
{
108+
$requestId = $event->getContext()->getRequestId();
109+
110+
if ($response instanceof HttpResponse) {
111+
$response
112+
->withHeader('X-Request-ID', $requestId)
113+
->withHeader('X-Amzn-RequestId', $requestId);
114+
}
115+
107116
$data = $response->getResponseData();
108117

109118
// Lambda has a 6MB response payload limit. Send an error if we hit this limit instead of getting an
110119
// error from the API gateway.
111120
if (!empty($data['body']) && mb_strlen((string) $data['body']) >= 6000000) {
112-
$data = (new ForbiddenHttpResponse('Response Too Large'))->getResponseData();
121+
$data = (new ForbiddenHttpResponse('Response Too Large'))
122+
->withHeader('X-Request-ID', $requestId)
123+
->withHeader('X-Amzn-RequestId', $requestId)
124+
->getResponseData();
113125
}
114126

115127
$this->sendData($data, sprintf('invocation/%s/response', $event->getContext()->getRequestId()));

0 commit comments

Comments
 (0)