Skip to content

Commit c5ddde5

Browse files
committed
Add API Gateway request timestamp to server variables
This PR adds support for extracting the original request timestamp from the API Gateway event (`requestContext.requestTimeEpoch` or `requestContext.timeEpoch`) and exposes it in the `$_SERVER` array as `AWS_API_GATEWAY_REQUEST_TIME`. This enables more accurate request duration tracking and allows downstream code to calculate cold start latency by comparing the API Gateway arrival time to actual execution time. The timestamp is derived directly from the incoming event and kept within the request context rather than being set as a global environment variable. This keeps the data scoped to the request lifecycle and avoids side effects across concurrent executions. If preferred, I’m happy to revise this to expose it via an environment variable or another mechanism.
1 parent 33fbe0e commit c5ddde5

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

src/Runtime/Request.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ public static function fromLambdaEvent(array $event, array $serverVariables = []
7979
$serverVariables['SCRIPT_FILENAME'] = $handler;
8080
}
8181

82+
if ($timestamp = self::extractRequestTimestamp($event)) {
83+
$serverVariables['AWS_API_GATEWAY_REQUEST_TIME'] = $timestamp;
84+
}
85+
8286
[$headers, $serverVariables] = static::ensureContentTypeIsSet(
8387
$event, $headers, $serverVariables
8488
);
@@ -266,4 +270,22 @@ protected static function ensureSourceIpAddressIsSet(array $event, array $header
266270

267271
return $headers;
268272
}
273+
274+
/**
275+
* Extracts the time (millisecond epoch) when the request was received by the API Gateway.
276+
*
277+
* @param array $event
278+
*
279+
* @return int|null
280+
*/
281+
protected static function extractRequestTimestamp(array $event)
282+
{
283+
if (! isset($event['requestContext'])) {
284+
return null; // No requestContext, so no timestamp available
285+
}
286+
287+
return $event['requestContext']['requestTimeEpoch'] // REST API (V1)
288+
?? $event['requestContext']['timeEpoch'] // HTTP API (V2)
289+
?? null; // ELB doesn't add a timestamp
290+
}
269291
}

tests/Unit/FpmRequestTest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@
22

33
namespace Laravel\Vapor\Tests\Unit;
44

5+
use Carbon\Carbon;
56
use Laravel\Vapor\Runtime\Fpm\FpmRequest;
67
use Mockery;
78
use PHPUnit\Framework\TestCase;
89

910
class FpmRequestTest extends TestCase
1011
{
12+
public function setUp(): void
13+
{
14+
parent::setUp();
15+
16+
Carbon::setTestNow('2021-01-01 00:00:00');
17+
}
18+
1119
protected function tearDown(): void
1220
{
1321
Mockery::close();
@@ -182,4 +190,41 @@ public function test_request_content_length_is_numeric()
182190

183191
$this->assertSame(0, $request->getContentLength());
184192
}
193+
194+
public function test_api_gateway_v1_request_time_is_set()
195+
{
196+
$epoch = Carbon::now()->getPreciseTimestamp(3);
197+
198+
$request = FpmRequest::fromLambdaEvent([
199+
'httpMethod' => 'GET',
200+
'requestContext' => [
201+
'requestTimeEpoch' => $epoch,
202+
],
203+
]);
204+
205+
$this->assertSame($epoch, $request->serverVariables['AWS_API_GATEWAY_REQUEST_TIME']);
206+
}
207+
208+
public function test_api_gateway_v2_request_time_is_set()
209+
{
210+
$epoch = Carbon::now()->getPreciseTimestamp(3);
211+
212+
$request = FpmRequest::fromLambdaEvent([
213+
'httpMethod' => 'GET',
214+
'requestContext' => [
215+
'timeEpoch' => $epoch,
216+
],
217+
]);
218+
219+
$this->assertSame($epoch, $request->serverVariables['AWS_API_GATEWAY_REQUEST_TIME']);
220+
}
221+
222+
public function test_elb_request_time_is_not_set()
223+
{
224+
$request = FpmRequest::fromLambdaEvent([
225+
'httpMethod' => 'GET',
226+
]);
227+
228+
$this->assertArrayNotHasKey('AWS_API_GATEWAY_REQUEST_TIME', $request->serverVariables);
229+
}
185230
}

0 commit comments

Comments
 (0)