S3とLambdaでSSIっぽいことをする

Amazon S3では、静的Webサイトのホスティングができますが、あくまで静的なものに限り、共通ヘッダー、フッターなどのセットをPHPやSSI(Server Side Includes)で実行することはできません。

が、AWS Lambdaを使ってそれっぽいことを実現してみます。
どう実現するのかというと…
S3のPUTイベントをトリガーに、拡張子「.ssi」が付いたファイルが置かれたら、Lambdaを起動して、プログラム内でヘッダーとフッターをインクルードしてファイルを作成し直すといったものです。

まずはLambdaを作成します。
LambdaのロールにはS3とCloudWatchLogsのアクセス権限を与えておきます。
import json
import os
import logging
import boto3
import urllib.parse

logger = logging.getLogger()
logger.setLevel(logging.INFO)

s3 = boto3.client('s3')

def lambda_handler(event, context):
    logger.info('## ENVIRONMENT VARIABLES')
    logger.info(os.environ)
    logger.info('## EVENT')
    logger.info(event)
 
    input_bucket = event['Records'][0]['s3']['bucket']['name']
    logger.info('## INPUT BUKET')
    logger.info(input_bucket)
    
    input_key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    logger.info('## INPUT KEY')
    logger.info(input_key)


    try:
        # 入力ファイルの取得
        response = s3.get_object(Bucket=input_bucket, Key=input_key)
        input_html = response[u'Body'].read().decode('utf-8')

        # SSIファイルの取得
        ssi_bucket = 'your_bucket'
        response = s3.get_object(Bucket=ssi_bucket, Key='ssi/header.html')
        header_html = response[u'Body'].read().decode('utf-8')

        response = s3.get_object(Bucket=ssi_bucket, Key='ssi/footer.html')
        footer_html = response[u'Body'].read().decode('utf-8')

        # SSIを実行
        output_html = input_html.replace('<!--#include virtual="/ssi/header.html" -->', header_html)
        output_html = output_html.replace('<!--#include virtual="/ssi/footer.html" -->', footer_html)
        
        # ファイル出力
        output_bucket = input_bucket
        logger.info('## OUTPUT BUKET')
        logger.info(output_bucket)
        
        output_key    = input_key.replace(".ssi", "")
        logger.info('## OUTPUT KEY')
        logger.info(output_key)
        
        out_s3   = boto3.resource('s3')
        s3_obj   = out_s3.Object(output_bucket, output_key)
        response = s3_obj.put(Body = bytes(output_html, 'UTF-8'))

    except Exception as e:
        logger.info(e)
        raise e
トリガーを設定します。
無限ループにならないようにサフィックスに「.ssi」を付けておきます。
これで準備完了です。
ヘッダー、フッターには以下のHTMLを用意しました。
<header>
    ヘッダー
</haeder>
<footer>
    フッター
</footer>
「index.html.ssi」の名前でこちらのファイルをS3にアップロードします。
<html>
<body>
<!--#include virtual="/ssi/header.html" -->
<p>コンテンツ</p>
<!--#include virtual="/ssi/footer.html" -->
</body>
</html>
トリガーでLambdaが起動し、しっかりヘッダーとフッターが挿入されたindex.htmlができあがりました。
<html>
<body>
<header>
    ヘッダー
</haeder>
<p>コンテンツ</p>
<footer>
    フッター
</footer>
</body>
</html>