Clojure/Pedestal サーバを WSL で立てると Windows 側からアクセスできない問題の解決法

表題の通りです．Port forwarding などの設定ができているにもかかわらずローカルの wsl 上に Pedestal で立てたサーバだけが Windows 側からアクセスできない場合の対処法になります．

# TL; DR

`io.pedestal.http/create-server` の引数の map に以下の key-value を追加する：
```clojure
::http/host "0.0.0.0"
```

# 環境

- OS: Windows 11 Home (version: 23H2)
- WSL: Distribution = Ubuntu 20.04 LTS
```
> wsl --version
WSL バージョン: 2.2.4.0
カーネル バージョン: 5.15.153.1-2
WSLg バージョン: 1.0.61
MSRDC バージョン: 1.2.5326
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26091.1-240325-1447.ge-release
Windows バージョン: 10.0.22631.4169
```

``` .wslconfig
[wsl2]
localhostForwarding=True
```

- Clojure:

```
> clojure --version
Clojure CLI version 1.11.1.1413
```

# Pedestal サーバ

## プロジェクト構成

[Pedestal チュートリアル](http://pedestal.io/pedestal/0.7/guides/hello-world.html) に従ってプロジェクトを作成すると大体以下のようになります．

```
pedestal-example/
    src/
        hello.clj
    deps.edn
```

```clojure:src/hello.clj
(ns hello
  (:require [io.pedestal.http :as http]
            [io.pedestal.http.route :as route]))

(defn respond-hello [_request]
  {:status 200 :body "Hello, world!"})


(def routes
  (route/expand-routes
   #{["/greet" :get respond-hello :route-name :greet]}))

(def service-map
  {::http/routes routes
   ::http/type :jetty
   ::http/port 8080})

(defn create-server []
  (http/create-server service-map))

(defn start []
  (http/start (create-server)))
```

```clojure:deps.edn
{:paths ["src"]
 :deps  {io.pedestal/pedestal.jetty {:mvn/version "0.7.0"}
         org.slf4j/slf4j-simple     {:mvn/version "2.0.10"}}}
```

## 実行コマンド

```
> clj
Clojure 1.11.1
user=> (require 'hello) (hello/start)
nil
[main] INFO org.eclipse.jetty.server.Server - jetty-11.0.20; built: 2024-01-29T21:04:22.394Z; git: 922f8dc188f7011e60d0361de585fd4ac4d63064; jvm 17.0.12+7-Ubuntu-1ubuntu220.04
[main] INFO org.eclipse.jetty.server.handler.ContextHandler - Started o.e.j.s.ServletContextHandler@63a875e6{/,null,AVAILABLE}
[main] INFO org.eclipse.jetty.server.AbstractConnector - Started ServerConnector@63782ad0{HTTP/1.1, (http/1.1, h2c)}{localhost:8080}
[main] INFO org.eclipse.jetty.server.Server - Started Server@3c36f10b{STARTING}[11.0.20,sto=0] @15277ms
```

## テスト

WSL 上で実行した場合

```
> curl http://localhost:8080/greet
Hello, world!
```

Windows 上で実行した場合(Powershell)

```
> curl -i -X GET "http://localhost:8080/greet"
curl: (52) Empty reply from server
```

エラーメッセージが `Empty reply from server` となっている点に留意が必要です．
単にサーバが立ち上がっていない場合は以下のようなエラーになります．

```
curl: (7) Failed to connect to localhost port 8080 after 2239 ms: Couldn't connect to server
```

# localhostForwarding が機能しているか確認

Python などで適当なサーバを立てて，Windows 側からアクセスできるか調べます．

```
> python3 -m http.server 8080
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
```

```
> # powershell
> curl -i -X GET "http://localhost:8080"
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.8.10
Date: Sun, 22 Sep 2024 13:04:04 GMT
Content-type: text/html; charset=utf-8
Content-Length: 647

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href=".clj-kondo/">.clj-kondo/</a></li>
<li><a href=".cpcache/">.cpcache/</a></li>
<li><a href=".lsp/">.lsp/</a></li>
<li><a href="deps.edn">deps.edn</a></li>
<li><a href="hello.js">hello.js</a></li>
<li><a href="node_modules/">node_modules/</a></li>
<li><a href="package-lock.json">package-lock.json</a></li>
<li><a href="src/">src/</a></li>
</ul>
<hr>
</body>
</html>
```

正常にアクセスできています．

もしここで Python の方のサーバにもアクセスできないのであれば WSL の localhostForwarding が上手くいっていない可能性があり，`.wslconfig` に `localhostForwarding=true` と追記(したのち wsl を再起動)するなどして localhostForwarding を有効化します．

# 解決法

`io.pedestal.http/create-service` の引数の map に `::http/host "0.0.0.0"` という key-value pair を追加することで Windows 側からもアクセスできるようになります．

``` diff_clojure
  (def service-map
    {
+    ::http/host "0.0.0.0"
     ::http/routes routes
     ::http/type :jetty
     ::http/port 8080})
     
  (defn create-server []
    (http/create-server service-map))
```

参考: https://stackoverflow.com/questions/69034828/clojure-pedestal-windows-wsl2-browsers-not-rendering

# 結論

普通に localhostForwarding の問題だと思い込んで数時間スタックしたので，初めて使うフレームワークでこの手の問題が起こったときは信頼できる簡易サーバを別途立てて検証するのが大事という教訓を得ました．