- まえがき
- EC2サーバーで稼働していたリダイレクト処理について
- CloudFront Functions でのイベント構造について
- CloudFront Functions でのクエリパラメータの取得方法
- まとめ
- おわりに
まえがき
インフラエンジニアの 冨田(@komitta)です。夏休みの宿題は最終日にやるタイプです。
弊社で運用しているエキテンではEC2環境からコンテナを使った環境への移行を行っています。
今回はEC2サーバーを廃棄するにあたって使用していたリダイレクト処理をCloudFront Functionsに移行したときのお話です。
すんなり終わるかと思っていたら、ハマった箇所があったためその話をしたいと思います。
EC2サーバーで稼働していたリダイレクト処理について
下記要件のリダイレクト処理のためにEC2サーバーで稼働しているApacheのmod_rewriteモジュールを使用してリダイレクトを行っていました。
アクセスURL | リダイレクト先URL |
---|---|
https://example.com/s_12345/?hoge=fuga | https://example.com/shop_12345/?hoge=fuga |
行っている処理の内容は s_12345
から shop_12345
へのパスの書き換えになります。
この通り複雑な処理ではないため、サーバーを新規で構築するのではなく既に稼働しているCloudFront Functions*1を使えば無駄なリソースを用意することなく対応できると考えました。
※Lambda@Edgeも置き換えの選択肢にありましたが要件がシンプルであるため、より軽量に処理することができるClouFront Functionsを選定しています。
CloudFront Functions でのイベント構造について
CloudFront Functions で受け取るイベント構造*2は以下になります。
{ "version": "1.0", "context": { "distributionDomainName": "d111111abcdef8.cloudfront.net", "distributionId": "EDFDVBD6EXAMPLE", "eventType": "viewer-response", "requestId": "EXAMPLEntjQpEXAMPLE_SG5Z-EXAMPLEPmPfEXAMPLEu3EqEXAMPLE==" }, "viewer": { "ip": "198.51.100.11" }, "request": { "method": "GET", "uri": "/media/index.mpd", "querystring": { "ID": { "value": "42" }, "Exp": { "value": "1619740800" }, "TTL": { "value": "1440" }, "NoValue": { "value": "" }, "querymv": { "value": "val1", "multiValue": [ { "value": "val1" }, { "value": "val2,val3" } ] } }, "headers": { "host": { "value": "video.example.com" }, "user-agent": { "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0" }, ~~以下省略~~ }
例えばリクエストURIが https://example.com/s_12345/?hoge=fuga
の場合、以下リクエストイベントを受け取ります。
"request": { "method": "GET", "uri": "/s_12345", "querystring": { "hoge": { "value": "fuga" } }, "headers": { "host": { "value": "example.com" } } }
そのため
function handler(event) { ~~省略~~~ var domain = event.request.headers.host.value var uri = event.request.uri ~~省略~~~ }
のように指定すればそれぞれ example.com
と s_12345
を取得することが可能です。
しかしクエリパラメータは 上記の通り querystring に格納されますが、格納される値は一意ではないのでホスト名やパスと同じ指定方法をとることができません。
CloudFront Functions でのクエリパラメータの取得方法
悩んだときは先人の力を借りようということで、似たような要件でリダイレクトを行っているページを探したところ、以下ページにquerystringを格納している方法が記載されていました。
function objectToQueryString(obj) { var str = []; for (var param in obj) if (obj[param].multiValue) str.push(param + "=" + obj[param].multiValue.map((item) => item.value).join(',')); else if (obj[param].value == '') str.push(param); else str.push(param + "=" + obj[param].value); return str.join("&"); } function handler(event) { ~~省略~~~ objectToQueryString(event.request.querystring) ~~省略~~~ }
event.request.querystring
の値を配列に格納して値を一つづつ &
で連携させることでクエリパラメータを表現しています。
こちらの内容を参考に最終的に以下のような形で要件をみたしたリダイレクトを行うことができました。
function objectToQueryString(obj) { var str = []; for (var param in obj) if (obj[param].multiValue) str.push(param + "=" + obj[param].multiValue.map((item) => item.value).join(',')); else if (obj[param].value == '') str.push(param); else str.push(param + "=" + obj[param].value); return str.join("&"); } function handler(event) { var path = event.request.uri; var domain = request.headers.host.value; var beforepath = /s_/; var redirectPath = path.replace(beforepath,`shop_`); if (Object.keys(event.request.querystring).length) var loc = `https://${domain}${redirectPath}?${objectToQueryString(event.request.querystring)}` else var loc = `https://${domain}${redirectPath}` var response = { statusCode: 301, statusDescription: 'Found', headers: { 'location': { value: loc } } }; return response; }
まとめ
JavaScriptが不慣れだったため、時間がかかりましたが要件を満たすリダイレクト処理をCloudFront Functionsを使って行うことができました。
ただしCloudFront Functionsの最大実行時間が1ms となっているように、あまり複雑なリダイレクト処理は行うべきではないと思います。
これからも要件に応じて最適な構成を選択していければと思います。
おわりに
仲間を募集しております
募集中の職種については以下を御覧ください。