LambdaFunctionURLsを使って30s越えのリクエストを検証する

# まえがき
前回のLambda エフェメラルストレージのアップデートに引き続き、今度はAPIGateway無くてもAPI経由でLambdaが起動できるようになりました。（2022.4.5）
このアップデートによりWebhookの接続先を直接指定できたり、ExpressやFlaskなどのバックエンドframeworkをLambda上に立て、Proxyさせるなどが簡単にできるようになります。
今までもAPIGatewayのProxy機能を利用したり、ALBからLambdaを呼び出すことも可能ではありましたが、よりシンプルにできるにようになりました。

# 概要
既にServerlessFrameworkでも対応されているのですが、ServerlessFrameworkのブログに、それぞれの役割が載っており、とても分かりやすかったです。

[AWS Lambda Function URLs with Serverless Framework 抜粋](https://www.serverless.com/blog/aws-lambda-function-urls-with-serverless-framework)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511411/4390a2fb-ff00-68d5-a7c9-1b83aaa1f27e.png)

特にMaximum HTTP response timeoutのレスポンス29秒タイムアウトの制限がなく、いとのことで調べてみようと思います。

# 設定
マネージメントコンソール上の「設定」から「一般設定」に「関数 URL - 新規」の項目が増えており、
こちらから作成すること可能ですが、既にServerless frameworkで対応されていましたので、こちらからデプロイしてみたいと思います。
※「LambdaFunctionsUrls」の機能を使用するにはServerless framework 3.12.0以上を使用する必要があります。

https://github.com/serverless/serverless/releases/tag/v3.12.0

まずはserverless projectを作成します。

```bash
# serverless framework version up
$ npm install -g serverless
52 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
$ serverless --version
Framework Core: 3.12.0
Plugin: 6.2.1
SDK: 4.3.2

$ serverless create --template aws-python3 --path LambdaFunctionUrlTest

✔ Project successfully created in "LambdaFunctionUrlTest" from "aws-python3" template (5s)
cd LambdaFunctionUrlTest
```

つぎにserverless.ymlを開き、urlsを追加します

```serverless.yml
functions:
  hello:
    handler: handler.hello
    url: true # 追加
```

IAM認証を使用する場合は以下のように修正します
``` serverlss.yml
    url:
      authorizer: aws_iam
```

デフォルトではCROSが許可されていません。許可する場合は以下のように修正します
```serverlss.yml
    url:
      # Configure CORS in details:
      cors:
        allowCredentials: …
        allowedHeaders: …
        allowedMethods: …
        allowedOrigins: …
        exposedResponseHeaders: …
        maxAge: ….
```

デプロイをします
デプロイ完了するとendpoint urlが表示されます
（外部公開されてしまうため、ダミーのURLを載せています）
```bash
$ serverless deploy

Deploying LambdaFunctionUrlsTest to stage dev (ap-northeast-1)

✔ Service deployed to stack LambdaFunctionUrlsTest-dev (49s)

endpoint: https://xxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/
functions:
  hello: LambdaFunctionUrlsTest-dev-hello (389 B)
```

マネージメントコンソールから「Lambda」 -> 「一般タブ」 -> 「関数 URL - 新規」から関数URLを確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511411/32017809-ef19-617c-6bef-aaa6af2f5d00.png)

次にAPIをcurlから実行してみます。
```bash
curl -X POST https://xxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/test
{
  "message": "Go Serverless v1.0! Your function executed successfully!",
  "input": {
    "version": "2.0",
    "routeKey": "$default",
    "rawPath": "/test",
    "rawQueryString": "",
    "headers": {
      "x-amzn-trace-id": "Root=1-624f6177-1a0368a27e5fa2960e347431",
      "x-forwarded-proto": "https",
      "host": "xxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws",
      "x-forwarded-port": "443",
      "x-forwarded-for": "240d:1a:6b8:1000:f440:a4c9:405:953c",
      "accept": "*/*",
      "user-agent": "curl/7.77.0"
    },
    "requestContext": {
      "accountId": "anonymous",
      "apiId": "xxxxxxxxxxxxxxxxxxxxxxxxx",
      "domainName": "xxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws",
      "domainPrefix": "jnizrnojzkxxevhncd3cwz5ode0leyol",
     "http": {
       "method": "POST",
       "path": "/test",
       "protocol": "HTTP/1.1",
       "sourceIp": "240d:1a:6b8:1000:f440:a4c9:405:953c",
       "userAgent": "curl/7.77.0"
    },
    "requestId": "823d3d28-62b8-4302-b2e9-5c0ef17385d7",
    "routeKey": "$default",
    "stage": "$default",
    "time": "07/Apr/2022:22:11:03 +0000",
    "timeEpoch": 1649369463756
  },
  "isBase64Encoded": false
}
```
リクエストのpathはlambdaのパラメータの`http.path`、methodは`http.method`に割り当たることが確認できました。

# 検証
handler.pyにsleep(60)を追加し、再度APIを実行してみます。

```bash
$ curl -so /dev/nul -w "time_total: %{time_total}\n" https://xxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/test
time_total: 60.530532
```

29秒を超えてもエラーにならないことが確認できました。

一応sleep(899)に変更し15分ギリギリのリクエストも試してみます。
```bash
curl -so /dev/nul -w "time_total: %{time_total}\n" https://xxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/test
time_total: 1737.450523
```
time_totalがおかしいですが、リクエスト中放置した際にPCがスリープされた時間も含まれているようです。CloudWatch Logs上で確認します。問題なくレスポンスが返ってきています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511411/7c9ff152-5e39-97aa-36a9-f83faeda6447.png)

次にLambdaのタイムアウトを超えた場合に、どのようなエラーが返ってくるか確認します。

```bash
$ curl -so /dev/nul -w "time_total: %{time_total}\n" https://xxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/test
< HTTP/1.1 502 Bad Gateway
< Date: Sat, 09 Apr 2022 22:44:51 GMT
< Content-Type: application/json
< Content-Length: 21
< Connection: keep-alive
< x-amzn-RequestId: 18337225-5086-4559-ae1b-8c0eabba9b2c
< X-Amzn-Trace-Id: root=1-62520c27-0d32bdbb64658b1d19185c0b;sampled=0
< 
Internal Server Error
```
status codeは502 Bad Gatewayが返ってくることが確認できました。
メッセージは`Internal Server Error`となっていることに注意です。

# まとめ
LambdaFunctionURLsを使うことで、29秒超えのリクエストが追加費用が必要なく簡単にできるようになりました。
いままでAPIGatewayの29秒制限によりに同期的なAPIを非同期に変更した場合など、アーキテクチャを変更せずURLsを発行して一時的に回避するなどができそうです。
次はbackend frameworkを利用してLambda Function URLsの機能を実装してみたいと思います。