OpenResty+Luaでコンテンツを時限公開する

OpenResty + Luaを使うと、nginx単体では**できそうでできなかった**細かな条件分けを構築することができます。
私が一番苦労したのは、静的コンテンツを時限公開することでした。

# 時刻で条件分けする
```nginx.conf
  location /hoge {
    default_type text/html;
    content_by_lua_block {
-- 1675756800 = 2023/02/07 17:00:00
      if (os.time() < 1675756800) then
        ngx.say("このコンテンツは公開中です")
      else
        ngx.say("このコンテンツは非公開です")
      end
      ngx.say(ngx.localtime())
      ngx.say(os.time())
    }
  }
```
## 結果
```shell
$ curl xxx.xxx.xxx.xxx/hoge
このコンテンツは公開中です
2023-02-07 16:59:27
1675756767
$ curl xxx.xxx.xxx.xxx/hoge
このコンテンツは非公開です
2023-02-07 17:02:25
1675756945
```

# 時刻でステータスコードをつける
`nginx.status =` は ngx.say()の前にないと動きません。
これはハマりどころでしたが、HTTPのレスポンスの構造を考えると納得ではあります。
```nginx.conf
  location /hoge {
    default_type text/html;
    content_by_lua_block {
      if (os.time() < 1675756800) then
        ngx.status = ngx.OK
        ngx.say("このコンテンツは公開中です")
      else
        ngx.status = ngx.HTTP_FORBIDDEN
        ngx.say("このコンテンツは非公開です")
      end
      ngx.say(ngx.localtime())
      ngx.say(os.time())
    }
  }
```
## 結果
nginx.conf側の時刻をいじってテストしました。

```shell
$ curl -vvv "xxx.xxx.xxx.xxx/hoge"
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: openresty
< Date: Tue, 07 Feb 2023 08:56:37 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding
< Cache-Control: no-cache
<
このコンテンツは公開中です
2023-02-07 17:56:37
1675760197
* Connection #0 to host xxx.xxx.xxx.xxx left intact

$ curl -vvv "xxx.xxx.xxx.xxx/hoge"
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< Server: openresty
< Date: Tue, 07 Feb 2023 08:56:57 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding
<
このコンテンツは非公開です
2023-02-07 17:56:57
1675760217
* Connection #0 to host xxx.xxx.xxx.xxx left intact
```

# 時刻でリダイレクトする
`ngx.redirect` で可能です。オプションをつけないと302リダイレクトされます。
301リダイレクトする場合には、それがキャッシュされることに気を付けてください。
公開前URLが露出したり、公開URLが再利用される環境で実装すると、一部ユーザーが、本来の公開時刻が過ぎてもページを閲覧できない可能性があります。
https://github.com/openresty/lua-nginx-module#ngxredirect

```nginx.conf
  location /hoge {
    default_type text/html;
    content_by_lua_block {
      if (os.time() < 1675756800) then
        ngx.status = ngx.OK
        ngx.say("このコンテンツは公開中です")
      else
        return ngx.redirect("http://example.com")
      end
      ngx.say(ngx.localtime())
      ngx.say(os.time())
    }
  }
```
## 結果
リダイレクト側のみテストします。

```shell
$ curl -v "xxx.xxx.xxx.xxx/hoge"
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 302 Moved Temporarily
< Server: openresty
< Date: Tue, 07 Feb 2023 09:03:26 GMT
< Content-Type: text/html
< Content-Length: 142
< Connection: keep-alive
< Location: http://example.com
< Cache-Control: no-cache
<
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>openresty</center>
</body>
</html>
* Connection #0 to host xxx.xxx.xxx.xxx left intact

```

# 時限公開 + プロキシ
luaでレスポンスを生成する場合は`content_by_lua_block` で処理をフックできますが、レスポンスがプロキシから生成される場合、その前段階、 `rewrite_by_lua_block` で対応する必要があります。
ここもハマりどころです。
https://github.com/openresty/lua-nginx-module#directives の下部、`Order of Lua Nginx Module Directives`図が参考になります。

```nginx.conf
  location /hoge {
    default_type text/html;
    rewrite_by_lua_block {
      if (os.time() < 1675760400) then
        ngx.exit(ngx.HTTP_FORBIDDEN)
      end
    }
    proxy_pass https://example.com/;
  }
```

## 結果
### exitされる場合
OpenResty標準の403が返ってきます。

```shell
$ curl -v "xxx.xxx.xxx.xxx/hoge"
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< Server: openresty
< Date: Tue, 07 Feb 2023 09:37:24 GMT
< Content-Type: text/html
< Content-Length: 150
< Connection: keep-alive
<
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty</center>
</body>
</html>
* Connection #0 to host xxx.xxx.xxx.xxx left intact
```

### exitされずプロキシされた場合
`proxy_pass`先の処理が戻ってきます。
```shell
$ curl --head -v "xxx.xxx.xxx.xxx/hoge"
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 80 (#0)
> HEAD /hoge HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: openresty
Server: openresty
< Date: Tue, 07 Feb 2023 09:31:53 GMT
Date: Tue, 07 Feb 2023 09:31:53 GMT
< Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=UTF-8
< Connection: keep-alive
Connection: keep-alive
< Vary: Accept-Encoding
Vary: Accept-Encoding
....
< Set-Cookie: status=guest; path=/; domain=.example.com
Set-Cookie: status=guest; path=/; domain=.example.com
< Set-Cookie: v2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.example.com
Set-Cookie: v2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.example.com
...
<
* Connection #0 to host xxx.xxx.xxx.xxx left intact

```