CloudFrontFunctionを用いクッキーの情報をurlクエリに追加する

#1. はじめに
　つい先日、cloudfonrtにCLoudFrontFunctionsという機能が実装されました。確か今年の5月ぐらいだったと記憶してます。
　丁度都合よくCloudFrontにアクセスしたURLやクエリの変更処理を行いたかったため、是非もないといった具合で早速使用してみました。

#2. Lambda@Edge と CloudFrontFunctions の違い
　AWSには元々Lambda@Edgeという機能が備わっています。Lambdaは様々なトリガーから起動することができます。（私はAPI Gatewayのトリガーしか用いたことがありませんが）Lambda@EdgeとはCloudFrontへのアクセス、つまりリクエストとレスポンスをトリガーに起動するLambdaになります。リクエスト、レスポンスの情報を処理・操作することでURLやヘッダー、様々なものを操作することができます。
　CloudFrontFunctionsでは、このLambda Edgeよりも手前で、より簡単に、よりリーズナブルにリクエスト、レスポンスを処理することができます。ただし、Lambda@Edgeと比べて機能に制限があります。詳しくは[こちら](https://dev.classmethod.jp/articles/amazon-cloudfront-functions-release/)をご覧ください。

#3. CloudFrontFunctionsの作成
　それでは早速CloudFrontFunctionsを作成しましょう。CloudFrontのコンソールを開き、仰々しく「NEW」が添えられているFunctionsをクリックします。そしてCreate functionを押しましょう。
![スクリーンショット 2021-07-29 153005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674265/1f8822de-5416-04d5-09df-04f0985b5027.png)
　名前と説明の入力を促されるので適当に入力しましょう。
　正常に作成できればこのような画面が表示されていると思います。
![スクリーンショット 2021-07-29 153754.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674265/a1ea7617-edaf-95b1-27b6-bb95fb1b66c9.png)
　この画面ではそれぞれ以下のようにCloudFrontFunctionsを操作することができます。
####Build
　このページではFunctionのコードそのものを編集することができます。「Development」と「Live」がありますが、Liveが実際にCloudFrontDistributionと紐づいているコードでDevelopmentがテスト用のコードになります。「Sava Change」のボタンがありますが、こちらのボタンを押してもLiveのコードが変わることはありません。「Test」ページでテストを実行する際や「Publish」ページで関数を更新する時に、「Sava Change」ボタンで保存された最新のコードが実行・更新されます。

####Test
 こちらのページではBuildで保存したコードのテストを実行することができます。テストを実行する際にリクエストやレスポンスのヘッダー、クッキー、クエリを好きに設定することができます。これで、より本番環境に近い形でテストを実行できます。また、コードの方でconsole関数を用いて出力を行えばその中身も見ることができます。

####Publish
　こちらのページではBuildで保存したコードをCloudFrontDistributionに紐づけ発行することができます。「Add association」で紐づけるDistributionを選択し、「Publish function」でコードの発行を行うことができます。

#4. コードの編集
###URLの変更
　最終的にはクッキーで取得した情報をクエリに追加したいのですが、ひとまずURLをログイン画面に飛ばすことを想定しいじってみます。今回はURLの変更ですのでイベントタイプは「Viewer Request」を利用します。
　※ちなみにCLoudFrontFunctionsのjavascriptは少し古いversionになるためconstやletはまだ対応していません。

```javascript
function handler(event) {
    var request = event.request;
    var host = request.headers.host.value;
    
    var response = {
        statusCode: 302,
        statusDescription: 'Found',
        headers: {
            "location": { "value": 'https://host/login/index.html' }
        }
    }
    return response
}
```

　CloudFrontFunctionが実行されるとhandler関数がeventを受け取って実行されます。eventの中にはヘッダーの情報等が含まれておりクッキー・クエリ・IPアドレス等がここから確認できます。responseでステータスコードやヘッダーを指定すると変更が加えられた状態でDistributionにアクセスされます。
　今回はログイン画面へのリダイレクトになりますので、ステータスコードは302、locationヘッダーにログインページのURLを指定します。hostで取得してる値は実際にアクセスしてるURLになります。DistributionのDomain name ではなく Alternate domain names に該当する可能性もありますのでご注意ください。

　せっかくですのでこのコードでテストを実行してみましょう。BuildページでSava changeボタンを押し、コードを保存します。また、現状ヘッダの中にはhostヘッダーはなく、値を取得することができないため、Testページでヘッダーの追加を行います。TestページのAdd Headerボタンを押し、hostヘッダの中に任意の値を挿入しましょう。
![スクリーンショット 2021-07-30 091934.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674265/6384b9a2-3f30-da47-cd04-d251fc5a722c.png)
　最後にTest functionを押し、無事にテストできたことを確認してください。

###クッキーを取得しクエリに追加
　クッキーを取得し、それをクエリに追加するコードを書いてみましょう。

```javascript
function handler(event) {
    var request = event.request;
    var cookies = event.request.cookies;
    
    var cookieTest = cookies['cookieTest']['value'];
    var response = {
        statusCode: 302,
        statusDescription: 'Found',
        headers: {
            "location": { "value": 'https://'+host+'/?cookieTest='+cookieTest }
        }
    }
    return response
}
```
　先ほどのhostと同じ要領でクッキーを取得することができます。この関数によりクエリを追加した状態でリダイレクトできるようになりました。Testを行うにはTestページでクッキーにcookieTestを追加して行います。

###条件の追加
　ただし、この関数では問題があります。CloudFrontFunctionはリクエストを受け取ると実行されてしますのでこのままでは下記のように無限ループに陥ってしまいます。

CLoudFrontDistributionにアクセス
↓
CloudFrontFunctionsによりクエリを追加してリダイレクト
↓
CLoudFrontDistributionにアクセス
↓
CloudFrontFunctionsによりクエリを追加してリダイレクト
↓
・・・

この無限ループを防ぐため、以下の条件をこの関数に追加しましょう。

条件１　クッキーにcookieTestが含まれていなかったらログインページにリダイレクト
条件２　クエリにcookieTestが含まれていたら特に処理をせずにステータスコード200でアクセス
条件３　条件１、条件２を満たさない時、クッキーcookieTestをクエリに追加

```javascript
function handler(event) {
    var request = event.request;
    var cookies = event.request.cookies;
    var host = request.headers.host.value
    
    if(!cookies['cookieTest']){
        var response = {
            statusCode: 302,
            statusDescription: 'Found',
            headers: {
                "location": { "value": 'https://'+host+'/login/index.html' }
            }
        }
        return response;
    }else if(request.querystring['cookieTest']){
        return request;
    }else{
        var cookieTest = cookies['cookieTest']['value'];
        var response = {
            statusCode: 302,
            statusDescription: 'Found',
            headers: {
                "location": { "value": 'https://'+host+'/?cookieTest='+cookieTest }
            }
        }
        return response
    }
}
```

#5. おわりに
　いかがだったでしょうか。現状、CloudFrontFunctionsに関する情報はあまり多くなく、自分でコードを書くのに苦労しました。この記事が少しでも皆様の役に立てば幸いです。