VarnishでVCLファイルを変換したCのソースを見てみる

## VCLファイルをCに手動で変換する

[varnishd — Varnish version 4.1.0 documentation](https://www.varnish-cache.org/docs/4.1/reference/varnishd.html)の `-C` オプションの説明に書かれているようにVCLファイルはCに変換してコンパイルされます。

運用時はVarnish起動時に自動的にVCLファイルをCに変換・コンパイルして読み込んでくれるので特に気にする必要はないです。

変換結果のCのソースファイルを見たい場合は以下のように手動でコンパイルします。

```shell-session
sudo varnishd -C -f ${VCLファイル名} > ${Cファイル名} 2>&1
```

## 例1: リクエストされたホスト名に応じてバックエンドを切り替えるVCL

[Backends and virtual hosts in Varnish](https://www.varnish-cache.org/docs/trunk/users-guide/vcl-backends.html#backends-and-virtual-hosts-in-varnish)で紹介されています。

[varnish-compiled-vcl-examples/virtualhost_backend.vcl at 9e0320d](https://github.com/hnakamur/varnish-compiled-vcl-examples/blob/9e0320d876212c0b259ecb9efe976abdded910b3/virtualhost_backend.vcl#L13-L19)

```
sub vcl_recv {
    if (req.http.host ~ "foo.com") {
        set req.backend_hint = foo;
    } elsif (req.http.host ~ "bar.com") {
        set req.backend_hint = bar;
    }
}
```

Cに変換した結果は[varnish-compiled-vcl-examples/virtualhost_backend.c at 9e0320d](https://github.com/hnakamur/varnish-compiled-vcl-examples/blob/9e0320d876212c0b259ecb9efe976abdded910b3/virtualhost_backend.c#L1402-L1426)です。

```c
int __match_proto__(vcl_func_f)
VGC_function_vcl_recv(VRT_CTX)
{
  /* ... from ('input' Line 13 Pos 5) */
  {
    {
      VRT_count(ctx, 1);
      if (
        VRT_re_match(ctx, VRT_GetHdr(ctx, &VGC_HDR_REQ_host), VGC_re_2)
      )
        {
          VRT_count(ctx, 2);
          VRT_l_req_backend_hint(ctx, 
            vgc_backend_foo
          );
        }
      else if (
        VRT_re_match(ctx, VRT_GetHdr(ctx, &VGC_HDR_REQ_host), VGC_re_3)
      )
        {
          VRT_count(ctx, 3);
          VRT_l_req_backend_hint(ctx, 
            vgc_backend_bar
          );
        }
...(snip)...
```

VCLファイル内で明示的にreturnしていない場合は、built-inのVCLの内容が自分のVCLの後に差し込まれます。

built-inの `vcl_recv` の内容は
[varnish-compiled-vcl-examples/builtin.vcl at 643ddc5](https://github.com/hnakamur/varnish-compiled-vcl-examples/blob/643ddc5e322d4309d7f443b68deb337fe95e24a3/builtin.vcl#L6-L31)

```
# sub vcl_recv {
#     if (req.method == "PRI") {
# 	/* We do not support SPDY or HTTP/2.0 */
# 	return (synth(405));
#     }
#     if (req.method != "GET" &&
#       req.method != "HEAD" &&
#       req.method != "PUT" &&
#       req.method != "POST" &&
#       req.method != "TRACE" &&
#       req.method != "OPTIONS" &&
#       req.method != "DELETE") {
#         /* Non-RFC2616 or CONNECT which is weird. */
#         return (pipe);
#     }
# 
#     if (req.method != "GET" && req.method != "HEAD") {
#         /* We only deal with GET and HEAD by default */
#         return (pass);
#     }
#     if (req.http.Authorization || req.http.Cookie) {
#         /* Not cacheable by default */
#         return (pass);
#     }
#     return (hash);
# }
```

となっていて、Cへの変換結果は[varnish-compiled-vcl-examples/virtualhost_backend.c at 9e0320d](https://github.com/hnakamur/varnish-compiled-vcl-examples/blob/9e0320d876212c0b259ecb9efe976abdded910b3/virtualhost_backend.c#L1430-L1490)となっています。

```c
  /* ... from ('Builtin' Line 47 Pos 5) */
  {
    {
      VRT_count(ctx, 7);
      if (
        !VRT_strcmp(VRT_r_req_method(ctx), "PRI")
      )
        {
          VRT_count(ctx, 8);
          VRT_synth(ctx,
            405
            ,
            (const char*)0
          );
          VRT_handling(ctx, VCL_RET_SYNTH);
          return (1);
        }
      VRT_count(ctx, 9);
      if (
        (
          VRT_strcmp(VRT_r_req_method(ctx), "GET")&&
          VRT_strcmp(VRT_r_req_method(ctx), "HEAD")&&
          VRT_strcmp(VRT_r_req_method(ctx), "PUT")&&
          VRT_strcmp(VRT_r_req_method(ctx), "POST")&&
          VRT_strcmp(VRT_r_req_method(ctx), "TRACE")&&
          VRT_strcmp(VRT_r_req_method(ctx), "OPTIONS")&&
          VRT_strcmp(VRT_r_req_method(ctx), "DELETE"))
      )
        {
          VRT_count(ctx, 10);
          VRT_handling(ctx, VCL_RET_PIPE);
          return (1);
        }
      VRT_count(ctx, 11);
      if (
        (
          VRT_strcmp(VRT_r_req_method(ctx), "GET")&&
          VRT_strcmp(VRT_r_req_method(ctx), "HEAD"))
      )
        {
          VRT_count(ctx, 12);
          VRT_handling(ctx, VCL_RET_PASS);
          return (1);
        }
      VRT_count(ctx, 13);
      if (
        (
          (VRT_GetHdr(ctx, &VGC_HDR_REQ_Authorization) != 0)||
          (VRT_GetHdr(ctx, &VGC_HDR_REQ_Cookie) != 0))
      )
        {
          VRT_count(ctx, 14);
          VRT_handling(ctx, VCL_RET_PASS);
          return (1);
        }
      VRT_count(ctx, 15);
      VRT_handling(ctx, VCL_RET_HASH);
      return (1);
    }
  }
}
```

VCLでの `return` はCでは `VRT_handling` 関数呼び出しに変換されることがわかります。

## 例2: リクエストされたホスト名に応じてストレージを切り替えるVCL

[Partitioning your Varnish Cache](http://info.varnish-software.com/blog/partitioning-your-varnish-cache)で紹介されています。

[varnish-compiled-vcl-examples/partition_storage.vcl at 643ddc5](https://github.com/hnakamur/varnish-compiled-vcl-examples/blob/643ddc5e322d4309d7f443b68deb337fe95e24a3/partition_storage.vcl#L12-L23)

```
sub vcl_backend_response {
    if (bereq.http.host ~ "foo") {
        set beresp.storage_hint = "foo";
        set beresp.http.x-storage = "foo";
    } else if (bereq.http.host ~ "bar") {
        set beresp.storage_hint = "bar";
        set beresp.http.x-storage = "bar";
    } else {
        set beresp.storage_hint = "default";
        set beresp.http.x-storage = "default";
    }
}
```

Cに変換した結果は[varnish-compiled-vcl-examples/partition_storage.c at 643ddc5](https://github.com/hnakamur/varnish-compiled-vcl-examples/blob/643ddc5e322d4309d7f443b68deb337fe95e24a3/partition_storage.c#L1181-L1229)です。

```c
int __match_proto__(vcl_func_f)
VGC_function_vcl_backend_response(VRT_CTX)
{
  /* ... from ('input' Line 12 Pos 5) */
  {
    {
      VRT_count(ctx, 2);
      if (
        VRT_re_match(ctx, VRT_GetHdr(ctx, &VGC_HDR_BEREQ_host), VGC_re_1)
      )
        {
          VRT_count(ctx, 3);
          VRT_l_beresp_storage_hint(ctx, 
            "foo",
              vrt_magic_string_end
          );
          VRT_SetHdr(ctx, &VGC_HDR_BERESP_x_2d_storage,
            "foo",
              vrt_magic_string_end
          );
        }
      else if (
        VRT_re_match(ctx, VRT_GetHdr(ctx, &VGC_HDR_BEREQ_host), VGC_re_2)
      )
        {
          VRT_count(ctx, 4);
          VRT_l_beresp_storage_hint(ctx, 
            "bar",
              vrt_magic_string_end
          );
          VRT_SetHdr(ctx, &VGC_HDR_BERESP_x_2d_storage,
            "bar",
              vrt_magic_string_end
          );
        }
      else
        {
          VRT_count(ctx, 5);
          VRT_l_beresp_storage_hint(ctx, 
            "default",
              vrt_magic_string_end
          );
          VRT_SetHdr(ctx, &VGC_HDR_BERESP_x_2d_storage,
            "default",
              vrt_magic_string_end
          );
        }
    }
  }
...(snip)...
```

こちらも `vcl_backend_response` 内に `return` を書いていないので、この後にbuilt-inの `vcl_backend_response` の内容が差し込まれます。

[varnish-compiled-vcl-examples/builtin.vcl at 643ddc5](https://github.com/hnakamur/varnish-compiled-vcl-examples/blob/643ddc5e322d4309d7f443b68deb337fe95e24a3/builtin.vcl#L113-L127)

```
# sub vcl_backend_response {
#     if (beresp.ttl <= 0s ||
#       beresp.http.Set-Cookie ||
#       beresp.http.Surrogate-control ~ "no-store" ||
#       (!beresp.http.Surrogate-Control &&
#         beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
#       beresp.http.Vary == "*") {
#         /*
#         * Mark as "Hit-For-Pass" for the next 2 minutes
#         */
#         set beresp.ttl = 120s;
#         set beresp.uncacheable = true;
#     }
#     return (deliver);
# }
```

Cへの変換結果は[varnish-compiled-vcl-examples/partition_storage.c at 643ddc5](https://github.com/hnakamur/varnish-compiled-vcl-examples/blob/643ddc5e322d4309d7f443b68deb337fe95e24a3/partition_storage.c#L1230-L1258)です。

```c
  /* ... from ('Builtin' Line 154 Pos 5) */
  {
    {
      VRT_count(ctx, 31);
      if (
        (
          (VRT_r_beresp_ttl(ctx) <= 0)||
          (VRT_GetHdr(ctx, &VGC_HDR_BERESP_Set_2d_Cookie) != 0)||
          VRT_re_match(ctx, VRT_GetHdr(ctx, &VGC_HDR_BERESP_Surrogate_2d_control), VGC_re_3)||
          ((
            !((VRT_GetHdr(ctx, &VGC_HDR_BERESP_Surrogate_2d_Control) != 0))&&
            VRT_re_match(ctx, VRT_GetHdr(ctx, &VGC_HDR_BERESP_Cache_2d_Control), VGC_re_4)))||
          !VRT_strcmp(VRT_GetHdr(ctx, &VGC_HDR_BERESP_Vary), "*"))
      )
        {
          VRT_count(ctx, 32);
          VRT_l_beresp_ttl(ctx, 
            120
          );
          VRT_l_beresp_uncacheable(ctx, 
            (0==0)
          );
        }
      VRT_count(ctx, 33);
      VRT_handling(ctx, VCL_RET_DELIVER);
      return (1);
    }
  }
}
```